From 0e29477363aafde55a002f692411893374257f03 Mon Sep 17 00:00:00 2001 From: porisius <47161774+porisius@users.noreply.github.com> Date: Sat, 17 Sep 2022 21:44:02 -0400 Subject: [PATCH 01/21] SMR: API support for Dedicated Servers (#12) * feat: add query for resolving mod version constraints * fix: correct sql grouping * chore: dependency version bumps * chore: sample config file * chore: increase linter timeout * chore: fix workflow linter argument * Combine SMR DS Dev with Staging/Tags * migrate to go 1.18.2 * SML platform link insert * starting Mod platform links * Mod/SML platform custom model addition * chore: merge compatibility * Fixes and completion of SML Links * progress: mod links * Validation: add .so files to scanning * Validation: add .so files to scanning * Split Combined Zip to Separate * Remove side, add index, cleanup testing logs * DeleteCombinedMod Cleans up combined mod upload, either after separating and succeeding or on error. * Fix: smllink * Fix: modlink * Fix: SeparateMod * Fix: Validations * fixes: mod_links and added routes * chore: lint fixes, and there was a lot of them... * optimization: seperatemod * cleanup logging * cleanup: SML Links * fix attempt: Mod Link to match SML Link * SML Links to SML Arch * Mod Links to Mod Arch / Key * ModLinks convert to asset * Fix: ModLinks Asset * DeleteModLink and filter pdb/debug on upload * Arch and preloads for getMods * chore: make linter happy... wrapchecks for errors * fix: platform name for Win64Server platform * Rename to archs and remove unused code * Remove VT Key * Merge validations dll/so * Link is dead, all hail Arch * Remove unnecessary log * Remove TODO Validation * Better error logging plus lint satisfaction with wrapcheck * Missed the memo, sorry Vilsol * Doc Comment for downloadModArch and downloadModVersionArch * Added context for log * Optimize SeparateMod * removed unneeded function * chore:lint * Move deleting uploaded file after everything checks out * fix: change where legacy link is gathered, save correct size/hash * fix: do not take logger from context * fix: non-nullable compatibility info * chore: upgrade to go 1.19 chore: fix linter errors * chore: bump gqlgen dep for pwetty playground Co-authored-by: Vilsol Co-authored-by: Feyko --- dataloader/loaders.go | 4 +- db/postgres/mod.go | 6 +- db/postgres/mod_archs.go | 121 +++++++++++++ db/postgres/postgres.go | 3 +- db/postgres/postgres_types.go | 30 ++++ db/postgres/sml_archs.go | 90 ++++++++++ db/postgres/sml_version.go | 18 +- db/postgres/version.go | 26 +-- go.sum | 1 - gql/gql_types.go | 56 ++++++ gql/resolver.go | 3 + gql/resolver_mod_archs.go | 34 ++++ gql/resolver_sml_archs.go | 97 +++++++++++ gql/resolver_sml_versions.go | 38 +++++ gql/resolver_versions.go | 1 + gql/versions.go | 21 ++- gqlgen.yml | 9 + .../sql/000020_add_compatibility.up.sql | 0 .../sql/000021_add_mod_platform.down.sql | 2 + migrations/sql/000021_add_mod_platform.up.sql | 21 +++ models/filters.go | 82 +++++++++ nodes/mod.go | 44 ++++- nodes/routes.go | 2 + nodes/version.go | 33 ++++ oauth/facebook.go | 4 +- .../consumer_scan_mod_on_virus_total.go | 10 +- schemas/mod_archs.graphql | 47 ++++++ schemas/sml_archs.graphql | 50 ++++++ schemas/sml_version.graphql | 3 + schemas/version.graphql | 1 + static/data-json-schema.json | 2 +- storage/storage.go | 159 +++++++++++++++++- util/image.go | 3 +- validation/validation.go | 8 +- 34 files changed, 982 insertions(+), 47 deletions(-) create mode 100644 db/postgres/mod_archs.go create mode 100644 db/postgres/sml_archs.go create mode 100644 gql/resolver_mod_archs.go create mode 100644 gql/resolver_sml_archs.go mode change 100755 => 100644 migrations/sql/000020_add_compatibility.up.sql create mode 100644 migrations/sql/000021_add_mod_platform.down.sql create mode 100644 migrations/sql/000021_add_mod_platform.up.sql create mode 100644 schemas/mod_archs.graphql create mode 100644 schemas/sml_archs.graphql diff --git a/dataloader/loaders.go b/dataloader/loaders.go index e1f9d88..203c408 100644 --- a/dataloader/loaders.go +++ b/dataloader/loaders.go @@ -109,7 +109,7 @@ func Middleware() func(handlerFunc echo.HandlerFunc) echo.HandlerFunc { var entities []postgres.Version reqCtx := c.Request().Context() - postgres.DBCtx(reqCtx).Where("approved = ? AND denied = ? AND mod_id IN ?", true, false, fetchIds).Order("created_at desc").Find(&entities) + postgres.DBCtx(reqCtx).Preload("Arch").Where("approved = ? AND denied = ? AND mod_id IN ?", true, false, fetchIds).Order("created_at desc").Find(&entities) for _, entity := range entities { byID[entity.ModID] = append(byID[entity.ModID], entity) @@ -145,7 +145,7 @@ func Middleware() func(handlerFunc echo.HandlerFunc) echo.HandlerFunc { var entities []postgres.Version reqCtx := c.Request().Context() - postgres.DBCtx(reqCtx).Select( + postgres.DBCtx(reqCtx).Preload("Arch").Select( "id", "created_at", "updated_at", diff --git a/db/postgres/mod.go b/db/postgres/mod.go index a411685..0e9eaab 100644 --- a/db/postgres/mod.go +++ b/db/postgres/mod.go @@ -26,7 +26,7 @@ func GetModByID(ctx context.Context, modID string) *Mod { func GetModByIDNoCache(ctx context.Context, modID string) *Mod { var mod Mod - DBCtx(ctx).Preload("Tags").Find(&mod, "id = ?", modID) + DBCtx(ctx).Preload("Tags").Preload("Versions.Arch").Find(&mod, "id = ?", modID) if mod.ID == "" { return nil @@ -44,7 +44,7 @@ func GetModByReference(ctx context.Context, modReference string) *Mod { } var mod Mod - DBCtx(ctx).Preload("Tags").Find(&mod, "mod_reference = ?", modReference) + DBCtx(ctx).Preload("Tags").Preload("Versions.Arch").Find(&mod, "mod_reference = ?", modReference) if mod.ID == "" { return nil @@ -213,7 +213,7 @@ func NewModQuery(ctx context.Context, filter *models.ModFilter, unapproved bool, } query = query.Where("approved = ? AND denied = ?", !unapproved, false) - query = query.Preload("Tags") + query = query.Preload("Tags").Preload("Versions.Arch") if filter != nil { if filter.Search != nil && *filter.Search != "" { cleanSearch := strings.Replace(strings.TrimSpace(*filter.Search), " ", " & ", -1) diff --git a/db/postgres/mod_archs.go b/db/postgres/mod_archs.go new file mode 100644 index 0000000..2b7c3ae --- /dev/null +++ b/db/postgres/mod_archs.go @@ -0,0 +1,121 @@ +package postgres + +import ( + "context" + "strings" + + "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/models" + "github.com/satisfactorymodding/smr-api/util" +) + +func CreateModArch(ctx context.Context, modArch *ModArch) (*ModArch, error) { + modArch.ID = util.GenerateUniqueID() + DBCtx(ctx).Create(&modArch) + return modArch, nil +} + +func GetModArch(ctx context.Context, modArchID string) *ModArch { + cacheKey := "GetModArch_" + modArchID + + if modArch, ok := dbCache.Get(cacheKey); ok { + return modArch.(*ModArch) + } + + var modArch ModArch + DBCtx(ctx).Find(&modArch, "id = ?", modArchID) + + if modArch.ID == "" { + return nil + } + + dbCache.Set(cacheKey, modArch, cache.DefaultExpiration) + + return &modArch +} + +func GetModArchs(ctx context.Context, filter *models.ModArchFilter) []ModArch { + var modArchs []ModArch + query := DBCtx(ctx) + + if filter != nil { + query = query.Limit(*filter.Limit). + Offset(*filter.Offset). + Order(string(*filter.OrderBy) + " " + string(*filter.Order)) + + if filter.Search != nil && *filter.Search != "" { + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + } + } + + query.Find(&modArchs) + return modArchs +} + +func GetVersionModArchs(ctx context.Context, versionID string) []ModArch { + var modArchs []ModArch + query := DBCtx(ctx).Find(&modArchs, "mod_version_arch_id = ?", versionID) + + query.Find(&modArchs) + return modArchs +} + +func GetModArchByID(ctx context.Context, modArchID string) *ModArch { + cacheKey := "GetModArch_" + modArchID + + if modArch, ok := dbCache.Get(cacheKey); ok { + return modArch.(*ModArch) + } + + var modArch ModArch + DBCtx(ctx).Find(&modArch, "id = ?", modArchID) + + if modArch.ID == "" { + return nil + } + + dbCache.Set(cacheKey, modArch, cache.DefaultExpiration) + + return &modArch +} + +func GetModArchsByID(ctx context.Context, modArchIds []string) []ModArch { + var modArchs []ModArch + + DBCtx(ctx).Find(&modArchs, "id in (?)", modArchIds) + + if len(modArchIds) != len(modArchs) { + return nil + } + + return modArchs +} + +func GetModArchByPlatform(ctx context.Context, versionID string, platform string) *ModArch { + cacheKey := "GetModArch_" + versionID + "_" + platform + if modplatform, ok := dbCache.Get(cacheKey); ok { + return modplatform.(*ModArch) + } + + var modplatform ModArch + DBCtx(ctx).First(&modplatform, "mod_version_arch_id = ? AND platform = ?", versionID, platform) + + if modplatform.ModVersionArchID == "" { + return nil + } + + dbCache.Set(cacheKey, &modplatform, cache.DefaultExpiration) + + return &modplatform +} + +func GetModArchDownload(ctx context.Context, versionID string, platform string) string { + var modPlatform ModArch + DBCtx(ctx).First(&modPlatform, "mod_version_arch_id = ? AND platform = ?", versionID, platform) + + if modPlatform.ModVersionArchID == "" { + return "" + } + + return modPlatform.Key +} diff --git a/db/postgres/postgres.go b/db/postgres/postgres.go index c104b89..9e6b4b9 100644 --- a/db/postgres/postgres.go +++ b/db/postgres/postgres.go @@ -5,11 +5,10 @@ import ( "fmt" "time" - "github.com/satisfactorymodding/smr-api/db/postgres/otel" - "github.com/patrickmn/go-cache" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/satisfactorymodding/smr-api/db/postgres/otel" "github.com/spf13/viper" "gorm.io/driver/postgres" "gorm.io/gorm" diff --git a/db/postgres/postgres_types.go b/db/postgres/postgres_types.go index 003495d..ff6ada0 100644 --- a/db/postgres/postgres_types.go +++ b/db/postgres/postgres_types.go @@ -6,6 +6,10 @@ import ( "gorm.io/gorm" ) +type Tabler interface { + TableName() string +} + type SMRDates struct { CreatedAt time.Time UpdatedAt time.Time @@ -91,6 +95,7 @@ type Version struct { Approved bool `gorm:"default:false;not null"` Denied bool `gorm:"default:false;not null"` Hotness uint + Arch []ModArch `gorm:"foreignKey:mod_version_arch_id;preload:true"` Metadata *string ModReference *string VersionMajor *int @@ -128,6 +133,7 @@ type SMLVersion struct { Stability string `sql:"type:version_stability"` Date time.Time Link string + Arch []SMLArch `gorm:"foreignKey:sml_version_arch_id;preload:true"` Changelog string BootstrapVersion *string } @@ -187,3 +193,27 @@ type Compatibility struct { State string Note string } + +type ModArch struct { + ID string `gorm:"primary_key;type:varchar(16)"` + ModVersionArchID string + Platform string + Key string + Size int64 + Hash string +} + +func (ModArch) TableName() string { + return "mod_archs" +} + +type SMLArch struct { + ID string `gorm:"primary_key;type:varchar(14)"` + SMLVersionArchID string + Platform string + Link string +} + +func (SMLArch) TableName() string { + return "sml_archs" +} diff --git a/db/postgres/sml_archs.go b/db/postgres/sml_archs.go new file mode 100644 index 0000000..885d78c --- /dev/null +++ b/db/postgres/sml_archs.go @@ -0,0 +1,90 @@ +package postgres + +import ( + "context" + "strings" + + "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/models" + "github.com/satisfactorymodding/smr-api/util" +) + +func CreateSMLArch(ctx context.Context, smlLink *SMLArch) (*SMLArch, error) { + smlLink.ID = util.GenerateUniqueID() + + DBCtx(ctx).Create(&smlLink) + + return smlLink, nil +} + +func GetSMLArch(ctx context.Context, smlLinkID string) *SMLArch { + cacheKey := "GetSMLArch_" + smlLinkID + + if smlLink, ok := dbCache.Get(cacheKey); ok { + return smlLink.(*SMLArch) + } + + var smlLink SMLArch + DBCtx(ctx).Find(&smlLink, "id = ?", smlLinkID) + + if smlLink.ID == "" { + return nil + } + + dbCache.Set(cacheKey, smlLink, cache.DefaultExpiration) + + return &smlLink +} + +func GetSMLArchs(ctx context.Context, filter *models.SMLArchFilter) []SMLArch { + var smlLinks []SMLArch + query := DBCtx(ctx) + + if filter != nil { + query = query.Limit(*filter.Limit). + Offset(*filter.Offset). + Order(string(*filter.OrderBy) + " " + string(*filter.Order)) + + if filter.Search != nil && *filter.Search != "" { + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + } + } + + query.Find(&smlLinks) + return smlLinks +} + +func GetSMLArchByID(ctx context.Context, smlLinkID string) []SMLArch { + var smlLinks []SMLArch + + DBCtx(ctx).Find(&smlLinks, "id in ?", smlLinkID) + + if len(smlLinks) != 0 { + return nil + } + + return smlLinks +} + +func GetSMLArchsByID(ctx context.Context, smlLinkIds []string) []SMLArch { + var smlLinks []SMLArch + + DBCtx(ctx).Find(&smlLinks, "id in (?)", smlLinkIds) + + if len(smlLinkIds) != len(smlLinks) { + return nil + } + + return smlLinks +} + +func GetSMLArchDownload(ctx context.Context, smlVersionID string, platform string) string { + var smlPlatform SMLArch + DBCtx(ctx).First(&smlPlatform, "sml_version_arch_id = ? AND platform = ?", smlVersionID, platform) + + if smlPlatform.SMLVersionArchID == "" { + return "" + } + + return smlPlatform.Link +} diff --git a/db/postgres/sml_version.go b/db/postgres/sml_version.go index 64e84c9..8e5a890 100644 --- a/db/postgres/sml_version.go +++ b/db/postgres/sml_version.go @@ -13,12 +13,21 @@ func CreateSMLVersion(ctx context.Context, smlVersion *SMLVersion) (*SMLVersion, DBCtx(ctx).Create(&smlVersion) + for _, link := range smlVersion.Arch { + DBCtx(ctx).Create(&SMLArch{ + ID: util.GenerateUniqueID(), + SMLVersionArchID: smlVersion.ID, + Platform: link.Platform, + Link: link.Link, + }) + } + return smlVersion, nil } func GetSMLVersionByID(ctx context.Context, smlVersionID string) *SMLVersion { var smlVersion SMLVersion - DBCtx(ctx).Find(&smlVersion, "id = ?", smlVersionID) + DBCtx(ctx).Preload("Arch").Find(&smlVersion, "id in (?)", smlVersionID) if smlVersion.ID == "" { return nil @@ -41,13 +50,14 @@ func GetSMLVersions(ctx context.Context, filter *models.SMLVersionFilter) []SMLV } } - query.Find(&smlVersions) + query.Preload("Arch").Find(&smlVersions) + return smlVersions } func GetSMLVersionsByID(ctx context.Context, smlVersionIds []string) []SMLVersion { var smlVersions []SMLVersion - DBCtx(ctx).Find(&smlVersions, "id in (?)", smlVersionIds) + DBCtx(ctx).Preload("Arch").Find(&smlVersions, "id in (?)", smlVersionIds) if len(smlVersionIds) != len(smlVersions) { return nil @@ -73,7 +83,7 @@ func GetSMLVersionCount(ctx context.Context, filter *models.SMLVersionFilter) in func GetSMLLatestVersions(ctx context.Context) *[]SMLVersion { var smlVersions []SMLVersion - DBCtx(ctx).Select("distinct on (stability) *"). + DBCtx(ctx).Preload("Arch").Select("distinct on (stability) *"). Order("stability, created_at desc"). Find(&smlVersions) diff --git a/db/postgres/version.go b/db/postgres/version.go index 4cff8ef..db4fdf8 100644 --- a/db/postgres/version.go +++ b/db/postgres/version.go @@ -24,7 +24,7 @@ func GetVersionsByID(ctx context.Context, versionIds []string) []Version { } var versions []Version - DBCtx(ctx).Find(&versions, "id in (?)", versionIds) + DBCtx(ctx).Preload("Arch").Find(&versions, "id in (?)", versionIds) if len(versionIds) != len(versions) { return nil @@ -43,7 +43,7 @@ func GetModLatestVersions(ctx context.Context, modID string, unapproved bool) *[ var versions []Version - DBCtx(ctx).Select("distinct on (mod_id, stability) *"). + DBCtx(ctx).Preload("Arch").Select("distinct on (mod_id, stability) *"). Where("mod_id = ?", modID). Where("approved = ? AND denied = ?", !unapproved, false). Order("mod_id, stability, created_at desc"). @@ -62,7 +62,7 @@ func GetModsLatestVersions(ctx context.Context, modIds []string, unapproved bool var versions []Version - DBCtx(ctx).Select("distinct on (mod_id, stability) *"). + DBCtx(ctx).Preload("Arch").Select("distinct on (mod_id, stability) *"). Where("mod_id in (?)", modIds). Where("approved = ? AND denied = ?", !unapproved, false). Order("mod_id, stability, created_at desc"). @@ -80,7 +80,7 @@ func GetModVersions(ctx context.Context, modID string, limit int, offset int, or } var versions []Version - DBCtx(ctx).Limit(limit).Offset(offset).Order(orderBy+" "+order).Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) + DBCtx(ctx).Preload("Arch").Limit(limit).Offset(offset).Order(orderBy+" "+order).Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) dbCache.Set(cacheKey, versions, cache.DefaultExpiration) @@ -98,7 +98,7 @@ func GetModVersionsNew(ctx context.Context, modID string, filter *models.Version } var versions []Version - query := DBCtx(ctx) + query := DBCtx(ctx).Preload("Arch") if filter != nil { query = query.Limit(*filter.Limit). @@ -106,7 +106,7 @@ func GetModVersionsNew(ctx context.Context, modID string, filter *models.Version Order(string(*filter.OrderBy) + " " + string(*filter.Order)) } - query.Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) + query.Preload("Arch").Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) if cacheKey != "" { dbCache.Set(cacheKey, versions, cache.DefaultExpiration) @@ -122,7 +122,7 @@ func GetModVersion(ctx context.Context, modID string, versionID string) *Version } var version Version - DBCtx(ctx).First(&version, "mod_id = ? AND id = ?", modID, versionID) + DBCtx(ctx).Preload("Arch").First(&version, "mod_id = ? AND id = ?", modID, versionID) if version.ID == "" { return nil @@ -140,7 +140,7 @@ func GetModVersionByName(ctx context.Context, modID string, versionName string) } var version Version - DBCtx(ctx).First(&version, "mod_id = ? AND version = ?", modID, versionName) + DBCtx(ctx).Preload("Arch").First(&version, "mod_id = ? AND version = ?", modID, versionName) if version.ID == "" { return nil @@ -186,7 +186,7 @@ func GetVersion(ctx context.Context, versionID string) *Version { } var version Version - DBCtx(ctx).First(&version, "id = ?", versionID) + DBCtx(ctx).Preload("Arch").First(&version, "id = ?", versionID) if version.ID == "" { return nil @@ -208,7 +208,7 @@ func GetVersionsNew(ctx context.Context, filter *models.VersionFilter, unapprove } var versions []Version - query := DBCtx(ctx).Where("approved = ? AND denied = ?", !unapproved, false) + query := DBCtx(ctx).Preload("Arch").Where("approved = ? AND denied = ?", !unapproved, false) if filter != nil { query = query.Limit(*filter.Limit). @@ -224,7 +224,7 @@ func GetVersionsNew(ctx context.Context, filter *models.VersionFilter, unapprove } } - query.Find(&versions) + query.Preload("Arch").Find(&versions) if cacheKey != "" { dbCache.Set(cacheKey, versions, cache.DefaultExpiration) @@ -288,7 +288,7 @@ func GetModVersionsConstraint(ctx context.Context, modID string, constraint stri return nil } - query := DBCtx(ctx).Where("mod_id", modID) + query := DBCtx(ctx).Preload("Arch").Where("mod_id", modID) /* <=1.2.3 @@ -357,6 +357,6 @@ func GetModVersionsConstraint(ctx context.Context, modID string, constraint stri } var versions []Version - query.Find(&versions) + query.Preload("Arch").Find(&versions) return versions } diff --git a/go.sum b/go.sum index f5182f5..9204d1e 100755 --- a/go.sum +++ b/go.sum @@ -137,7 +137,6 @@ github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:Uz github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= diff --git a/gql/gql_types.go b/gql/gql_types.go index 607d367..23c0dbd 100644 --- a/gql/gql_types.go +++ b/gql/gql_types.go @@ -61,6 +61,7 @@ func DBModToGenerated(mod *postgres.Mod) *generated.Mod { LastVersionDate: &LastVersionDate, ModReference: mod.ModReference, Hidden: mod.Hidden, + Versions: DBVersionsToGeneratedSlice(mod.Versions), Tags: DBTagsToGeneratedSlice(mod.Tags), Compatibility: DBCompInfoToGenCompInfo(mod.Compatibility), } @@ -84,6 +85,7 @@ func DBVersionToGenerated(version *postgres.Version) *generated.Version { Changelog: version.Changelog, Downloads: int(version.Downloads), Stability: generated.VersionStabilities(version.Stability), + Arch: DBModArchsToGeneratedSlice(version.Arch), Approved: version.Approved, UpdatedAt: version.UpdatedAt.Format(time.RFC3339Nano), CreatedAt: version.CreatedAt.Format(time.RFC3339Nano), @@ -94,6 +96,14 @@ func DBVersionToGenerated(version *postgres.Version) *generated.Version { } } +func DBVersionsToGeneratedSlice(versions []postgres.Version) []*generated.Version { + converted := make([]*generated.Version, len(versions)) + for i, version := range versions { + converted[i] = DBVersionToGenerated(&version) + } + return converted +} + func DBGuideToGenerated(guide *postgres.Guide) *generated.Guide { if guide == nil { return nil @@ -124,6 +134,7 @@ func DBSMLVersionToGenerated(smlVersion *postgres.SMLVersion) *generated.SMLVers BootstrapVersion: smlVersion.BootstrapVersion, Stability: generated.VersionStabilities(smlVersion.Stability), Link: smlVersion.Link, + Arch: DBSMLArchsToGeneratedSlice(smlVersion.Arch), Changelog: smlVersion.Changelog, Date: smlVersion.Date.Format(time.RFC3339Nano), UpdatedAt: smlVersion.UpdatedAt.Format(time.RFC3339Nano), @@ -200,6 +211,51 @@ func DBTagsToGeneratedSlice(tags []postgres.Tag) []*generated.Tag { return converted } +func DBModArchToGenerated(modArch *postgres.ModArch) *generated.ModArch { + if modArch == nil { + return nil + } + + size := int(modArch.Size) + + return &generated.ModArch{ + ID: modArch.ID, + ModVersionArchID: modArch.ModVersionArchID, + Platform: modArch.Platform, + Hash: &modArch.Hash, + Size: &size, + } +} + +func DBModArchsToGeneratedSlice(modArchs []postgres.ModArch) []*generated.ModArch { + converted := make([]*generated.ModArch, len(modArchs)) + for i, modArch := range modArchs { + converted[i] = DBModArchToGenerated(&modArch) + } + return converted +} + +func DBSMLArchToGenerated(smlLink *postgres.SMLArch) *generated.SMLArch { + if smlLink == nil { + return nil + } + + return &generated.SMLArch{ + ID: smlLink.ID, + SMLVersionArchID: smlLink.SMLVersionArchID, + Platform: smlLink.Platform, + Link: smlLink.Link, + } +} + +func DBSMLArchsToGeneratedSlice(smlLinks []postgres.SMLArch) []*generated.SMLArch { + converted := make([]*generated.SMLArch, len(smlLinks)) + for i, smlLink := range smlLinks { + converted[i] = DBSMLArchToGenerated(&smlLink) + } + return converted +} + func GenCompInfoToDBCompInfo(gen *generated.CompatibilityInfoInput) *postgres.CompatibilityInfo { if gen == nil { return nil diff --git a/gql/resolver.go b/gql/resolver.go index 07b9e99..3cb3c6c 100755 --- a/gql/resolver.go +++ b/gql/resolver.go @@ -21,6 +21,9 @@ func (r *Resolver) User() generated.UserResolver { func (r *Resolver) UserMod() generated.UserModResolver { return &userModResolver{r} } +func (r *Resolver) ModArch() generated.ModArchResolver { + return &modlinkResolver{r} +} func (r *Resolver) Version() generated.VersionResolver { return &versionResolver{r} } diff --git a/gql/resolver_mod_archs.go b/gql/resolver_mod_archs.go new file mode 100644 index 0000000..02b1686 --- /dev/null +++ b/gql/resolver_mod_archs.go @@ -0,0 +1,34 @@ +package gql + +import ( + "context" + + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/generated" +) + +type modlinkResolver struct{ *Resolver } + +func (r *modlinkResolver) Asset(_ context.Context, obj *generated.ModArch) (string, error) { + return "/v1/version/" + obj.ModVersionArchID + "/" + obj.Platform + "/download", nil +} + +func (r *queryResolver) GetModArch(ctx context.Context, modArchID string) (*generated.ModArch, error) { + wrapper, newCtx := WrapQueryTrace(ctx, "getModArch") + defer wrapper.end() + modArch := postgres.GetModArch(newCtx, modArchID) + return DBModArchToGenerated(modArch), nil +} + +func (r *queryResolver) GetModArchs(ctx context.Context, filter map[string]interface{}) (*generated.GetModArchs, error) { + wrapper, _ := WrapQueryTrace(ctx, "getModArchs") + defer wrapper.end() + return &generated.GetModArchs{}, nil +} + +func (r *queryResolver) GetModArchByID(ctx context.Context, modArchID string) (*generated.ModArch, error) { + wrapper, newCtx := WrapQueryTrace(ctx, "getModArchByID") + defer wrapper.end() + modArch := postgres.GetModArchByID(newCtx, modArchID) + return DBModArchToGenerated(modArch), nil +} diff --git a/gql/resolver_sml_archs.go b/gql/resolver_sml_archs.go new file mode 100644 index 0000000..e504db8 --- /dev/null +++ b/gql/resolver_sml_archs.go @@ -0,0 +1,97 @@ +package gql + +import ( + "context" + + "github.com/pkg/errors" + "github.com/satisfactorymodding/smr-api/util" + "gopkg.in/go-playground/validator.v9" + + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/generated" +) + +func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlLink generated.NewSMLArch) (*generated.SMLArch, error) { + wrapper, newCtx := WrapMutationTrace(ctx, "createSMLArch") + defer wrapper.end() + + val := ctx.Value(util.ContextValidator{}).(*validator.Validate) + if err := val.Struct(&smlLink); err != nil { + return nil, errors.Wrap(err, "validation failed") + } + + dbSMLArchs := &postgres.SMLArch{ + ID: string(util.GenerateUniqueID()), + SMLVersionArchID: string(smlLink.SMLVersionArchID), + Platform: string(smlLink.Platform), + Link: string(smlLink.Link), + } + + resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) + + if err != nil { + return nil, err + } + + return DBSMLArchToGenerated(resultSMLArch), nil +} + +func (r *mutationResolver) DeleteSMLArch(ctx context.Context, linksID string) (bool, error) { + wrapper, newCtx := WrapMutationTrace(ctx, "deleteSMLArch") + defer wrapper.end() + + dbSMLArch := postgres.GetSMLArchByID(newCtx, linksID) + + if dbSMLArch == nil { + return false, errors.New("SML Link not found") + } + + postgres.Delete(newCtx, &dbSMLArch) + + return true, nil +} + +func (r *mutationResolver) UpdateSMLArch(ctx context.Context, smlLinkID string, smlLink generated.UpdateSMLArch) (*generated.SMLArch, error) { + wrapper, newCtx := WrapMutationTrace(ctx, "updateSMLArch") + defer wrapper.end() + val := ctx.Value(util.ContextValidator{}).(*validator.Validate) + if err := val.Struct(&smlLink); err != nil { + return nil, errors.Wrap(err, "validation failed") + } + + dbSMLArch := postgres.GetSMLArch(newCtx, smlLinkID) + + if dbSMLArch == nil { + return nil, errors.New("sml link not found") + } + + SetStringINNOE((*string)(&smlLink.SMLVersionArchID), &dbSMLArch.SMLVersionArchID) + SetStringINNOE((*string)(&smlLink.Platform), &dbSMLArch.Platform) + SetStringINNOE((*string)(&smlLink.Link), &dbSMLArch.Link) + + postgres.Save(newCtx, &dbSMLArch) + + return DBSMLArchToGenerated(dbSMLArch), nil +} + +func (r *queryResolver) GetSMLArch(ctx context.Context, smlLinkID string) (*generated.SMLArch, error) { + wrapper, newCtx := WrapQueryTrace(ctx, "getSMLArch") + defer wrapper.end() + + smlLink := postgres.GetSMLArch(newCtx, smlLinkID) + + return DBSMLArchToGenerated(smlLink), nil +} + +func (r *queryResolver) GetSMLArchs(ctx context.Context, filter map[string]interface{}) (*generated.GetSMLArchs, error) { + wrapper, _ := WrapQueryTrace(ctx, "getSMLArchs") + defer wrapper.end() + return &generated.GetSMLArchs{}, nil +} + +func (r *queryResolver) GetSMLDownload(ctx context.Context, smlVersionID string, platform string) (string, error) { + wrapper, newCtx := WrapQueryTrace(ctx, "getSMLDownload") + defer wrapper.end() + smlLink := postgres.GetSMLArchDownload(newCtx, smlVersionID, platform) + return smlLink, nil +} diff --git a/gql/resolver_sml_versions.go b/gql/resolver_sml_versions.go index 0bfb287..ac2ba17 100644 --- a/gql/resolver_sml_versions.go +++ b/gql/resolver_sml_versions.go @@ -42,6 +42,23 @@ func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion gene resultSMLVersion, err := postgres.CreateSMLVersion(newCtx, dbSMLVersion) + for _, smlLink := range smlVersion.Arch { + dbSMLArchs := &postgres.SMLArch{ + ID: util.GenerateUniqueID(), + SMLVersionArchID: string(resultSMLVersion.ID), + Platform: string(smlLink.Platform), + Link: string(smlLink.Link), + } + + resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) + + if err != nil { + return nil, err + } + + DBSMLArchToGenerated(resultSMLArch) + } + if err != nil { return nil, err } @@ -72,6 +89,17 @@ func (r *mutationResolver) UpdateSMLVersion(ctx context.Context, smlVersionID st SetStringINNOE(smlVersion.Changelog, &dbSMLVersion.Changelog) SetDateINN(smlVersion.Date, &dbSMLVersion.Date) + for _, smlLink := range smlVersion.Arch { + dbSMLArch := postgres.GetSMLArch(newCtx, smlLink.ID) + + SetStringINNOE((*string)(&smlLink.ID), &dbSMLArch.ID) + SetStringINNOE((*string)(&smlLink.SMLVersionArchID), &dbSMLArch.SMLVersionArchID) + SetStringINNOE((*string)(&smlLink.Platform), &dbSMLArch.Platform) + SetStringINNOE((*string)(&smlLink.Link), &dbSMLArch.Link) + + postgres.Save(newCtx, dbSMLArch) + } + postgres.Save(newCtx, &dbSMLVersion) return DBSMLVersionToGenerated(dbSMLVersion), nil @@ -87,6 +115,16 @@ func (r *mutationResolver) DeleteSMLVersion(ctx context.Context, smlVersionID st return false, errors.New("smlVersion not found") } + for _, smlLink := range dbSMLVersion.Arch { + dbSMLArch := postgres.GetSMLArch(newCtx, smlLink.ID) + + if dbSMLVersion == nil { + return false, errors.New("smlLink not found") + } + + postgres.Delete(newCtx, &dbSMLArch) + } + postgres.Delete(newCtx, &dbSMLVersion) return true, nil diff --git a/gql/resolver_versions.go b/gql/resolver_versions.go index 2d93959..bb6332c 100644 --- a/gql/resolver_versions.go +++ b/gql/resolver_versions.go @@ -186,6 +186,7 @@ func (r *mutationResolver) ApproveVersion(ctx context.Context, versionID string) postgres.Save(newCtx, &mod) go integrations.NewVersion(util.ReWrapCtx(ctx), dbVersion) + go storage.DeleteModArch(ctx, dbVersion.ModID, mod.Name, versionID, "Combined") return true, nil } diff --git a/gql/versions.go b/gql/versions.go index a4f1dc1..ff491f8 100644 --- a/gql/versions.go +++ b/gql/versions.go @@ -68,8 +68,6 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI ModID: mod.ID, Stability: string(version.Stability), ModReference: &modInfo.ModReference, - Size: &modInfo.Size, - Hash: &modInfo.Hash, VersionMajor: &versionMajor, VersionMinor: &versionMinor, VersionPatch: &versionPatch, @@ -123,10 +121,10 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI postgres.Save(ctx, &dbVersion) } - // TODO Validate mod files - success, key := storage.RenameVersion(ctx, mod.ID, mod.Name, versionID, modInfo.Version) + //Okay, uploaded file is read and readable... let's dump it and separate, and re-save as ModName-Combined-SemVersion in ModArch + separated := storage.SeparateMod(ctx, fileData, mod.ID, mod.Name, dbVersion.ID, modInfo.Version) - if !success { + if !separated { for modID, condition := range modInfo.Dependencies { dependency := postgres.VersionDependency{ VersionID: dbVersion.ID, @@ -151,13 +149,23 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI postgres.DeleteForced(ctx, &dbVersion) storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) + + for _, dbModArch := range dbVersion.Arch { + postgres.DeleteForced(ctx, &dbModArch) + } return nil, errors.New("failed to upload mod") } - dbVersion.Key = key + dbModArch := postgres.GetModArchByPlatform(ctx, dbVersion.ID, "WindowsNoEditor") + + dbVersion.Key = dbModArch.Key + dbVersion.Hash = &dbModArch.Hash + dbVersion.Size = &dbModArch.Size postgres.Save(ctx, &dbVersion) postgres.Save(ctx, &mod) + storage.DeleteVersion(ctx, mod.ID, mod.Name, versionID) + if autoApproved { mod := postgres.GetModByID(ctx, dbVersion.ModID) now := time.Now() @@ -165,6 +173,7 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI postgres.Save(ctx, &mod) go integrations.NewVersion(util.ReWrapCtx(ctx), dbVersion) + storage.DeleteModArch(ctx, mod.ID, mod.Name, versionID, "Combined") } else { l.Info().Msg("Submitting version job for virus scan") jobs.SubmitJobScanModOnVirusTotalTask(ctx, mod.ID, dbVersion.ID, true) diff --git a/gqlgen.yml b/gqlgen.yml index 445684c..ecb1118 100755 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -20,6 +20,10 @@ models: UpdateMod: model: github.com/satisfactorymodding/smr-api/generated.UpdateMod + ModArchFilter: + model: "map[string]interface{}" + SMLArchFilter: + model: "map[string]interface{}" VersionFilter: model: "map[string]interface{}" ModFilter: @@ -53,6 +57,11 @@ models: latestVersions: resolver: true + ModArch: + fields: + asset: + resolver: true + UserMod: fields: user: diff --git a/migrations/sql/000020_add_compatibility.up.sql b/migrations/sql/000020_add_compatibility.up.sql old mode 100755 new mode 100644 diff --git a/migrations/sql/000021_add_mod_platform.down.sql b/migrations/sql/000021_add_mod_platform.down.sql new file mode 100644 index 0000000..93b25e0 --- /dev/null +++ b/migrations/sql/000021_add_mod_platform.down.sql @@ -0,0 +1,2 @@ +drop table if exists mod_archs; +drop table if exists sml_archs; \ No newline at end of file diff --git a/migrations/sql/000021_add_mod_platform.up.sql b/migrations/sql/000021_add_mod_platform.up.sql new file mode 100644 index 0000000..90e544d --- /dev/null +++ b/migrations/sql/000021_add_mod_platform.up.sql @@ -0,0 +1,21 @@ +-- Mod Links +create table if not exists mod_archs +( + id varchar(14) not null constraint mod_archs_pkey primary key, + mod_version_arch_id varchar(14), + platform varchar(16), + key text, + hash char(64), + size bigint +); +create index if not exists idx_mod_arch_id on mod_archs (mod_version_arch_id, platform); + +-- SML Links +create table if not exists sml_archs +( + id varchar(14) not null constraint sml_archs_pkey primary key, + sml_version_arch_id varchar(14), + platform varchar(16), + link text +); +create index if not exists idx_sml_archs_id on sml_archs (sml_version_arch_id, platform); \ No newline at end of file diff --git a/models/filters.go b/models/filters.go index 1023c2b..688f3a9 100644 --- a/models/filters.go +++ b/models/filters.go @@ -330,3 +330,85 @@ func ProcessBootstrapVersionFilter(filter map[string]interface{}) (*BootstrapVer return base, nil } + +type SMLArchFilter struct { + Limit *int `json:"limit" validate:"omitempty,min=1,max=100"` + Offset *int `json:"offset" validate:"omitempty,min=0"` + OrderBy *generated.SMLArchFields `json:"order_by"` + Order *generated.Order `json:"order"` + Search *string `json:"search" validate:"omitempty,min=3"` + Ids []string `json:"ids" validate:"omitempty,max=100"` +} + +func DefaultSMLArchFilter() *SMLArchFilter { + limit := 10 + offset := 0 + order := generated.OrderDesc + orderBy := generated.SMLArchFieldsPlatform + return &SMLArchFilter{ + Limit: &limit, + Offset: &offset, + Ids: nil, + Order: &order, + OrderBy: &orderBy, + } +} + +func ProcessSMLArchFilter(filter map[string]interface{}) (*SMLArchFilter, error) { + base := DefaultSMLArchFilter() + + if filter == nil { + return base, nil + } + + if err := ApplyChanges(filter, base); err != nil { + return nil, err + } + + if err := dataValidator.Struct(base); err != nil { + return nil, errors.Wrap(err, "failed to validate SMLArchFilter") + } + + return base, nil +} + +type ModArchFilter struct { + Limit *int `json:"limit" validate:"omitempty,min=1,max=100"` + Offset *int `json:"offset" validate:"omitempty,min=0"` + OrderBy *generated.ModArchFields `json:"order_by"` + Order *generated.Order `json:"order"` + Search *string `json:"search" validate:"omitempty,min=3"` + Ids []string `json:"ids" validate:"omitempty,max=100"` +} + +func DefaultModArchFilter() *ModArchFilter { + limit := 10 + offset := 0 + order := generated.OrderDesc + orderBy := generated.ModArchFieldsPlatform + return &ModArchFilter{ + Limit: &limit, + Offset: &offset, + Ids: nil, + Order: &order, + OrderBy: &orderBy, + } +} + +func ProcessModArchFilter(filter map[string]interface{}) (*ModArchFilter, error) { + base := DefaultModArchFilter() + + if filter == nil { + return base, nil + } + + if err := ApplyChanges(filter, base); err != nil { + return nil, err + } + + if err := dataValidator.Struct(base); err != nil { + return nil, errors.Wrap(err, "failed to validate ModArchFilter") + } + + return base, nil +} diff --git a/nodes/mod.go b/nodes/mod.go index ae9b605..4e1dda5 100644 --- a/nodes/mod.go +++ b/nodes/mod.go @@ -273,13 +273,13 @@ func downloadModVersion(c echo.Context) error { mod := postgres.GetModByID(c.Request().Context(), modID) if mod == nil { - return c.String(404, "mod not found") + return c.String(404, "mod not found, modID:"+modID) } version := postgres.GetModVersion(c.Request().Context(), mod.ID, versionID) if version == nil { - return c.String(404, "version not found") + return c.String(404, "version not found, modID:"+modID+" versionID:"+versionID) } if redis.CanIncrement(c.RealIP(), "download", "version:"+versionID, time.Hour*4) { @@ -288,3 +288,43 @@ func downloadModVersion(c echo.Context) error { return c.Redirect(302, storage.GenerateDownloadLink(version.Key)) } + +// @Summary Download a Mod Version by Platform +// @Tags Mod +// @Description Download a mod version by mod ID and version ID and Platform +// @Accept json +// @Produce json +// @Param modId path string true "Mod ID" +// @Param versionId path string true "Version ID" +// @Param versionId path string true "Platform" +// @Success 200 +// @Router /mod/{modId}/versions/{versionId}/{platform}/download [get] +func downloadModVersionArch(c echo.Context) error { + modID := c.Param("modId") + versionID := c.Param("versionId") + platform := c.Param("platform") + + mod := postgres.GetModByID(c.Request().Context(), modID) + + if mod == nil { + return c.String(404, "mod not found, modID:"+modID) + } + + version := postgres.GetModVersion(c.Request().Context(), mod.ID, versionID) + + if version == nil { + return c.String(404, "version not found, modID:"+modID+" versionID:"+versionID) + } + + arch := postgres.GetModArchByPlatform(c.Request().Context(), versionID, platform) + + if arch == nil { + return c.String(404, "platform not found, modID:"+modID+" versionID:"+versionID+" platform:"+platform) + } + + if redis.CanIncrement(c.RealIP(), "download", "version:"+versionID, time.Hour*4) { + postgres.IncrementVersionDownloads(c.Request().Context(), version) + } + + return c.Redirect(302, storage.GenerateDownloadLink(arch.Key)) +} diff --git a/nodes/routes.go b/nodes/routes.go index dd563e8..8cd75b8 100755 --- a/nodes/routes.go +++ b/nodes/routes.go @@ -15,6 +15,7 @@ func RegisterModRoutes(router *echo.Group) { router.GET("/:modId/versions/:versionId", dataWrapper(getModVersion)) router.GET("/:modId/versions/:versionId/download", downloadModVersion) + router.GET("/:modId/versions/:versionId/:platform/download", downloadModVersionArch) } func RegisterModsRoutes(router *echo.Group) { @@ -47,6 +48,7 @@ func RegisterUsersRoutes(router *echo.Group) { func RegisterVersionRoutes(router *echo.Group) { router.GET("/:versionId", dataWrapper(getVersion)) router.GET("/:versionId/download", downloadVersion) + router.GET("/:versionId/:platform/download", downloadModArch) } func RegisterSMLRoutes(router *echo.Group) { diff --git a/nodes/version.go b/nodes/version.go index c3134c4..b8ab977 100644 --- a/nodes/version.go +++ b/nodes/version.go @@ -52,3 +52,36 @@ func downloadVersion(c echo.Context) error { return c.Redirect(302, storage.GenerateDownloadLink(version.Key)) } + +// @Summary Download a Platform +// @Tags Version +// @Tags Platform +// @Description Download a mod version by version ID and Platform +// @Accept json +// @Produce json +// @Param versionId path string true "Version ID" +// @Param versionId path string true "Version ID" +// @Success 200 +// @Router /versions/{versionId}/{platform}/download [get] +func downloadModArch(c echo.Context) error { + versionID := c.Param("versionId") + platform := c.Param("platform") + + version := postgres.GetVersion(c.Request().Context(), versionID) + + if version == nil { + return c.String(404, "version not found, versionID:"+versionID) + } + + arch := postgres.GetModArchByPlatform(c.Request().Context(), versionID, platform) + + if arch == nil { + return c.String(404, "platform not found, versionID:"+versionID+" platform:"+platform) + } + + if redis.CanIncrement(c.RealIP(), "download", "version:"+versionID, time.Hour*4) { + postgres.IncrementVersionDownloads(c.Request().Context(), version) + } + + return c.Redirect(302, storage.GenerateDownloadLink(arch.Key)) +} diff --git a/oauth/facebook.go b/oauth/facebook.go index db3cf33..69c27cb 100644 --- a/oauth/facebook.go +++ b/oauth/facebook.go @@ -4,10 +4,8 @@ import ( "encoding/json" "io" - "github.com/satisfactorymodding/smr-api/redis" - "github.com/pkg/errors" - + "github.com/satisfactorymodding/smr-api/redis" "golang.org/x/oauth2" ) diff --git a/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go b/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go index 46d4d7d..f024084 100644 --- a/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go +++ b/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go @@ -39,7 +39,10 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { log.Info().Msgf("starting virus scan of mod %s version %s", task.ModID, task.VersionID) version := postgres.GetVersion(ctx, task.VersionID) - link := storage.GenerateDownloadLink(version.Key) + mod := postgres.GetModByID(ctx, version.ModID) + + modArch := postgres.GetModArchByPlatform(ctx, task.VersionID, "Combined") + link := storage.GenerateDownloadLink(modArch.Key) response, _ := http.Get(link) @@ -58,7 +61,7 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { toScan := make([]io.Reader, 0) names := make([]string, 0) for _, file := range archive.File { - if path.Ext(file.Name) == ".dll" { + if path.Ext(file.Name) == ".dll" || path.Ext(file.Name) == ".so" { open, err := file.Open() if err != nil { @@ -94,5 +97,8 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { go integrations.NewVersion(util.ReWrapCtx(ctx), version) } + go storage.DeleteModArch(ctx, mod.ID, mod.Name, version.ID, "Combined") + go postgres.Delete(ctx, modArch) + return nil } diff --git a/schemas/mod_archs.graphql b/schemas/mod_archs.graphql new file mode 100644 index 0000000..f1a65f9 --- /dev/null +++ b/schemas/mod_archs.graphql @@ -0,0 +1,47 @@ +### Types + +scalar ModArchID + +type ModArch { + id: ModArchID! + ModVersionArchID: String! + platform: String! + asset: String! + size: Int + hash: String +} + +enum ModArchFields { + platform +} + +type GetModArchs { + arch: [ModArch!]! +} + +### Inputs + +input ModArchFilter { + limit: Int + offset: Int + order_by: ModArchFields + order: Order + search: String + ids: [String!] +} + +input NewModArch { + id: ModArchID + ModVersionArchID: String! + platform: String! + key: String! + size: Int + hash: String +} + +### Queries + +extend type Query { + getModArch(id: ModArchID!): ModArch + getModArchs(filter: ModArchFilter): GetModArchs! +} \ No newline at end of file diff --git a/schemas/sml_archs.graphql b/schemas/sml_archs.graphql new file mode 100644 index 0000000..0acf854 --- /dev/null +++ b/schemas/sml_archs.graphql @@ -0,0 +1,50 @@ +### Types + +scalar SMLArchID + +type SMLArch { + id: SMLArchID! + SMLVersionArchID: String! + platform: String! + link: String! +} + +enum SMLArchFields { + platform +} + +type GetSMLArchs { + arch: [SMLArch!]! +} + +### Inputs + +input SMLArchFilter { + limit: Int + offset: Int + order_by: SMLArchFields + order: Order + search: String + ids: [String!] +} + +input NewSMLArch { + id: SMLArchID! + SMLVersionArchID: String! + platform: String! + link: String! +} + +input UpdateSMLArch { + id: SMLArchID! + SMLVersionArchID: String! + platform: String! + link: String! +} + +### Queries + +extend type Query { + getSMLArch(smlVersionId: SMLVersionID!): SMLArch + getSMLArchs(filter: SMLArchFilter): GetSMLArchs! +} \ No newline at end of file diff --git a/schemas/sml_version.graphql b/schemas/sml_version.graphql index 90ed470..ef207ca 100755 --- a/schemas/sml_version.graphql +++ b/schemas/sml_version.graphql @@ -8,6 +8,7 @@ type SMLVersion { satisfactory_version: Int! stability: VersionStabilities! link: String! + arch: [SMLArch]! changelog: String! date: Date! bootstrap_version: String @@ -36,6 +37,7 @@ input NewSMLVersion { satisfactory_version: Int! stability: VersionStabilities! link: String! + arch: [NewSMLArch!]! changelog: String! date: Date! bootstrap_version: String @@ -46,6 +48,7 @@ input UpdateSMLVersion { satisfactory_version: Int stability: VersionStabilities link: String + arch: [UpdateSMLArch]! changelog: String date: Date bootstrap_version: String diff --git a/schemas/version.graphql b/schemas/version.graphql index 0677402..2513639 100755 --- a/schemas/version.graphql +++ b/schemas/version.graphql @@ -32,6 +32,7 @@ type Version { updated_at: Date! created_at: Date! link: String! + arch: [ModArch]! metadata: String size: Int hash: String diff --git a/static/data-json-schema.json b/static/data-json-schema.json index 1471f2b..c66445a 100755 --- a/static/data-json-schema.json +++ b/static/data-json-schema.json @@ -89,7 +89,7 @@ "examples": [ "SDF.dll" ], - "pattern": "^(.*)\\.(pak|dll)$" + "pattern": "^(.*)\\.(pak|dll|so)$" }, "type": { "type": "string", diff --git a/storage/storage.go b/storage/storage.go index c0de920..8cf03a7 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,12 +1,17 @@ package storage import ( + "archive/zip" + "bytes" "context" + "crypto/sha256" + "encoding/hex" "fmt" "io" "strings" "github.com/avast/retry-go/v3" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/pkg/errors" @@ -272,7 +277,7 @@ func RenameVersion(ctx context.Context, modID string, name string, versionID str return true, fmt.Sprintf("/mods/%s/%s.smod", modID, EncodeName(cleanName)+"-"+version) } -func DeleteMod(ctx context.Context, modID string, name string, versionID string) bool { +func DeleteVersion(ctx context.Context, modID string, name string, versionID string) bool { if storage == nil { return false } @@ -280,7 +285,6 @@ func DeleteMod(ctx context.Context, modID string, name string, versionID string) cleanName := cleanModName(name) key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+versionID) - if err := storage.Delete(key); err != nil { log.Err(err).Msg("failed to delete version") return false @@ -289,6 +293,49 @@ func DeleteMod(ctx context.Context, modID string, name string, versionID string) return true } +func DeleteMod(ctx context.Context, modID string, name string, versionID string) bool { + if storage == nil { + return false + } + + cleanName := cleanModName(name) + + query := postgres.GetModVersion(ctx, modID, versionID) + + if len(query.Arch) != 0 { + for _, link := range query.Arch { + if success := DeleteModArch(ctx, modID, cleanName, versionID, link.Platform); !success { + return false + } + } + } else { + key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+versionID) + + if err := storage.Delete(key); err != nil { + log.Ctx(ctx).Err(err).Msg("failed to delete version") + return false + } + } + + return true +} + +func DeleteModArch(ctx context.Context, modID string, name string, versionID string, platform string) bool { + if storage == nil { + return false + } + + cleanName := cleanModName(name) + key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+platform+"-"+versionID) + + if err := storage.Delete(key); err != nil { + log.Ctx(ctx).Err(err).Msg("failed to delete version link") + return false + } + + return true +} + func ModVersionMeta(ctx context.Context, modID string, name string, versionID string) *ObjectMeta { if storage == nil { return nil @@ -348,3 +395,111 @@ func EncodeName(name string) string { } return result } + +func SeparateMod(ctx context.Context, body []byte, modID, name string, versionID string, modVersion string) bool { + //read combined file + zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) + + if err != nil { + return false + } + + ModPlatforms := []string{"Combined", "WindowsNoEditor", "WindowsServer", "LinuxServer"} + cleanName := cleanModName(name) + bufPlatform := bytes.NewBuffer(body) + + for _, ModPlatform := range ModPlatforms { + if ModPlatform != "Combined" { + bufPlatform = new(bytes.Buffer) + zipWriter := zip.NewWriter(bufPlatform) + + for _, file := range zipReader.File { + if strings.HasPrefix(file.Name, ".pdb") || strings.HasPrefix(file.Name, ".debug") || !strings.Contains(file.Name, ModPlatform) { + continue + } + + err = WriteZipFile(ctx, file, ModPlatform, zipWriter) + + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to write zip to " + ModPlatform + " smod") + return false + } + } + + zipWriter.Close() + } + + key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+ModPlatform+"-"+modVersion) + + err = WriteModArch(ctx, key, versionID, ModPlatform, bufPlatform) + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to save " + ModPlatform + " smod") + return false + } + } + + return true +} + +func WriteZipFile(ctx context.Context, file *zip.File, platform string, zipWriter *zip.Writer) error { + fileName := strings.ReplaceAll(file.Name, platform+"/", "") + zipFile, err := zipWriter.Create(fileName) + + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to create smod file for " + platform) + return errors.Wrap(err, "Failed to open smod file for "+platform) + } + + rawFile, err := file.Open() + + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to open smod file for " + platform) + return errors.Wrap(err, "Failed to open smod file for "+platform) + } + + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(rawFile) + + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to read from buffer for " + platform) + return errors.Wrap(err, "Failed to read from buffer for "+platform) + } + + _, err = zipFile.Write(buf.Bytes()) + + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to write to smod file: " + platform) + return errors.Wrap(err, "Failed to write smod file for "+platform) + } + + return nil +} + +func WriteModArch(ctx context.Context, key string, versionID string, platform string, buffer *bytes.Buffer) error { + _, err := storage.Put(ctx, key, bytes.NewReader(buffer.Bytes())) + + if err != nil { + log.Ctx(ctx).Err(err).Msg("failed to write smod: " + key) + return errors.Wrap(err, "Failed to load smod:"+key) + } + + hash := sha256.New() + hash.Write(buffer.Bytes()) + + dbModArch := &postgres.ModArch{ + ModVersionArchID: versionID, + Platform: platform, + Key: key, + Hash: hex.EncodeToString(hash.Sum(nil)), + Size: int64(len(buffer.Bytes())), + } + + _, err = postgres.CreateModArch(ctx, dbModArch) + + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to create ModArch: " + versionID + "-" + platform) + return errors.Wrap(err, "Failed to create ModArch: "+versionID+"-"+platform) + } + + return nil +} diff --git a/util/image.go b/util/image.go index dc355ea..eddbe7a 100644 --- a/util/image.go +++ b/util/image.go @@ -11,9 +11,8 @@ import ( "io" "net/http" - "github.com/satisfactorymodding/smr-api/util/converter" - "github.com/pkg/errors" + "github.com/satisfactorymodding/smr-api/util/converter" ) func LinkToWebp(ctx context.Context, url string) ([]byte, error) { diff --git a/validation/validation.go b/validation/validation.go index ab40c11..f58e9fa 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -78,7 +78,7 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal dataFile = v break } - if v.Name == modReference+".uplugin" { + if v.Name == "WindowsNoEditor/"+modReference+".uplugin" { uPlugin = v break } @@ -101,7 +101,7 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal } if modInfo == nil { - return nil, errors.New("missing " + modReference + ".uplugin or data.json") + return nil, errors.New("missing WindowsNoEditor/" + modReference + ".uplugin or data.json") } if withMetadata { @@ -221,7 +221,7 @@ func validateDataJSON(archive *zip.Reader, dataFile *zip.File, withValidation bo // Validate that all listed files are accounted for in data.json for _, archiveFile := range archive.File { if archiveFile != nil { - if strings.HasSuffix(archiveFile.Name, ".dll") || strings.HasSuffix(archiveFile.Name, ".pak") { + if strings.HasSuffix(archiveFile.Name, ".dll") || strings.HasSuffix(archiveFile.Name, ".pak") || strings.HasSuffix(archiveFile.Name, ".so") { found := false for _, obj := range modInfo.Objects { if obj.Path == archiveFile.Name { @@ -340,7 +340,7 @@ func validateUPluginJSON(archive *zip.Reader, uPluginFile *zip.File, withValidat Path: file.Name, Type: "pak", }) - } else if extension == "dll" { + } else if extension == "dll" || extension == "so" { modInfo.Objects = append(modInfo.Objects, ModObject{ Path: file.Name, Type: "sml_mod", From cb70874eb624839f1fa70a31f7efa9b9ba07b2c3 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 19 Sep 2022 00:35:06 +0300 Subject: [PATCH 02/21] fix: correctly take pointer when storing db objects in cache chore: add unit tests --- .github/workflows/build.yml | 50 +++++++++- README.md | 2 +- api.go | 27 ++++- cmd/api/serve.go | 4 +- config/config.go | 17 +++- db/postgres/announcement.go | 3 +- db/postgres/mod_archs.go | 5 +- db/postgres/sml_archs.go | 31 +++--- db/postgres/tags.go | 8 +- go.mod | 13 +++ go.sum | 43 +++++++- gql/resolver_sml_archs.go | 15 ++- gql/resolver_sml_versions.go | 14 +-- migrations/migrations.go | 8 +- migrations/sql/000015_enable_trgm.up.sql | 2 +- schemas/mod_archs.graphql | 9 -- schemas/sml_archs.graphql | 2 - tests/announcements_test.go | 122 +++++++++++++++++++++++ tests/utils.go | 109 ++++++++++++++++++++ 19 files changed, 422 insertions(+), 62 deletions(-) create mode 100644 tests/announcements_test.go create mode 100644 tests/utils.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84083eb..1d040fc 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,54 @@ jobs: build: name: Build runs-on: ubuntu-latest + steps: + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + + - name: Download dependencies + run: sudo apt update && sudo apt install -y build-essential libpng-dev + + - name: Go Generate + run: go generate -tags tools -x ./... + + - name: Build + run: go build -v -o api-linux-amd64 . + env: + CGO_ENABLED: 1 + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + + - name: Download dependencies + run: sudo apt update && sudo apt install -y build-essential libpng-dev + + - name: Go Generate + run: go generate -tags tools -x ./... + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + skip-pkg-cache: true + skip-build-cache: true + args: --timeout 5m + + test: + name: Test + runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@v3 @@ -29,6 +77,6 @@ jobs: args: --timeout 5m - name: Build - run: go build -v -o api-linux-amd64 . + run: go test -v ./... env: CGO_ENABLED: 1 diff --git a/README.md b/README.md index b9e8dfb..d5257f9 100755 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ To run the API, you will need to have a working Postgres, Redis and Storage. The start via: ```bash -docker-compose -f docker-compose.dev.yml up -d +docker-compose -f docker-compose-dev.yml up -d ``` It is suggested you create a configuration file at `config.json` (but you can also use environment variables). diff --git a/api.go b/api.go index c167f7f..181e5ba 100644 --- a/api.go +++ b/api.go @@ -64,8 +64,8 @@ func (cv *CustomValidator) Validate(i interface{}) error { return errors.Wrap(cv.validator.Struct(i), "validation error") } -func Serve() { - ctx := config.InitializeConfig() +func Initialize(baseCtx context.Context) context.Context { + ctx := config.InitializeConfig(baseCtx) if os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") != "" { cleanup := installExportPipeline(ctx) @@ -82,8 +82,16 @@ func Serve() { jobs.InitializeJobs(ctx) validation.InitializeVirusTotal() + return ctx +} + +func Migrate(ctx context.Context) { migrations.RunMigrations(ctx) +} + +var e *echo.Echo +func Setup(ctx context.Context) { if viper.GetBool("profiler") { go func() { debugServer := echo.New() @@ -101,7 +109,7 @@ func Serve() { dataValidator := validator.New() - e := echo.New() + e = echo.New() e.HideBanner = true e.Validator = &CustomValidator{validator: dataValidator} @@ -271,7 +279,9 @@ func Serve() { <-signals _ = e.Close() }() +} +func Serve() { address := fmt.Sprintf(":%d", viper.GetInt("port")) log.Info().Str("address", address).Msg("starting server") @@ -311,3 +321,14 @@ func newResource() *resource.Resource { ) return r } + +func Start() { + ctx := Initialize(context.Background()) + Migrate(ctx) + Setup(ctx) + Serve() +} + +func Stop() error { + return errors.Wrap(e.Close(), "failed to stop http server") +} diff --git a/cmd/api/serve.go b/cmd/api/serve.go index 250aba1..2fc957c 100644 --- a/cmd/api/serve.go +++ b/cmd/api/serve.go @@ -1,6 +1,6 @@ package main -import smr "github.com/satisfactorymodding/smr-api" +import "github.com/satisfactorymodding/smr-api" // @title Satisfactory Mod Repo API // @version 1 @@ -12,5 +12,5 @@ import smr "github.com/satisfactorymodding/smr-api" // @host api.ficsit.app // @BasePath /v1 func main() { - smr.Serve() + smr.Start() } diff --git a/config/config.go b/config/config.go index c1103c1..1a85e5f 100644 --- a/config/config.go +++ b/config/config.go @@ -11,9 +11,15 @@ import ( "github.com/spf13/viper" ) -func InitializeConfig() context.Context { +var configDir = "." + +func SetConfigDir(newConfigDir string) { + configDir = newConfigDir +} + +func InitializeConfig(baseCtx context.Context) context.Context { viper.SetConfigName("config") - viper.AddConfigPath(".") + viper.AddConfigPath(configDir) viper.AutomaticEnv() viper.SetEnvPrefix("repo") @@ -30,7 +36,12 @@ func InitializeConfig() context.Context { } log.Logger = zerolog.New(out).With().Str("service", "api").Timestamp().Logger() - ctx := log.Logger.WithContext(context.Background()) + + if baseCtx == nil { + baseCtx = context.Background() + } + + ctx := log.Logger.WithContext(baseCtx) if err != nil { log.Warn().Err(err).Msg("config initialized using defaults and environment only!") diff --git a/db/postgres/announcement.go b/db/postgres/announcement.go index c7e614f..46481be 100644 --- a/db/postgres/announcement.go +++ b/db/postgres/announcement.go @@ -4,6 +4,7 @@ import ( "context" "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/util" ) @@ -27,7 +28,7 @@ func GetAnnouncementByID(ctx context.Context, announcementID string) *Announceme return nil } - dbCache.Set(cacheKey, announcement, cache.DefaultExpiration) + dbCache.Set(cacheKey, &announcement, cache.DefaultExpiration) return &announcement } diff --git a/db/postgres/mod_archs.go b/db/postgres/mod_archs.go index 2b7c3ae..5936116 100644 --- a/db/postgres/mod_archs.go +++ b/db/postgres/mod_archs.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" ) @@ -29,7 +30,7 @@ func GetModArch(ctx context.Context, modArchID string) *ModArch { return nil } - dbCache.Set(cacheKey, modArch, cache.DefaultExpiration) + dbCache.Set(cacheKey, &modArch, cache.DefaultExpiration) return &modArch } @@ -74,7 +75,7 @@ func GetModArchByID(ctx context.Context, modArchID string) *ModArch { return nil } - dbCache.Set(cacheKey, modArch, cache.DefaultExpiration) + dbCache.Set(cacheKey, &modArch, cache.DefaultExpiration) return &modArch } diff --git a/db/postgres/sml_archs.go b/db/postgres/sml_archs.go index 885d78c..876b8d7 100644 --- a/db/postgres/sml_archs.go +++ b/db/postgres/sml_archs.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" ) @@ -24,16 +25,16 @@ func GetSMLArch(ctx context.Context, smlLinkID string) *SMLArch { return smlLink.(*SMLArch) } - var smlLink SMLArch - DBCtx(ctx).Find(&smlLink, "id = ?", smlLinkID) + var smlArch SMLArch + DBCtx(ctx).Find(&smlArch, "id = ?", smlLinkID) - if smlLink.ID == "" { + if smlArch.ID == "" { return nil } - dbCache.Set(cacheKey, smlLink, cache.DefaultExpiration) + dbCache.Set(cacheKey, &smlArch, cache.DefaultExpiration) - return &smlLink + return &smlArch } func GetSMLArchs(ctx context.Context, filter *models.SMLArchFilter) []SMLArch { @@ -55,34 +56,34 @@ func GetSMLArchs(ctx context.Context, filter *models.SMLArchFilter) []SMLArch { } func GetSMLArchByID(ctx context.Context, smlLinkID string) []SMLArch { - var smlLinks []SMLArch + var smlArchs []SMLArch - DBCtx(ctx).Find(&smlLinks, "id in ?", smlLinkID) + DBCtx(ctx).Find(&smlArchs, "id in ?", smlLinkID) - if len(smlLinks) != 0 { + if len(smlArchs) != 0 { return nil } - return smlLinks + return smlArchs } -func GetSMLArchsByID(ctx context.Context, smlLinkIds []string) []SMLArch { - var smlLinks []SMLArch +func GetSMLArchsByID(ctx context.Context, smlArchIds []string) []SMLArch { + var smlArchs []SMLArch - DBCtx(ctx).Find(&smlLinks, "id in (?)", smlLinkIds) + DBCtx(ctx).Find(&smlArchs, "id in (?)", smlArchIds) - if len(smlLinkIds) != len(smlLinks) { + if len(smlArchIds) != len(smlArchs) { return nil } - return smlLinks + return smlArchs } func GetSMLArchDownload(ctx context.Context, smlVersionID string, platform string) string { var smlPlatform SMLArch DBCtx(ctx).First(&smlPlatform, "sml_version_arch_id = ? AND platform = ?", smlVersionID, platform) - if smlPlatform.SMLVersionArchID == "" { + if smlPlatform.ID == "" { return "" } diff --git a/db/postgres/tags.go b/db/postgres/tags.go index a157105..252faea 100644 --- a/db/postgres/tags.go +++ b/db/postgres/tags.go @@ -9,13 +9,11 @@ import ( "strings" "time" - "github.com/mitchellh/hashstructure/v2" "github.com/satisfactorymodding/smr-api/generated" - "github.com/satisfactorymodding/smr-api/util" "github.com/finnbear/moderation" - + "github.com/mitchellh/hashstructure/v2" "github.com/patrickmn/go-cache" ) @@ -88,7 +86,7 @@ func GetTagByName(ctx context.Context, tagName string) *Tag { return nil } - dbCache.Set(cacheKey, tag, cache.DefaultExpiration) + dbCache.Set(cacheKey, &tag, cache.DefaultExpiration) return &tag } @@ -107,7 +105,7 @@ func GetTagByID(ctx context.Context, tagID string) *Tag { return nil } - dbCache.Set(cacheKey, tag, cache.DefaultExpiration) + dbCache.Set(cacheKey, &tag, cache.DefaultExpiration) return &tag } diff --git a/go.mod b/go.mod index cffed55..2a6ed37 100755 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.19 require ( github.com/99designs/gqlgen v0.17.16 + github.com/MarvinJWendt/testza v0.4.3 github.com/Masterminds/semver/v3 v3.1.1 github.com/Vilsol/ue4pak v0.1.5 github.com/VirusTotal/vt-go v0.0.0-20220413144842-e010bf48aaee @@ -21,6 +22,7 @@ require ( github.com/lab259/go-migration v1.3.1 github.com/labstack/echo-contrib v0.13.0 github.com/labstack/echo/v4 v4.7.2 + github.com/machinebox/graphql v0.2.2 github.com/microcosm-cc/bluemonday v1.0.18 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/mapstructure v1.5.0 @@ -61,12 +63,14 @@ require ( github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb // indirect github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/atomicgo/cursor v0.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bsm/redislock v0.7.1 // indirect github.com/capnm/sysinfo v0.0.0-20130621111458-5909a53897f3 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -88,6 +92,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gookit/color v1.5.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect @@ -108,16 +113,22 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/cpuid/v2 v2.1.0 // indirect github.com/labstack/gommon v0.3.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.2 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/matryer/is v1.4.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect + github.com/pterm/pterm v0.12.40 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.2.0 // indirect github.com/sizeofint/webp-animation v0.0.0-20190207194838-b631dc900de9 // indirect github.com/spate/glimage v0.0.0-20200505055513-fbdcc60a65e5 // indirect github.com/spf13/afero v1.8.2 // indirect @@ -135,6 +146,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect go.opentelemetry.io/proto/otlp v0.16.0 // indirect @@ -142,6 +154,7 @@ require ( golang.org/x/exp v0.0.0-20210220032938-85be41e4509f // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect golang.org/x/tools v0.1.10 // indirect diff --git a/go.sum b/go.sum index 9204d1e..42c970c 100755 --- a/go.sum +++ b/go.sum @@ -89,6 +89,14 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.3 h1:u2XaM4IqGp9dsdUmML8/Z791fu4yjQYzOiufOtJwTII= +github.com/MarvinJWendt/testza v0.4.3/go.mod h1:CpXaOfceNEYnLDtNIyTrPPcCpDJYqzZnu2aiA2Wp33U= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= @@ -137,6 +145,7 @@ github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:Uz github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= @@ -161,6 +170,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/avast/retry-go/v3 v3.1.1 h1:49Scxf4v8PmiQ/nY0aY3p0hDueqSmc7++cBbtiDGu2g= github.com/avast/retry-go/v3 v3.1.1/go.mod h1:6cXRK369RpzFL3UQGqIUp9Q7GDrams+KsYWrfNA1/nQ= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= @@ -719,6 +730,9 @@ github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3i github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= @@ -909,6 +923,11 @@ github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= +github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -948,6 +967,8 @@ github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo= +github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= @@ -963,6 +984,8 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -983,6 +1006,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= @@ -1176,8 +1201,18 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40 h1:LvQE43RYegVH+y5sCDcqjlbsRu0DlAecEn9FDfs9ePs= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1204,8 +1239,9 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= @@ -1343,6 +1379,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= @@ -1757,6 +1795,7 @@ golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1771,12 +1810,14 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/gql/resolver_sml_archs.go b/gql/resolver_sml_archs.go index e504db8..ca1fc93 100644 --- a/gql/resolver_sml_archs.go +++ b/gql/resolver_sml_archs.go @@ -4,9 +4,10 @@ import ( "context" "github.com/pkg/errors" - "github.com/satisfactorymodding/smr-api/util" "gopkg.in/go-playground/validator.v9" + "github.com/satisfactorymodding/smr-api/util" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" ) @@ -21,10 +22,9 @@ func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlLink generated. } dbSMLArchs := &postgres.SMLArch{ - ID: string(util.GenerateUniqueID()), - SMLVersionArchID: string(smlLink.SMLVersionArchID), - Platform: string(smlLink.Platform), - Link: string(smlLink.Link), + ID: util.GenerateUniqueID(), + Platform: smlLink.Platform, + Link: smlLink.Link, } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) @@ -65,9 +65,8 @@ func (r *mutationResolver) UpdateSMLArch(ctx context.Context, smlLinkID string, return nil, errors.New("sml link not found") } - SetStringINNOE((*string)(&smlLink.SMLVersionArchID), &dbSMLArch.SMLVersionArchID) - SetStringINNOE((*string)(&smlLink.Platform), &dbSMLArch.Platform) - SetStringINNOE((*string)(&smlLink.Link), &dbSMLArch.Link) + SetStringINNOE(&smlLink.Platform, &dbSMLArch.Platform) + SetStringINNOE(&smlLink.Link, &dbSMLArch.Link) postgres.Save(newCtx, &dbSMLArch) diff --git a/gql/resolver_sml_versions.go b/gql/resolver_sml_versions.go index ac2ba17..bfe1a4b 100644 --- a/gql/resolver_sml_versions.go +++ b/gql/resolver_sml_versions.go @@ -45,9 +45,9 @@ func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion gene for _, smlLink := range smlVersion.Arch { dbSMLArchs := &postgres.SMLArch{ ID: util.GenerateUniqueID(), - SMLVersionArchID: string(resultSMLVersion.ID), - Platform: string(smlLink.Platform), - Link: string(smlLink.Link), + SMLVersionArchID: resultSMLVersion.ID, + Platform: smlLink.Platform, + Link: smlLink.Link, } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) @@ -92,10 +92,10 @@ func (r *mutationResolver) UpdateSMLVersion(ctx context.Context, smlVersionID st for _, smlLink := range smlVersion.Arch { dbSMLArch := postgres.GetSMLArch(newCtx, smlLink.ID) - SetStringINNOE((*string)(&smlLink.ID), &dbSMLArch.ID) - SetStringINNOE((*string)(&smlLink.SMLVersionArchID), &dbSMLArch.SMLVersionArchID) - SetStringINNOE((*string)(&smlLink.Platform), &dbSMLArch.Platform) - SetStringINNOE((*string)(&smlLink.Link), &dbSMLArch.Link) + SetStringINNOE(&smlLink.ID, &dbSMLArch.ID) + SetStringINNOE(&smlLink.SMLVersionArchID, &dbSMLArch.SMLVersionArchID) + SetStringINNOE(&smlLink.Platform, &dbSMLArch.Platform) + SetStringINNOE(&smlLink.Link, &dbSMLArch.Link) postgres.Save(newCtx, dbSMLArch) } diff --git a/migrations/migrations.go b/migrations/migrations.go index 5bf8cf1..9113424 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -21,6 +21,12 @@ func RunMigrations(ctx context.Context) { log.Info().Msg("Migrations Complete") } +var migrationDir = "./migrations" + +func SetMigrationDir(newMigrationDir string) { + migrationDir = newMigrationDir +} + func databaseMigrations(ctx context.Context) { db, _ := postgres2.DBCtx(ctx).DB() driver, err := postgres.WithInstance(db, &postgres.Config{}) @@ -29,7 +35,7 @@ func databaseMigrations(ctx context.Context) { panic(err) } - m, err := migrate.NewWithDatabaseInstance("file://./migrations/sql", "postgres", driver) + m, err := migrate.NewWithDatabaseInstance("file://"+migrationDir+"/sql", "postgres", driver) if err != nil { panic(err) diff --git a/migrations/sql/000015_enable_trgm.up.sql b/migrations/sql/000015_enable_trgm.up.sql index d497121..588aec0 100755 --- a/migrations/sql/000015_enable_trgm.up.sql +++ b/migrations/sql/000015_enable_trgm.up.sql @@ -1 +1 @@ -CREATE EXTENSION pg_trgm; +CREATE EXTENSION IF NOT EXISTS pg_trgm; diff --git a/schemas/mod_archs.graphql b/schemas/mod_archs.graphql index f1a65f9..cded1f6 100644 --- a/schemas/mod_archs.graphql +++ b/schemas/mod_archs.graphql @@ -30,15 +30,6 @@ input ModArchFilter { ids: [String!] } -input NewModArch { - id: ModArchID - ModVersionArchID: String! - platform: String! - key: String! - size: Int - hash: String -} - ### Queries extend type Query { diff --git a/schemas/sml_archs.graphql b/schemas/sml_archs.graphql index 0acf854..d020a24 100644 --- a/schemas/sml_archs.graphql +++ b/schemas/sml_archs.graphql @@ -29,8 +29,6 @@ input SMLArchFilter { } input NewSMLArch { - id: SMLArchID! - SMLVersionArchID: String! platform: String! link: String! } diff --git a/tests/announcements_test.go b/tests/announcements_test.go new file mode 100644 index 0000000..2dc8580 --- /dev/null +++ b/tests/announcements_test.go @@ -0,0 +1,122 @@ +package tests + +import ( + "testing" + + "github.com/MarvinJWendt/testza" + + "github.com/satisfactorymodding/smr-api/config" + "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/migrations" +) + +func init() { + migrations.SetMigrationDir("../migrations") + config.SetConfigDir("../") +} + +func TestAnnouncements(t *testing.T) { + ctx, client, stop := setup() + defer stop() + + token, err := makeUser(ctx) + testza.AssertNoError(t, err) + + // Create + createAnnouncement := authRequest(`mutation { + createAnnouncement(announcement: { + importance: Alert, + message: "Hello World" + }) { + id + } + }`, token) + + var createAnnouncementResponse struct { + CreateAnnouncement generated.Announcement + } + testza.AssertNoError(t, client.Run(ctx, createAnnouncement, &createAnnouncementResponse)) + testza.AssertNotEqual(t, "", createAnnouncementResponse.CreateAnnouncement.ID) + + // Query One + queryAnnouncement := authRequest(`query ($id: AnnouncementID!) { + getAnnouncement(announcementId: $id) { + id + message + importance + } + }`, token) + queryAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) + + var queryAnnouncementResponse struct { + GetAnnouncement generated.Announcement + } + testza.AssertNoError(t, client.Run(ctx, queryAnnouncement, &queryAnnouncementResponse)) + testza.AssertEqual(t, createAnnouncementResponse.CreateAnnouncement.ID, queryAnnouncementResponse.GetAnnouncement.ID) + testza.AssertEqual(t, "Hello World", queryAnnouncementResponse.GetAnnouncement.Message) + testza.AssertEqual(t, generated.AnnouncementImportanceAlert, queryAnnouncementResponse.GetAnnouncement.Importance) + + // Update + updateAnnouncement := authRequest(`mutation ($id: AnnouncementID!) { + updateAnnouncement( + announcementId: $id, + announcement: { + importance: Fix, + message: "Foo Bar" + } + ) { + id + } + }`, token) + updateAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) + + var updateAnnouncementResponse struct { + UpdateAnnouncement generated.Announcement + } + testza.AssertNoError(t, client.Run(ctx, updateAnnouncement, &updateAnnouncementResponse)) + + // Query Many + queryAnnouncements := authRequest(`query { + getAnnouncements { + id + message + importance + } + }`, token) + + var queryAnnouncementsResponse struct { + GetAnnouncements []generated.Announcement + } + testza.AssertNoError(t, client.Run(ctx, queryAnnouncements, &queryAnnouncementsResponse)) + testza.AssertEqual(t, 1, len(queryAnnouncementsResponse.GetAnnouncements)) + testza.AssertEqual(t, createAnnouncementResponse.CreateAnnouncement.ID, queryAnnouncementsResponse.GetAnnouncements[0].ID) + testza.AssertEqual(t, "Foo Bar", queryAnnouncementsResponse.GetAnnouncements[0].Message) + testza.AssertEqual(t, generated.AnnouncementImportanceFix, queryAnnouncementsResponse.GetAnnouncements[0].Importance) + + // Query By Importance + getAnnouncementsByImportance := authRequest(`query { + getAnnouncementsByImportance(importance: Info) { + id + message + importance + } + }`, token) + + var getAnnouncementsByImportanceResponse struct { + GetAnnouncements []generated.Announcement + } + testza.AssertNoError(t, client.Run(ctx, getAnnouncementsByImportance, &getAnnouncementsByImportanceResponse)) + testza.AssertEqual(t, 0, len(getAnnouncementsByImportanceResponse.GetAnnouncements)) + + // Delete + deleteAnnouncement := authRequest(`mutation ($id: AnnouncementID!) { + deleteAnnouncement(announcementId: $id) + }`, token) + deleteAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) + + var deleteAnnouncementResponse struct { + DeleteAnnouncement bool + } + testza.AssertNoError(t, client.Run(ctx, deleteAnnouncement, &deleteAnnouncementResponse)) + testza.AssertTrue(t, deleteAnnouncementResponse.DeleteAnnouncement) +} diff --git a/tests/utils.go b/tests/utils.go new file mode 100644 index 0000000..b4086bb --- /dev/null +++ b/tests/utils.go @@ -0,0 +1,109 @@ +package tests + +import ( + "context" + "sync" + + "github.com/machinebox/graphql" + "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api" + "github.com/satisfactorymodding/smr-api/auth" + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/util" +) + +func setup() (context.Context, *graphql.Client, func()) { + client := graphql.NewClient("http://localhost:5020/v2/query") + + ctx := smr.Initialize(context.Background()) + + var out []struct { + TableName string + } + + err := postgres.DBCtx(ctx).Raw(`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'`).Scan(&out).Error + if err != nil { + panic(err) + } + + for _, name := range out { + err := postgres.DBCtx(ctx).Exec(`DROP TABLE IF EXISTS ` + name.TableName + ` CASCADE`).Error + if err != nil { + panic(err) + } + } + + smr.Migrate(ctx) + smr.Setup(ctx) + go smr.Serve() + + stopChannel := make(chan bool) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + + <-stopChannel + if err := smr.Stop(); err != nil { + panic(err) + } + }() + + return context.Background(), client, func() { + stopChannel <- true + wg.Wait() + } +} + +func makeUser(ctx context.Context) (string, error) { + user := postgres.User{ + SMRModel: postgres.SMRModel{ + ID: util.GenerateUniqueID(), + }, + Email: "test_user@ficsit.app", + Username: "test_user", + } + + err := postgres.DBCtx(ctx).Create(&user).Error + if err != nil { + return "", err + } + + log.Info().Str("id", user.ID).Msg("created fake test_user") + + userGroup := postgres.UserGroup{ + UserID: user.ID, + GroupID: auth.GroupAdmin.ID, + } + + err = postgres.DBCtx(ctx).Create(&userGroup).Error + if err != nil { + return "", err + } + + log.Info().Msg("created user admin group") + + session := postgres.UserSession{ + SMRModel: postgres.SMRModel{ + ID: util.GenerateUniqueID(), + }, + User: user, + Token: util.GenerateUserToken(), + } + + err = postgres.DBCtx(ctx).Create(&session).Error + if err != nil { + return "", err + } + + log.Info().Str("token", session.Token).Msg("created fake user session") + + return session.Token, nil +} + +func authRequest(q string, token string) *graphql.Request { + req := graphql.NewRequest(q) + req.Header.Set("Authorization", token) + return req +} From 23fe4e134cf378c9ad5aa14479dedce8ca8b0ae1 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 19 Sep 2022 00:44:54 +0300 Subject: [PATCH 03/21] chore: add paseto key to workflow tests chore: start stack on workflow tests --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1d040fc..8730fdb 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,7 +76,12 @@ jobs: skip-build-cache: true args: --timeout 5m - - name: Build + - name: Start stack + run: docker-compose -f docker-compose-dev.yml up -d + + - name: Test run: go test -v ./... env: CGO_ENABLED: 1 + REPO_PASETO.PUBLIC_KEY: 408c5155a389aeabf1c1b0da73ff5a3079b6aa6628e4c661b1e1ce412181cc8a + REPO_PASETO.PRIVATE_KEY: a5f7409588f6b72d443db0d432f37f1214a5ec88cb55a70e24b90194ed549465408c5155a389aeabf1c1b0da73ff5a3079b6aa6628e4c661b1e1ce412181cc8a From 830a1549c32144d70ab5c7214169285f0ede77f4 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 19 Sep 2022 00:46:05 +0300 Subject: [PATCH 04/21] chore: remove linter from test workflow --- .github/workflows/build.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8730fdb..cb6d609 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,13 +69,6 @@ jobs: - name: Go Generate run: go generate -tags tools -x ./... - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - skip-pkg-cache: true - skip-build-cache: true - args: --timeout 5m - - name: Start stack run: docker-compose -f docker-compose-dev.yml up -d From 1db7dd82999f615b8daa29b694cfe85015164573 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 19 Sep 2022 00:53:43 +0300 Subject: [PATCH 05/21] chore: badges galore chore: add codecov --- .github/workflows/build.yml | 5 ++++- README.md | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb6d609..fe34539 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,8 +73,11 @@ jobs: run: docker-compose -f docker-compose-dev.yml up -d - name: Test - run: go test -v ./... + run: go test -v -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... ./... env: CGO_ENABLED: 1 REPO_PASETO.PUBLIC_KEY: 408c5155a389aeabf1c1b0da73ff5a3079b6aa6628e4c661b1e1ce412181cc8a REPO_PASETO.PRIVATE_KEY: a5f7409588f6b72d443db0d432f37f1214a5ec88cb55a70e24b90194ed549465408c5155a389aeabf1c1b0da73ff5a3079b6aa6628e4c661b1e1ce412181cc8a + + - name: Codecov + uses: codecov/codecov-action@v1 \ No newline at end of file diff --git a/README.md b/README.md index d5257f9..269a593 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SMR API [![build](https://github.com/satisfactorymodding/smr-api/actions/workflows/build.yml/badge.svg)](https://github.com/satisfactorymodding/smr-api/actions/workflows/build.yml) +# SMR API ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/satisfactorymodding/smr-api/build) ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/satisfactorymodding/smr-api) [![codecov](https://codecov.io/gh/satisfactorymodding/smr-api/branch/master/graph/badge.svg?token=LFNKYWS0N2)](https://codecov.io/gh/satisfactorymodding/smr-api) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/satisfactorymodding/smr-api) The Satisfactory Mod Repository backend API From 8084880cb004bc8b40d6a87c062622364276c78c Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 19 Sep 2022 08:08:10 +0300 Subject: [PATCH 06/21] fix: invalidate cache on delete --- db/postgres/postgres.go | 9 +- tests/announcements_test.go | 173 ++++++++++++++++++------------------ 2 files changed, 96 insertions(+), 86 deletions(-) diff --git a/db/postgres/postgres.go b/db/postgres/postgres.go index 9e6b4b9..7537d03 100644 --- a/db/postgres/postgres.go +++ b/db/postgres/postgres.go @@ -8,12 +8,13 @@ import ( "github.com/patrickmn/go-cache" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/satisfactorymodding/smr-api/db/postgres/otel" "github.com/spf13/viper" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" "gorm.io/gorm/utils" + + "github.com/satisfactorymodding/smr-api/db/postgres/otel" ) var db *gorm.DB @@ -108,10 +109,12 @@ func Save(ctx context.Context, object interface{}) { func Delete(ctx context.Context, object interface{}) { DBCtx(ctx).Delete(object) + ClearCache() } func DeleteForced(ctx context.Context, object interface{}) { DBCtx(ctx).Unscoped().Delete(object) + ClearCache() } func DBCtx(ctx context.Context) *gorm.DB { @@ -126,3 +129,7 @@ func DBCtx(ctx context.Context) *gorm.DB { return db } + +func ClearCache() { + dbCache.Flush() +} diff --git a/tests/announcements_test.go b/tests/announcements_test.go index 2dc8580..5919b72 100644 --- a/tests/announcements_test.go +++ b/tests/announcements_test.go @@ -22,101 +22,104 @@ func TestAnnouncements(t *testing.T) { token, err := makeUser(ctx) testza.AssertNoError(t, err) - // Create - createAnnouncement := authRequest(`mutation { - createAnnouncement(announcement: { - importance: Alert, - message: "Hello World" - }) { - id - } - }`, token) + // Run Twice to detect any cache issues + for i := 0; i < 2; i++ { + // Create + createAnnouncement := authRequest(`mutation { + createAnnouncement(announcement: { + importance: Alert, + message: "Hello World" + }) { + id + } + }`, token) - var createAnnouncementResponse struct { - CreateAnnouncement generated.Announcement - } - testza.AssertNoError(t, client.Run(ctx, createAnnouncement, &createAnnouncementResponse)) - testza.AssertNotEqual(t, "", createAnnouncementResponse.CreateAnnouncement.ID) - - // Query One - queryAnnouncement := authRequest(`query ($id: AnnouncementID!) { - getAnnouncement(announcementId: $id) { - id - message - importance + var createAnnouncementResponse struct { + CreateAnnouncement generated.Announcement } - }`, token) - queryAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) - - var queryAnnouncementResponse struct { - GetAnnouncement generated.Announcement - } - testza.AssertNoError(t, client.Run(ctx, queryAnnouncement, &queryAnnouncementResponse)) - testza.AssertEqual(t, createAnnouncementResponse.CreateAnnouncement.ID, queryAnnouncementResponse.GetAnnouncement.ID) - testza.AssertEqual(t, "Hello World", queryAnnouncementResponse.GetAnnouncement.Message) - testza.AssertEqual(t, generated.AnnouncementImportanceAlert, queryAnnouncementResponse.GetAnnouncement.Importance) - - // Update - updateAnnouncement := authRequest(`mutation ($id: AnnouncementID!) { - updateAnnouncement( - announcementId: $id, - announcement: { - importance: Fix, - message: "Foo Bar" + testza.AssertNoError(t, client.Run(ctx, createAnnouncement, &createAnnouncementResponse)) + testza.AssertNotEqual(t, "", createAnnouncementResponse.CreateAnnouncement.ID) + + // Query One + queryAnnouncement := authRequest(`query ($id: AnnouncementID!) { + getAnnouncement(announcementId: $id) { + id + message + importance } - ) { - id + }`, token) + queryAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) + + var queryAnnouncementResponse struct { + GetAnnouncement generated.Announcement } - }`, token) - updateAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) + testza.AssertNoError(t, client.Run(ctx, queryAnnouncement, &queryAnnouncementResponse)) + testza.AssertEqual(t, createAnnouncementResponse.CreateAnnouncement.ID, queryAnnouncementResponse.GetAnnouncement.ID) + testza.AssertEqual(t, "Hello World", queryAnnouncementResponse.GetAnnouncement.Message) + testza.AssertEqual(t, generated.AnnouncementImportanceAlert, queryAnnouncementResponse.GetAnnouncement.Importance) + + // Update + updateAnnouncement := authRequest(`mutation ($id: AnnouncementID!) { + updateAnnouncement( + announcementId: $id, + announcement: { + importance: Fix, + message: "Foo Bar" + } + ) { + id + } + }`, token) + updateAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) - var updateAnnouncementResponse struct { - UpdateAnnouncement generated.Announcement - } - testza.AssertNoError(t, client.Run(ctx, updateAnnouncement, &updateAnnouncementResponse)) - - // Query Many - queryAnnouncements := authRequest(`query { - getAnnouncements { - id - message - importance + var updateAnnouncementResponse struct { + UpdateAnnouncement generated.Announcement } - }`, token) + testza.AssertNoError(t, client.Run(ctx, updateAnnouncement, &updateAnnouncementResponse)) + + // Query Many + queryAnnouncements := authRequest(`query { + getAnnouncements { + id + message + importance + } + }`, token) - var queryAnnouncementsResponse struct { - GetAnnouncements []generated.Announcement - } - testza.AssertNoError(t, client.Run(ctx, queryAnnouncements, &queryAnnouncementsResponse)) - testza.AssertEqual(t, 1, len(queryAnnouncementsResponse.GetAnnouncements)) - testza.AssertEqual(t, createAnnouncementResponse.CreateAnnouncement.ID, queryAnnouncementsResponse.GetAnnouncements[0].ID) - testza.AssertEqual(t, "Foo Bar", queryAnnouncementsResponse.GetAnnouncements[0].Message) - testza.AssertEqual(t, generated.AnnouncementImportanceFix, queryAnnouncementsResponse.GetAnnouncements[0].Importance) - - // Query By Importance - getAnnouncementsByImportance := authRequest(`query { - getAnnouncementsByImportance(importance: Info) { - id - message - importance + var queryAnnouncementsResponse struct { + GetAnnouncements []generated.Announcement } - }`, token) + testza.AssertNoError(t, client.Run(ctx, queryAnnouncements, &queryAnnouncementsResponse)) + testza.AssertEqual(t, 1, len(queryAnnouncementsResponse.GetAnnouncements)) + testza.AssertEqual(t, createAnnouncementResponse.CreateAnnouncement.ID, queryAnnouncementsResponse.GetAnnouncements[0].ID) + testza.AssertEqual(t, "Foo Bar", queryAnnouncementsResponse.GetAnnouncements[0].Message) + testza.AssertEqual(t, generated.AnnouncementImportanceFix, queryAnnouncementsResponse.GetAnnouncements[0].Importance) + + // Query By Importance + getAnnouncementsByImportance := authRequest(`query { + getAnnouncementsByImportance(importance: Info) { + id + message + importance + } + }`, token) - var getAnnouncementsByImportanceResponse struct { - GetAnnouncements []generated.Announcement - } - testza.AssertNoError(t, client.Run(ctx, getAnnouncementsByImportance, &getAnnouncementsByImportanceResponse)) - testza.AssertEqual(t, 0, len(getAnnouncementsByImportanceResponse.GetAnnouncements)) + var getAnnouncementsByImportanceResponse struct { + GetAnnouncements []generated.Announcement + } + testza.AssertNoError(t, client.Run(ctx, getAnnouncementsByImportance, &getAnnouncementsByImportanceResponse)) + testza.AssertEqual(t, 0, len(getAnnouncementsByImportanceResponse.GetAnnouncements)) - // Delete - deleteAnnouncement := authRequest(`mutation ($id: AnnouncementID!) { - deleteAnnouncement(announcementId: $id) - }`, token) - deleteAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) + // Delete + deleteAnnouncement := authRequest(`mutation ($id: AnnouncementID!) { + deleteAnnouncement(announcementId: $id) + }`, token) + deleteAnnouncement.Var("id", createAnnouncementResponse.CreateAnnouncement.ID) - var deleteAnnouncementResponse struct { - DeleteAnnouncement bool + var deleteAnnouncementResponse struct { + DeleteAnnouncement bool + } + testza.AssertNoError(t, client.Run(ctx, deleteAnnouncement, &deleteAnnouncementResponse)) + testza.AssertTrue(t, deleteAnnouncementResponse.DeleteAnnouncement) } - testza.AssertNoError(t, client.Run(ctx, deleteAnnouncement, &deleteAnnouncementResponse)) - testza.AssertTrue(t, deleteAnnouncementResponse.DeleteAnnouncement) } From 7c482f451af607c557714537d748cc8944cabb2b Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 19 Sep 2022 08:49:43 +0300 Subject: [PATCH 07/21] chore: add bootstrap version tests --- db/postgres/postgres.go | 14 ++++ tests/announcements_test.go | 2 + tests/bootstrap_versions_test.go | 137 +++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 tests/bootstrap_versions_test.go diff --git a/db/postgres/postgres.go b/db/postgres/postgres.go index 7537d03..a81e563 100644 --- a/db/postgres/postgres.go +++ b/db/postgres/postgres.go @@ -70,6 +70,8 @@ func (l *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (stri } } +var debugEnabled = false + func InitializePostgres(ctx context.Context) { connection := postgres.Open(fmt.Sprintf( "sslmode=disable host=%s port=%d user=%s dbname=%s password=%s", @@ -96,6 +98,10 @@ func InitializePostgres(ctx context.Context) { db = dbInit + if debugEnabled { + db = db.Debug() + } + dbCache = cache.New(time.Second*5, time.Second*10) // TODO Create search indexes @@ -133,3 +139,11 @@ func DBCtx(ctx context.Context) *gorm.DB { func ClearCache() { dbCache.Flush() } + +func EnableDebug() { + if db != nil { + db = db.Debug() + } + + debugEnabled = true +} diff --git a/tests/announcements_test.go b/tests/announcements_test.go index 5919b72..262468b 100644 --- a/tests/announcements_test.go +++ b/tests/announcements_test.go @@ -6,6 +6,7 @@ import ( "github.com/MarvinJWendt/testza" "github.com/satisfactorymodding/smr-api/config" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/migrations" ) @@ -13,6 +14,7 @@ import ( func init() { migrations.SetMigrationDir("../migrations") config.SetConfigDir("../") + postgres.EnableDebug() } func TestAnnouncements(t *testing.T) { diff --git a/tests/bootstrap_versions_test.go b/tests/bootstrap_versions_test.go new file mode 100644 index 0000000..669ea6e --- /dev/null +++ b/tests/bootstrap_versions_test.go @@ -0,0 +1,137 @@ +package tests + +import ( + "strconv" + "testing" + + "github.com/MarvinJWendt/testza" + + "github.com/satisfactorymodding/smr-api/config" + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/migrations" +) + +func init() { + migrations.SetMigrationDir("../migrations") + config.SetConfigDir("../") + postgres.EnableDebug() +} + +func TestBootstrapVersions(t *testing.T) { + ctx, client, stop := setup() + defer stop() + + token, err := makeUser(ctx) + testza.AssertNoError(t, err) + + // Run Twice to detect any cache issues + for i := 0; i < 2; i++ { + version := strconv.Itoa(i+1) + ".0.0" + + // Create + createBootstrapVersion := authRequest(`mutation ($version: String!) { + createBootstrapVersion(bootstrapVersion: { + version: $version, + satisfactory_version: 12345, + stability: beta, + link: "example.com", + changelog: "Hello World", + date: "2006-01-02T15:04:05Z" + }) { + id + } + }`, token) + createBootstrapVersion.Var("version", version) + + var createBootstrapVersionResponse struct { + CreateBootstrapVersion generated.BootstrapVersion + } + testza.AssertNoError(t, client.Run(ctx, createBootstrapVersion, &createBootstrapVersionResponse)) + testza.AssertNotEqual(t, "", createBootstrapVersionResponse.CreateBootstrapVersion.ID) + + // Query One + queryBootstrapVersion := authRequest(`query ($id: BootstrapVersionID!) { + getBootstrapVersion(bootstrapVersionID: $id) { + id + version + satisfactory_version + stability + link + changelog + date + } + }`, token) + queryBootstrapVersion.Var("id", createBootstrapVersionResponse.CreateBootstrapVersion.ID) + + var queryBootstrapVersionResponse struct { + GetBootstrapVersion generated.BootstrapVersion + } + testza.AssertNoError(t, client.Run(ctx, queryBootstrapVersion, &queryBootstrapVersionResponse)) + testza.AssertEqual(t, createBootstrapVersionResponse.CreateBootstrapVersion.ID, queryBootstrapVersionResponse.GetBootstrapVersion.ID) + testza.AssertEqual(t, version, queryBootstrapVersionResponse.GetBootstrapVersion.Version) + testza.AssertEqual(t, 12345, queryBootstrapVersionResponse.GetBootstrapVersion.SatisfactoryVersion) + testza.AssertEqual(t, generated.VersionStabilitiesBeta, queryBootstrapVersionResponse.GetBootstrapVersion.Stability) + testza.AssertEqual(t, "example.com", queryBootstrapVersionResponse.GetBootstrapVersion.Link) + testza.AssertEqual(t, "Hello World", queryBootstrapVersionResponse.GetBootstrapVersion.Changelog) + + // Update + updateBootstrapVersion := authRequest(`mutation ($id: BootstrapVersionID!) { + updateBootstrapVersion( + bootstrapVersionId: $id, + bootstrapVersion: { + changelog: "Foo Bar", + } + ) { + id + } + }`, token) + updateBootstrapVersion.Var("id", createBootstrapVersionResponse.CreateBootstrapVersion.ID) + + var updateBootstrapVersionResponse struct { + UpdateBootstrapVersion generated.BootstrapVersion + } + testza.AssertNoError(t, client.Run(ctx, updateBootstrapVersion, &updateBootstrapVersionResponse)) + + // Query Many + queryBootstrapVersions := authRequest(`query { + getBootstrapVersions { + count + bootstrap_versions { + id + version + satisfactory_version + stability + link + changelog + date + } + } + }`, token) + + var queryBootstrapVersionsResponse struct { + GetBootstrapVersions generated.GetBootstrapVersions + } + testza.AssertNoError(t, client.Run(ctx, queryBootstrapVersions, &queryBootstrapVersionsResponse)) + testza.AssertEqual(t, 1, queryBootstrapVersionsResponse.GetBootstrapVersions.Count) + testza.AssertEqual(t, 1, len(queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions)) + testza.AssertEqual(t, createBootstrapVersionResponse.CreateBootstrapVersion.ID, queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions[0].ID) + testza.AssertEqual(t, version, queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions[0].Version) + testza.AssertEqual(t, 12345, queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions[0].SatisfactoryVersion) + testza.AssertEqual(t, generated.VersionStabilitiesBeta, queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions[0].Stability) + testza.AssertEqual(t, "example.com", queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions[0].Link) + testza.AssertEqual(t, "Foo Bar", queryBootstrapVersionsResponse.GetBootstrapVersions.BootstrapVersions[0].Changelog) + + // Delete + deleteBootstrapVersion := authRequest(`mutation ($id: BootstrapVersionID!) { + deleteBootstrapVersion(bootstrapVersionId: $id) + }`, token) + deleteBootstrapVersion.Var("id", createBootstrapVersionResponse.CreateBootstrapVersion.ID) + + var deleteBootstrapVersionResponse struct { + DeleteBootstrapVersion bool + } + testza.AssertNoError(t, client.Run(ctx, deleteBootstrapVersion, &deleteBootstrapVersionResponse)) + testza.AssertTrue(t, deleteBootstrapVersionResponse.DeleteBootstrapVersion) + } +} From 4ca8f47222778d47b8d347e4192400c8f4746037 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 20 Sep 2022 00:51:14 +0300 Subject: [PATCH 08/21] chore: add guide tests --- redis/redis.go | 4 + tests/announcements_test.go | 2 +- tests/bootstrap_versions_test.go | 2 +- tests/guides_test.go | 128 +++++++++++++++++++++++++++++++ tests/utils.go | 13 ++-- 5 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 tests/guides_test.go diff --git a/redis/redis.go b/redis/redis.go index 4a9e7df..052facc 100644 --- a/redis/redis.go +++ b/redis/redis.go @@ -137,3 +137,7 @@ func GetVersionUploadState(versionID string) (*generated.CreateVersionResponse, return data.Data, nil } + +func FlushRedis() { + client.FlushDB() +} diff --git a/tests/announcements_test.go b/tests/announcements_test.go index 262468b..7a7d30e 100644 --- a/tests/announcements_test.go +++ b/tests/announcements_test.go @@ -21,7 +21,7 @@ func TestAnnouncements(t *testing.T) { ctx, client, stop := setup() defer stop() - token, err := makeUser(ctx) + token, _, err := makeUser(ctx) testza.AssertNoError(t, err) // Run Twice to detect any cache issues diff --git a/tests/bootstrap_versions_test.go b/tests/bootstrap_versions_test.go index 669ea6e..8c56581 100644 --- a/tests/bootstrap_versions_test.go +++ b/tests/bootstrap_versions_test.go @@ -22,7 +22,7 @@ func TestBootstrapVersions(t *testing.T) { ctx, client, stop := setup() defer stop() - token, err := makeUser(ctx) + token, _, err := makeUser(ctx) testza.AssertNoError(t, err) // Run Twice to detect any cache issues diff --git a/tests/guides_test.go b/tests/guides_test.go new file mode 100644 index 0000000..ed90d5f --- /dev/null +++ b/tests/guides_test.go @@ -0,0 +1,128 @@ +package tests + +import ( + "testing" + + "github.com/MarvinJWendt/testza" + + "github.com/satisfactorymodding/smr-api/config" + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/migrations" +) + +func init() { + migrations.SetMigrationDir("../migrations") + config.SetConfigDir("../") + postgres.EnableDebug() +} + +func TestGuides(t *testing.T) { + ctx, client, stop := setup() + defer stop() + + token, userId, err := makeUser(ctx) + testza.AssertNoError(t, err) + + // Run Twice to detect any cache issues + for i := 0; i < 2; i++ { + // Create + createGuide := authRequest(`mutation { + createGuide(guide: { + name: "Hello World", + short_description: "Short description about the guide", + guide: "The full guide text goes here." + }) { + id + } + }`, token) + + var createGuideResponse struct { + CreateGuide generated.Guide + } + testza.AssertNoError(t, client.Run(ctx, createGuide, &createGuideResponse)) + testza.AssertNotEqual(t, "", createGuideResponse.CreateGuide.ID) + + // Query One + queryGuide := authRequest(`query ($id: GuideID!) { + getGuide(guideId: $id) { + id + name + short_description + guide + user { + id + } + } + }`, token) + queryGuide.Var("id", createGuideResponse.CreateGuide.ID) + + var queryGuideResponse struct { + GetGuide generated.Guide + } + testza.AssertNoError(t, client.Run(ctx, queryGuide, &queryGuideResponse)) + testza.AssertEqual(t, createGuideResponse.CreateGuide.ID, queryGuideResponse.GetGuide.ID) + testza.AssertEqual(t, "Hello World", queryGuideResponse.GetGuide.Name) + testza.AssertEqual(t, "Short description about the guide", queryGuideResponse.GetGuide.ShortDescription) + testza.AssertEqual(t, "The full guide text goes here.", queryGuideResponse.GetGuide.Guide) + testza.AssertEqual(t, userId, queryGuideResponse.GetGuide.User.ID) + + // Update + updateGuide := authRequest(`mutation ($id: GuideID!) { + updateGuide( + guideId: $id, + guide: { + name: "Foo Bar" + } + ) { + id + } + }`, token) + updateGuide.Var("id", createGuideResponse.CreateGuide.ID) + + var updateGuideResponse struct { + UpdateGuide generated.Guide + } + testza.AssertNoError(t, client.Run(ctx, updateGuide, &updateGuideResponse)) + + // Query Many + queryGuides := authRequest(`query { + getGuides { + count + guides { + id + name + short_description + guide + user { + id + } + } + } + }`, token) + + var queryGuidesResponse struct { + GetGuides generated.GetGuides + } + testza.AssertNoError(t, client.Run(ctx, queryGuides, &queryGuidesResponse)) + testza.AssertEqual(t, 1, queryGuidesResponse.GetGuides.Count) + testza.AssertEqual(t, 1, len(queryGuidesResponse.GetGuides.Guides)) + testza.AssertEqual(t, createGuideResponse.CreateGuide.ID, queryGuidesResponse.GetGuides.Guides[0].ID) + testza.AssertEqual(t, "Foo Bar", queryGuidesResponse.GetGuides.Guides[0].Name) + testza.AssertEqual(t, "Short description about the guide", queryGuidesResponse.GetGuides.Guides[0].ShortDescription) + testza.AssertEqual(t, "The full guide text goes here.", queryGuidesResponse.GetGuides.Guides[0].Guide) + testza.AssertEqual(t, userId, queryGuidesResponse.GetGuides.Guides[0].User.ID) + + // Delete + deleteGuide := authRequest(`mutation ($id: GuideID!) { + deleteGuide(guideId: $id) + }`, token) + deleteGuide.Var("id", createGuideResponse.CreateGuide.ID) + + var deleteGuideResponse struct { + DeleteGuide bool + } + testza.AssertNoError(t, client.Run(ctx, deleteGuide, &deleteGuideResponse)) + testza.AssertTrue(t, deleteGuideResponse.DeleteGuide) + } +} diff --git a/tests/utils.go b/tests/utils.go index b4086bb..6e87a95 100644 --- a/tests/utils.go +++ b/tests/utils.go @@ -10,6 +10,7 @@ import ( "github.com/satisfactorymodding/smr-api" "github.com/satisfactorymodding/smr-api/auth" "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/redis" "github.com/satisfactorymodding/smr-api/util" ) @@ -18,6 +19,8 @@ func setup() (context.Context, *graphql.Client, func()) { ctx := smr.Initialize(context.Background()) + redis.FlushRedis() + var out []struct { TableName string } @@ -56,7 +59,7 @@ func setup() (context.Context, *graphql.Client, func()) { } } -func makeUser(ctx context.Context) (string, error) { +func makeUser(ctx context.Context) (string, string, error) { user := postgres.User{ SMRModel: postgres.SMRModel{ ID: util.GenerateUniqueID(), @@ -67,7 +70,7 @@ func makeUser(ctx context.Context) (string, error) { err := postgres.DBCtx(ctx).Create(&user).Error if err != nil { - return "", err + return "", "", err } log.Info().Str("id", user.ID).Msg("created fake test_user") @@ -79,7 +82,7 @@ func makeUser(ctx context.Context) (string, error) { err = postgres.DBCtx(ctx).Create(&userGroup).Error if err != nil { - return "", err + return "", "", err } log.Info().Msg("created user admin group") @@ -94,12 +97,12 @@ func makeUser(ctx context.Context) (string, error) { err = postgres.DBCtx(ctx).Create(&session).Error if err != nil { - return "", err + return "", "", err } log.Info().Str("token", session.Token).Msg("created fake user session") - return session.Token, nil + return session.Token, user.ID, nil } func authRequest(q string, token string) *graphql.Request { From 8b859ae3f73543ba9432012e538b8c2d9f5c48cb Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 20 Sep 2022 00:59:53 +0300 Subject: [PATCH 09/21] chore: make linter happy --- tests/guides_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/guides_test.go b/tests/guides_test.go index ed90d5f..5faa88b 100644 --- a/tests/guides_test.go +++ b/tests/guides_test.go @@ -21,7 +21,7 @@ func TestGuides(t *testing.T) { ctx, client, stop := setup() defer stop() - token, userId, err := makeUser(ctx) + token, userID, err := makeUser(ctx) testza.AssertNoError(t, err) // Run Twice to detect any cache issues @@ -65,7 +65,7 @@ func TestGuides(t *testing.T) { testza.AssertEqual(t, "Hello World", queryGuideResponse.GetGuide.Name) testza.AssertEqual(t, "Short description about the guide", queryGuideResponse.GetGuide.ShortDescription) testza.AssertEqual(t, "The full guide text goes here.", queryGuideResponse.GetGuide.Guide) - testza.AssertEqual(t, userId, queryGuideResponse.GetGuide.User.ID) + testza.AssertEqual(t, userID, queryGuideResponse.GetGuide.User.ID) // Update updateGuide := authRequest(`mutation ($id: GuideID!) { @@ -111,7 +111,7 @@ func TestGuides(t *testing.T) { testza.AssertEqual(t, "Foo Bar", queryGuidesResponse.GetGuides.Guides[0].Name) testza.AssertEqual(t, "Short description about the guide", queryGuidesResponse.GetGuides.Guides[0].ShortDescription) testza.AssertEqual(t, "The full guide text goes here.", queryGuidesResponse.GetGuides.Guides[0].Guide) - testza.AssertEqual(t, userId, queryGuidesResponse.GetGuides.Guides[0].User.ID) + testza.AssertEqual(t, userID, queryGuidesResponse.GetGuides.Guides[0].User.ID) // Delete deleteGuide := authRequest(`mutation ($id: GuideID!) { From f91db5387480f6199f4575383f930070a6f84dd1 Mon Sep 17 00:00:00 2001 From: porisius <47161774+porisius@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:28:11 -0400 Subject: [PATCH 10/21] fix: Admin NewSMLVersion unknown Arch field error (#18) * fix: fixed NewSMLVersion arch issue * cleanup smlLink to smlArch * Improvements in UpdateSMLArch * chore: make linter happy * fix: conversion to SMLVersion and ModVersion * chore: standardize case (Mod) * chore: standardize case (SML) * chore: remove SMLVersionID --- db/postgres/mod.go | 2 +- db/postgres/mod_archs.go | 4 +- db/postgres/postgres_types.go | 24 ++++---- db/postgres/sml_archs.go | 20 +++++-- db/postgres/sml_version.go | 8 +-- gql/gql_types.go | 26 ++++----- gql/resolver_mod_archs.go | 2 +- gql/resolver_sml_archs.go | 31 ++++++---- gql/resolver_sml_versions.go | 57 ++++++++++++++----- migrations/sql/000021_add_mod_platform.up.sql | 8 +-- schemas/mod_archs.graphql | 2 +- schemas/sml_archs.graphql | 4 +- storage/storage.go | 10 ++-- 13 files changed, 119 insertions(+), 79 deletions(-) diff --git a/db/postgres/mod.go b/db/postgres/mod.go index 0e9eaab..9482b46 100644 --- a/db/postgres/mod.go +++ b/db/postgres/mod.go @@ -270,7 +270,7 @@ func GetModByIDOrReference(ctx context.Context, modIDOrReference string) *Mod { } var mod Mod - DBCtx(ctx).Preload("Tags").Find(&mod, "mod_reference = ? OR id = ?", modIDOrReference, modIDOrReference) + DBCtx(ctx).Preload("Tags").Preload("Versions.Arch").Find(&mod, "mod_reference = ? OR id = ?", modIDOrReference, modIDOrReference) if mod.ID == "" { return nil diff --git a/db/postgres/mod_archs.go b/db/postgres/mod_archs.go index 5936116..1e395a2 100644 --- a/db/postgres/mod_archs.go +++ b/db/postgres/mod_archs.go @@ -101,7 +101,7 @@ func GetModArchByPlatform(ctx context.Context, versionID string, platform string var modplatform ModArch DBCtx(ctx).First(&modplatform, "mod_version_arch_id = ? AND platform = ?", versionID, platform) - if modplatform.ModVersionArchID == "" { + if modplatform.ModVersionID == "" { return nil } @@ -114,7 +114,7 @@ func GetModArchDownload(ctx context.Context, versionID string, platform string) var modPlatform ModArch DBCtx(ctx).First(&modPlatform, "mod_version_arch_id = ? AND platform = ?", versionID, platform) - if modPlatform.ModVersionArchID == "" { + if modPlatform.ModVersionID == "" { return "" } diff --git a/db/postgres/postgres_types.go b/db/postgres/postgres_types.go index ff6ada0..1a9994a 100644 --- a/db/postgres/postgres_types.go +++ b/db/postgres/postgres_types.go @@ -95,7 +95,7 @@ type Version struct { Approved bool `gorm:"default:false;not null"` Denied bool `gorm:"default:false;not null"` Hotness uint - Arch []ModArch `gorm:"foreignKey:mod_version_arch_id;preload:true"` + Arch []ModArch `gorm:"foreignKey:ModVersionID;preload:true"` Metadata *string ModReference *string VersionMajor *int @@ -133,7 +133,7 @@ type SMLVersion struct { Stability string `sql:"type:version_stability"` Date time.Time Link string - Arch []SMLArch `gorm:"foreignKey:sml_version_arch_id;preload:true"` + Arch []SMLArch `gorm:"foreignKey:SMLVersionID;preload:true"` Changelog string BootstrapVersion *string } @@ -195,12 +195,12 @@ type Compatibility struct { } type ModArch struct { - ID string `gorm:"primary_key;type:varchar(16)"` - ModVersionArchID string - Platform string - Key string - Size int64 - Hash string + ID string `gorm:"primary_key;type:varchar(16)"` + ModVersionID string `gorm:"column:mod_version_arch_id"` + Platform string + Key string + Size int64 + Hash string } func (ModArch) TableName() string { @@ -208,10 +208,10 @@ func (ModArch) TableName() string { } type SMLArch struct { - ID string `gorm:"primary_key;type:varchar(14)"` - SMLVersionArchID string - Platform string - Link string + ID string `gorm:"primary_key;type:varchar(14)"` + SMLVersionID string `gorm:"column:sml_version_arch_id"` + Platform string + Link string } func (SMLArch) TableName() string { diff --git a/db/postgres/sml_archs.go b/db/postgres/sml_archs.go index 876b8d7..571b7db 100644 --- a/db/postgres/sml_archs.go +++ b/db/postgres/sml_archs.go @@ -10,19 +10,19 @@ import ( "github.com/satisfactorymodding/smr-api/util" ) -func CreateSMLArch(ctx context.Context, smlLink *SMLArch) (*SMLArch, error) { - smlLink.ID = util.GenerateUniqueID() +func CreateSMLArch(ctx context.Context, smlArch *SMLArch) (*SMLArch, error) { + smlArch.ID = util.GenerateUniqueID() - DBCtx(ctx).Create(&smlLink) + DBCtx(ctx).Create(&smlArch) - return smlLink, nil + return smlArch, nil } func GetSMLArch(ctx context.Context, smlLinkID string) *SMLArch { cacheKey := "GetSMLArch_" + smlLinkID - if smlLink, ok := dbCache.Get(cacheKey); ok { - return smlLink.(*SMLArch) + if smlArch, ok := dbCache.Get(cacheKey); ok { + return smlArch.(*SMLArch) } var smlArch SMLArch @@ -79,6 +79,14 @@ func GetSMLArchsByID(ctx context.Context, smlArchIds []string) []SMLArch { return smlArchs } +func GetSMLArchBySMLID(ctx context.Context, smlVersionID string) []SMLArch { + var smlArchs []SMLArch + + DBCtx(ctx).Find(&smlArchs, "sml_version_arch_id = ?", smlVersionID) + + return smlArchs +} + func GetSMLArchDownload(ctx context.Context, smlVersionID string, platform string) string { var smlPlatform SMLArch DBCtx(ctx).First(&smlPlatform, "sml_version_arch_id = ? AND platform = ?", smlVersionID, platform) diff --git a/db/postgres/sml_version.go b/db/postgres/sml_version.go index 8e5a890..0eabc79 100644 --- a/db/postgres/sml_version.go +++ b/db/postgres/sml_version.go @@ -15,10 +15,10 @@ func CreateSMLVersion(ctx context.Context, smlVersion *SMLVersion) (*SMLVersion, for _, link := range smlVersion.Arch { DBCtx(ctx).Create(&SMLArch{ - ID: util.GenerateUniqueID(), - SMLVersionArchID: smlVersion.ID, - Platform: link.Platform, - Link: link.Link, + ID: util.GenerateUniqueID(), + SMLVersionID: smlVersion.ID, + Platform: link.Platform, + Link: link.Link, }) } diff --git a/gql/gql_types.go b/gql/gql_types.go index 23c0dbd..5c7e948 100644 --- a/gql/gql_types.go +++ b/gql/gql_types.go @@ -219,11 +219,11 @@ func DBModArchToGenerated(modArch *postgres.ModArch) *generated.ModArch { size := int(modArch.Size) return &generated.ModArch{ - ID: modArch.ID, - ModVersionArchID: modArch.ModVersionArchID, - Platform: modArch.Platform, - Hash: &modArch.Hash, - Size: &size, + ID: modArch.ID, + ModVersionID: modArch.ModVersionID, + Platform: modArch.Platform, + Hash: &modArch.Hash, + Size: &size, } } @@ -235,23 +235,23 @@ func DBModArchsToGeneratedSlice(modArchs []postgres.ModArch) []*generated.ModArc return converted } -func DBSMLArchToGenerated(smlLink *postgres.SMLArch) *generated.SMLArch { - if smlLink == nil { +func DBSMLArchToGenerated(smlArch *postgres.SMLArch) *generated.SMLArch { + if smlArch == nil { return nil } return &generated.SMLArch{ - ID: smlLink.ID, - SMLVersionArchID: smlLink.SMLVersionArchID, - Platform: smlLink.Platform, - Link: smlLink.Link, + ID: smlArch.ID, + SMLVersionID: smlArch.SMLVersionID, + Platform: smlArch.Platform, + Link: smlArch.Link, } } func DBSMLArchsToGeneratedSlice(smlLinks []postgres.SMLArch) []*generated.SMLArch { converted := make([]*generated.SMLArch, len(smlLinks)) - for i, smlLink := range smlLinks { - converted[i] = DBSMLArchToGenerated(&smlLink) + for i, smlArch := range smlLinks { + converted[i] = DBSMLArchToGenerated(&smlArch) } return converted } diff --git a/gql/resolver_mod_archs.go b/gql/resolver_mod_archs.go index 02b1686..0181328 100644 --- a/gql/resolver_mod_archs.go +++ b/gql/resolver_mod_archs.go @@ -10,7 +10,7 @@ import ( type modlinkResolver struct{ *Resolver } func (r *modlinkResolver) Asset(_ context.Context, obj *generated.ModArch) (string, error) { - return "/v1/version/" + obj.ModVersionArchID + "/" + obj.Platform + "/download", nil + return "/v1/version/" + obj.ModVersionID + "/" + obj.Platform + "/download", nil } func (r *queryResolver) GetModArch(ctx context.Context, modArchID string) (*generated.ModArch, error) { diff --git a/gql/resolver_sml_archs.go b/gql/resolver_sml_archs.go index ca1fc93..b91ef69 100644 --- a/gql/resolver_sml_archs.go +++ b/gql/resolver_sml_archs.go @@ -12,19 +12,19 @@ import ( "github.com/satisfactorymodding/smr-api/generated" ) -func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlLink generated.NewSMLArch) (*generated.SMLArch, error) { +func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlArch generated.NewSMLArch) (*generated.SMLArch, error) { wrapper, newCtx := WrapMutationTrace(ctx, "createSMLArch") defer wrapper.end() val := ctx.Value(util.ContextValidator{}).(*validator.Validate) - if err := val.Struct(&smlLink); err != nil { + if err := val.Struct(&smlArch); err != nil { return nil, errors.Wrap(err, "validation failed") } dbSMLArchs := &postgres.SMLArch{ ID: util.GenerateUniqueID(), - Platform: smlLink.Platform, - Link: smlLink.Link, + Platform: smlArch.Platform, + Link: smlArch.Link, } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) @@ -51,11 +51,11 @@ func (r *mutationResolver) DeleteSMLArch(ctx context.Context, linksID string) (b return true, nil } -func (r *mutationResolver) UpdateSMLArch(ctx context.Context, smlLinkID string, smlLink generated.UpdateSMLArch) (*generated.SMLArch, error) { +func (r *mutationResolver) UpdateSMLArch(ctx context.Context, smlLinkID string, smlArch generated.UpdateSMLArch) (*generated.SMLArch, error) { wrapper, newCtx := WrapMutationTrace(ctx, "updateSMLArch") defer wrapper.end() val := ctx.Value(util.ContextValidator{}).(*validator.Validate) - if err := val.Struct(&smlLink); err != nil { + if err := val.Struct(&smlArch); err != nil { return nil, errors.Wrap(err, "validation failed") } @@ -65,8 +65,8 @@ func (r *mutationResolver) UpdateSMLArch(ctx context.Context, smlLinkID string, return nil, errors.New("sml link not found") } - SetStringINNOE(&smlLink.Platform, &dbSMLArch.Platform) - SetStringINNOE(&smlLink.Link, &dbSMLArch.Link) + SetStringINNOE(&smlArch.Platform, &dbSMLArch.Platform) + SetStringINNOE(&smlArch.Link, &dbSMLArch.Link) postgres.Save(newCtx, &dbSMLArch) @@ -77,9 +77,9 @@ func (r *queryResolver) GetSMLArch(ctx context.Context, smlLinkID string) (*gene wrapper, newCtx := WrapQueryTrace(ctx, "getSMLArch") defer wrapper.end() - smlLink := postgres.GetSMLArch(newCtx, smlLinkID) + smlArch := postgres.GetSMLArch(newCtx, smlLinkID) - return DBSMLArchToGenerated(smlLink), nil + return DBSMLArchToGenerated(smlArch), nil } func (r *queryResolver) GetSMLArchs(ctx context.Context, filter map[string]interface{}) (*generated.GetSMLArchs, error) { @@ -88,9 +88,16 @@ func (r *queryResolver) GetSMLArchs(ctx context.Context, filter map[string]inter return &generated.GetSMLArchs{}, nil } +func (r *queryResolver) GetSMLArchBySMLID(ctx context.Context, smlVersionID string) ([]postgres.SMLArch, error) { + wrapper, newCtx := WrapQueryTrace(ctx, "GetSMLArchBySMLID") + defer wrapper.end() + smlArch := postgres.GetSMLArchBySMLID(newCtx, smlVersionID) + return smlArch, nil +} + func (r *queryResolver) GetSMLDownload(ctx context.Context, smlVersionID string, platform string) (string, error) { wrapper, newCtx := WrapQueryTrace(ctx, "getSMLDownload") defer wrapper.end() - smlLink := postgres.GetSMLArchDownload(newCtx, smlVersionID, platform) - return smlLink, nil + smlArch := postgres.GetSMLArchDownload(newCtx, smlVersionID, platform) + return smlArch, nil } diff --git a/gql/resolver_sml_versions.go b/gql/resolver_sml_versions.go index bfe1a4b..a329900 100644 --- a/gql/resolver_sml_versions.go +++ b/gql/resolver_sml_versions.go @@ -42,12 +42,12 @@ func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion gene resultSMLVersion, err := postgres.CreateSMLVersion(newCtx, dbSMLVersion) - for _, smlLink := range smlVersion.Arch { + for _, smlArch := range smlVersion.Arch { dbSMLArchs := &postgres.SMLArch{ - ID: util.GenerateUniqueID(), - SMLVersionArchID: resultSMLVersion.ID, - Platform: smlLink.Platform, - Link: smlLink.Link, + ID: util.GenerateUniqueID(), + SMLVersionID: resultSMLVersion.ID, + Platform: smlArch.Platform, + Link: smlArch.Link, } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) @@ -89,15 +89,42 @@ func (r *mutationResolver) UpdateSMLVersion(ctx context.Context, smlVersionID st SetStringINNOE(smlVersion.Changelog, &dbSMLVersion.Changelog) SetDateINN(smlVersion.Date, &dbSMLVersion.Date) - for _, smlLink := range smlVersion.Arch { - dbSMLArch := postgres.GetSMLArch(newCtx, smlLink.ID) + dbSMLArch := postgres.GetSMLArchBySMLID(newCtx, smlVersionID) - SetStringINNOE(&smlLink.ID, &dbSMLArch.ID) - SetStringINNOE(&smlLink.SMLVersionArchID, &dbSMLArch.SMLVersionArchID) - SetStringINNOE(&smlLink.Platform, &dbSMLArch.Platform) - SetStringINNOE(&smlLink.Link, &dbSMLArch.Link) + if len(dbSMLArch) == len(smlVersion.Arch) { + for i, smlArch := range smlVersion.Arch { + SetStringINNOE(&smlArch.Platform, &dbSMLArch[i].Platform) + SetStringINNOE(&smlArch.Link, &dbSMLArch[i].Link) - postgres.Save(newCtx, dbSMLArch) + postgres.Save(newCtx, dbSMLArch) + } + } else { + for _, smlArch := range dbSMLVersion.Arch { + dbSMLArch := postgres.GetSMLArchBySMLID(newCtx, smlVersionID) + + if dbSMLVersion == nil { + return nil, errors.New("smlArch not found" + smlArch.Platform) + } + + postgres.Delete(newCtx, &dbSMLArch) + } + + for _, smlArch := range smlVersion.Arch { + dbSMLArch := &postgres.SMLArch{ + ID: util.GenerateUniqueID(), + SMLVersionID: smlVersionID, + Platform: smlArch.Platform, + Link: smlArch.Link, + } + + resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArch) + + if err != nil { + return nil, err + } + + DBSMLArchToGenerated(resultSMLArch) + } } postgres.Save(newCtx, &dbSMLVersion) @@ -115,11 +142,11 @@ func (r *mutationResolver) DeleteSMLVersion(ctx context.Context, smlVersionID st return false, errors.New("smlVersion not found") } - for _, smlLink := range dbSMLVersion.Arch { - dbSMLArch := postgres.GetSMLArch(newCtx, smlLink.ID) + for _, smlArch := range dbSMLVersion.Arch { + dbSMLArch := postgres.GetSMLArch(newCtx, smlArch.ID) if dbSMLVersion == nil { - return false, errors.New("smlLink not found") + return false, errors.New("smlArch not found") } postgres.Delete(newCtx, &dbSMLArch) diff --git a/migrations/sql/000021_add_mod_platform.up.sql b/migrations/sql/000021_add_mod_platform.up.sql index 90e544d..7b2aad2 100644 --- a/migrations/sql/000021_add_mod_platform.up.sql +++ b/migrations/sql/000021_add_mod_platform.up.sql @@ -13,9 +13,9 @@ create index if not exists idx_mod_arch_id on mod_archs (mod_version_arch_id, pl -- SML Links create table if not exists sml_archs ( - id varchar(14) not null constraint sml_archs_pkey primary key, - sml_version_arch_id varchar(14), - platform varchar(16), - link text + id varchar(14) not null constraint sml_archs_pkey primary key, + sml_version_arch_id varchar(14), + platform varchar(16), + link text ); create index if not exists idx_sml_archs_id on sml_archs (sml_version_arch_id, platform); \ No newline at end of file diff --git a/schemas/mod_archs.graphql b/schemas/mod_archs.graphql index cded1f6..648fc18 100644 --- a/schemas/mod_archs.graphql +++ b/schemas/mod_archs.graphql @@ -4,7 +4,7 @@ scalar ModArchID type ModArch { id: ModArchID! - ModVersionArchID: String! + ModVersionID: String! platform: String! asset: String! size: Int diff --git a/schemas/sml_archs.graphql b/schemas/sml_archs.graphql index d020a24..e3cdb32 100644 --- a/schemas/sml_archs.graphql +++ b/schemas/sml_archs.graphql @@ -4,7 +4,7 @@ scalar SMLArchID type SMLArch { id: SMLArchID! - SMLVersionArchID: String! + SMLVersionID: String! platform: String! link: String! } @@ -34,8 +34,6 @@ input NewSMLArch { } input UpdateSMLArch { - id: SMLArchID! - SMLVersionArchID: String! platform: String! link: String! } diff --git a/storage/storage.go b/storage/storage.go index 8cf03a7..ea4acdd 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -487,11 +487,11 @@ func WriteModArch(ctx context.Context, key string, versionID string, platform st hash.Write(buffer.Bytes()) dbModArch := &postgres.ModArch{ - ModVersionArchID: versionID, - Platform: platform, - Key: key, - Hash: hex.EncodeToString(hash.Sum(nil)), - Size: int64(len(buffer.Bytes())), + ModVersionID: versionID, + Platform: platform, + Key: key, + Hash: hex.EncodeToString(hash.Sum(nil)), + Size: int64(len(buffer.Bytes())), } _, err = postgres.CreateModArch(ctx, dbModArch) From 7b4f778b17863df99c29fa25061b07345d7fa7f3 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Fri, 14 Oct 2022 17:47:12 +0300 Subject: [PATCH 11/21] chore: add more linting rules fix: delete combined version after upload fix: do not panic if version is not found --- .golangci.yml | 36 +++++- api.go | 46 ++++---- auth/permissions.go | 6 +- cmd/paseto/main.go | 1 - cmd/validate-zip/main.go | 1 - config/config.go | 1 + dataloader/loaders.go | 4 +- dataloader/userloader_gen.go | 26 +--- dataloader/usermodloader_gen.go | 26 +--- dataloader/versiondependencyloader_gen.go | 26 +--- dataloader/versionloader_gen.go | 26 +--- dataloader/versionloadernometa_gen.go | 26 +--- db/postgres/bootstrap_version.go | 4 +- db/postgres/guide.go | 8 +- db/postgres/mod.go | 12 +- db/postgres/mod_archs.go | 2 +- db/postgres/otel/callbacks.go | 6 +- db/postgres/otel/config.go | 2 +- db/postgres/otel/plugin.go | 3 +- db/postgres/postgres.go | 6 +- db/postgres/postgres_types.go | 111 ++++++++---------- db/postgres/sml_archs.go | 2 +- db/postgres/sml_version.go | 4 +- db/postgres/tags.go | 8 +- db/postgres/version.go | 8 +- db/statistics.go | 18 ++- docker-compose-dev.yml | 2 +- generated/custom_models.go | 2 +- gql/directive.go | 34 +++--- gql/gql_types.go | 24 ++-- gql/gql_utils.go | 9 +- gql/resolver.go | 14 +++ gql/resolver_announcements.go | 2 +- gql/resolver_bootstrap_versions.go | 13 +- gql/resolver_guides.go | 12 +- gql/resolver_mods.go | 23 +--- gql/resolver_oauth.go | 12 +- gql/resolver_sml_archs.go | 4 +- gql/resolver_sml_versions.go | 14 +-- gql/resolver_tags.go | 3 +- gql/resolver_users.go | 11 +- gql/resolver_versions.go | 17 +-- gql/versions.go | 15 ++- integrations/discord.go | 6 +- migrations/code.go | 10 +- .../code/20200426221900_test_new_migration.go | 4 +- migrations/code/20200501224200_parse_paks.go | 4 +- .../20200524203800_after_reference_fix.go | 4 +- .../20200621195500_after_id_length_fix.go | 4 +- ...20200622003600_after_validation_disable.go | 4 +- .../code/20200629093800_copy_to_new_bucket.go | 6 +- .../20200707150700_after_sml_version_fix.go | 6 +- .../code/20200829171600_after_db_dirty.go | 4 +- .../20200829225100_after_broken_datajson.go | 6 +- .../code/20200830011200_after_gorm_hotfix.go | 4 +- .../code/20201014162200_after_bp_fix.go | 4 +- .../code/20201016202600_after_body_enable.go | 4 +- .../20201019203800_after_remove_filter.go | 4 +- migrations/migrations.go | 8 +- models/filters.go | 8 +- nodes/mod.go | 4 +- nodes/mod_types.go | 30 ++--- nodes/node_types.go | 4 +- nodes/oauth.go | 7 +- nodes/shared.go | 7 +- nodes/sml.go | 1 + nodes/user.go | 1 + nodes/user_types.go | 4 +- nodes/version.go | 1 + oauth/facebook.go | 7 +- oauth/github.go | 8 +- oauth/google.go | 9 +- oauth/oauth.go | 14 ++- .../consumer_copy_object_from_old_bucket.go | 7 +- .../consumer_copy_object_to_old_bucket.go | 7 +- .../consumer_scan_mod_on_virus_total.go | 14 +-- ...onsumer_update_db_from_mod_version_file.go | 5 +- ...er_update_db_from_mod_version_json_file.go | 5 +- redis/jobs/consumers/utils.go | 9 +- redis/jobs/jobs.go | 11 +- redis/jobs/tasks/tasks.go | 12 +- redis/redis.go | 7 +- storage/b2.go | 16 +-- storage/s3.go | 33 +++--- storage/storage.go | 19 ++- storage/wasabi.go | 8 +- util/analytics.go | 12 +- util/converter/converter_linux.go | 14 +-- util/image.go | 13 +- util/random.go | 6 +- util/security.go | 7 +- validation/paks.go | 7 +- validation/paks_test.go | 3 +- validation/validation.go | 36 +++--- validation/virustotal.go | 8 +- 95 files changed, 461 insertions(+), 615 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 5fd1ecd..37211ec 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -14,6 +14,32 @@ linters-settings: ignorePackageGlobs: - github.com/satisfactorymodding/smr-api/* + govet: + check-shadowing: true + enable-all: true + disable: + - shadow + + gocritic: + disabled-checks: + - ifElseChain + + gci: + custom-order: true + sections: + - standard + - default + - prefix(github.com/satisfactorymodding/smr-api) + - blank + - dot + +run: + skip-files: + - ./generated/generated.go + - ./generated/models_gen.go + skip-dirs: + - ./docs/ + issues: exclude: - should pass the context parameter @@ -21,16 +47,13 @@ issues: linters: disable-all: true enable: - - deadcode - errcheck - gosimple - govet - ineffassign - staticcheck - - structcheck - typecheck - unused - - varcheck - bidichk - contextcheck - durationcheck @@ -38,8 +61,11 @@ linters: - goconst - goimports - revive - - ifshort - misspell - prealloc - whitespace - - wrapcheck \ No newline at end of file + - wrapcheck + - gci + - gocritic + - gofumpt + - nonamedreturns diff --git a/api.go b/api.go index 181e5ba..d6745b4 100644 --- a/api.go +++ b/api.go @@ -9,30 +9,6 @@ import ( "syscall" "time" - "github.com/satisfactorymodding/smr-api/auth" - "github.com/satisfactorymodding/smr-api/config" - "github.com/satisfactorymodding/smr-api/dataloader" - "github.com/satisfactorymodding/smr-api/db" - "github.com/satisfactorymodding/smr-api/db/postgres" - - "github.com/pkg/errors" - - // Load REST docs - _ "github.com/satisfactorymodding/smr-api/docs" - "github.com/satisfactorymodding/smr-api/generated" - "github.com/satisfactorymodding/smr-api/gql" - "github.com/satisfactorymodding/smr-api/migrations" - "github.com/satisfactorymodding/smr-api/nodes" - "github.com/satisfactorymodding/smr-api/oauth" - "github.com/satisfactorymodding/smr-api/redis" - "github.com/satisfactorymodding/smr-api/redis/jobs" - - // Load redis consumers - _ "github.com/satisfactorymodding/smr-api/redis/jobs/consumers" - "github.com/satisfactorymodding/smr-api/storage" - "github.com/satisfactorymodding/smr-api/util" - "github.com/satisfactorymodding/smr-api/validation" - "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/extension" "github.com/99designs/gqlgen/graphql/handler/lru" @@ -42,6 +18,7 @@ import ( "github.com/labstack/echo-contrib/pprof" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" echoSwagger "github.com/swaggo/echo-swagger" @@ -54,6 +31,27 @@ import ( semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" "gopkg.in/go-playground/validator.v9" + + "github.com/satisfactorymodding/smr-api/auth" + "github.com/satisfactorymodding/smr-api/config" + "github.com/satisfactorymodding/smr-api/dataloader" + "github.com/satisfactorymodding/smr-api/db" + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/gql" + "github.com/satisfactorymodding/smr-api/migrations" + "github.com/satisfactorymodding/smr-api/nodes" + "github.com/satisfactorymodding/smr-api/oauth" + "github.com/satisfactorymodding/smr-api/redis" + "github.com/satisfactorymodding/smr-api/redis/jobs" + "github.com/satisfactorymodding/smr-api/storage" + "github.com/satisfactorymodding/smr-api/util" + "github.com/satisfactorymodding/smr-api/validation" + + // Load REST docs + _ "github.com/satisfactorymodding/smr-api/docs" + // Load redis consumers + _ "github.com/satisfactorymodding/smr-api/redis/jobs/consumers" ) type CustomValidator struct { diff --git a/auth/permissions.go b/auth/permissions.go index 85f1588..9a84f68 100755 --- a/auth/permissions.go +++ b/auth/permissions.go @@ -105,8 +105,10 @@ var ( } ) -var idToGroupMapping = make(map[string]*Group) -var roleToGroupMapping = make(map[*Role][]*Group) +var ( + idToGroupMapping = make(map[string]*Group) + roleToGroupMapping = make(map[*Role][]*Group) +) func initializePermissions() { groups := []*Group{GroupAdmin, GroupModerator, GroupSMLDev, GroupBootstrapDev, GroupCompatibilityOfficer} diff --git a/cmd/paseto/main.go b/cmd/paseto/main.go index e09e7f1..c282972 100644 --- a/cmd/paseto/main.go +++ b/cmd/paseto/main.go @@ -7,7 +7,6 @@ import ( func main() { publicKey, privateKey, err := ed25519.GenerateKey(nil) - if err != nil { panic(err) } diff --git a/cmd/validate-zip/main.go b/cmd/validate-zip/main.go index ea1707d..77dad0d 100644 --- a/cmd/validate-zip/main.go +++ b/cmd/validate-zip/main.go @@ -16,7 +16,6 @@ func main() { validation.InitializeValidator() _, err := validation.ExtractModInfo(context.Background(), f, true, true, "N/A") - if err != nil { panic(err) } diff --git a/config/config.go b/config/config.go index 1a85e5f..ad26119 100644 --- a/config/config.go +++ b/config/config.go @@ -78,6 +78,7 @@ func initializeDefaults() { viper.SetDefault("storage.endpoint", "http://localhost:9000") viper.SetDefault("storage.region", "eu-central-1") viper.SetDefault("storage.base_url", "http://localhost:9000") + viper.SetDefault("storage.keypath", "%s/file/%s/%s") viper.SetDefault("oauth.github.client_id", "") viper.SetDefault("oauth.github.client_secret", "") diff --git a/dataloader/loaders.go b/dataloader/loaders.go index 203c408..8fc0303 100644 --- a/dataloader/loaders.go +++ b/dataloader/loaders.go @@ -4,10 +4,10 @@ import ( "context" "time" - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/labstack/echo/v4" "github.com/patrickmn/go-cache" + + "github.com/satisfactorymodding/smr-api/db/postgres" ) type loadersKey struct{} diff --git a/dataloader/userloader_gen.go b/dataloader/userloader_gen.go index c148a7d..a91e92c 100755 --- a/dataloader/userloader_gen.go +++ b/dataloader/userloader_gen.go @@ -32,34 +32,20 @@ func NewUserLoader(config UserLoaderConfig) *UserLoader { // UserLoader batches and caches requests type UserLoader struct { - // this method provides the data for the loader - fetch func(keys []string) ([]*postgres.User, []error) - - // how long to done before sending a batch - wait time.Duration - - // this will limit the maximum number of keys to send in one batch, 0 = no limit + fetch func(keys []string) ([]*postgres.User, []error) + cache map[string]*postgres.User + batch *userLoaderBatch + wait time.Duration maxBatch int - - // INTERNAL - - // lazily created cache - cache map[string]*postgres.User - - // the current batch. keys will continue to be collected until timeout is hit, - // then everything will be sent to the fetch method and out to the listeners - batch *userLoaderBatch - - // mutex to prevent races - mu sync.Mutex + mu sync.Mutex } type userLoaderBatch struct { + done chan struct{} keys []string data []*postgres.User error []error closing bool - done chan struct{} } // Load a User by key, batching and caching will be applied automatically diff --git a/dataloader/usermodloader_gen.go b/dataloader/usermodloader_gen.go index 3dab894..caf0335 100755 --- a/dataloader/usermodloader_gen.go +++ b/dataloader/usermodloader_gen.go @@ -32,34 +32,20 @@ func NewUserModLoader(config UserModLoaderConfig) *UserModLoader { // UserModLoader batches and caches requests type UserModLoader struct { - // this method provides the data for the loader - fetch func(keys []string) ([][]postgres.UserMod, []error) - - // how long to done before sending a batch - wait time.Duration - - // this will limit the maximum number of keys to send in one batch, 0 = no limit + fetch func(keys []string) ([][]postgres.UserMod, []error) + cache map[string][]postgres.UserMod + batch *userModLoaderBatch + wait time.Duration maxBatch int - - // INTERNAL - - // lazily created cache - cache map[string][]postgres.UserMod - - // the current batch. keys will continue to be collected until timeout is hit, - // then everything will be sent to the fetch method and out to the listeners - batch *userModLoaderBatch - - // mutex to prevent races - mu sync.Mutex + mu sync.Mutex } type userModLoaderBatch struct { + done chan struct{} keys []string data [][]postgres.UserMod error []error closing bool - done chan struct{} } // Load a UserMod by key, batching and caching will be applied automatically diff --git a/dataloader/versiondependencyloader_gen.go b/dataloader/versiondependencyloader_gen.go index a0c3532..dc05046 100755 --- a/dataloader/versiondependencyloader_gen.go +++ b/dataloader/versiondependencyloader_gen.go @@ -32,34 +32,20 @@ func NewVersionDependencyLoader(config VersionDependencyLoaderConfig) *VersionDe // VersionDependencyLoader batches and caches requests type VersionDependencyLoader struct { - // this method provides the data for the loader - fetch func(keys []string) ([][]postgres.VersionDependency, []error) - - // how long to done before sending a batch - wait time.Duration - - // this will limit the maximum number of keys to send in one batch, 0 = no limit + fetch func(keys []string) ([][]postgres.VersionDependency, []error) + cache map[string][]postgres.VersionDependency + batch *versionDependencyLoaderBatch + wait time.Duration maxBatch int - - // INTERNAL - - // lazily created cache - cache map[string][]postgres.VersionDependency - - // the current batch. keys will continue to be collected until timeout is hit, - // then everything will be sent to the fetch method and out to the listeners - batch *versionDependencyLoaderBatch - - // mutex to prevent races - mu sync.Mutex + mu sync.Mutex } type versionDependencyLoaderBatch struct { + done chan struct{} keys []string data [][]postgres.VersionDependency error []error closing bool - done chan struct{} } // Load a VersionDependency by key, batching and caching will be applied automatically diff --git a/dataloader/versionloader_gen.go b/dataloader/versionloader_gen.go index 5fafe79..75cd9c6 100755 --- a/dataloader/versionloader_gen.go +++ b/dataloader/versionloader_gen.go @@ -32,34 +32,20 @@ func NewVersionLoader(config VersionLoaderConfig) *VersionLoader { // VersionLoader batches and caches requests type VersionLoader struct { - // this method provides the data for the loader - fetch func(keys []string) ([][]postgres.Version, []error) - - // how long to done before sending a batch - wait time.Duration - - // this will limit the maximum number of keys to send in one batch, 0 = no limit + fetch func(keys []string) ([][]postgres.Version, []error) + cache map[string][]postgres.Version + batch *versionLoaderBatch + wait time.Duration maxBatch int - - // INTERNAL - - // lazily created cache - cache map[string][]postgres.Version - - // the current batch. keys will continue to be collected until timeout is hit, - // then everything will be sent to the fetch method and out to the listeners - batch *versionLoaderBatch - - // mutex to prevent races - mu sync.Mutex + mu sync.Mutex } type versionLoaderBatch struct { + done chan struct{} keys []string data [][]postgres.Version error []error closing bool - done chan struct{} } // Load a Version by key, batching and caching will be applied automatically diff --git a/dataloader/versionloadernometa_gen.go b/dataloader/versionloadernometa_gen.go index de26282..c55b5ae 100755 --- a/dataloader/versionloadernometa_gen.go +++ b/dataloader/versionloadernometa_gen.go @@ -32,34 +32,20 @@ func NewVersionLoaderNoMeta(config VersionLoaderNoMetaConfig) *VersionLoaderNoMe // VersionLoaderNoMeta batches and caches requests type VersionLoaderNoMeta struct { - // this method provides the data for the loader - fetch func(keys []string) ([][]postgres.Version, []error) - - // how long to done before sending a batch - wait time.Duration - - // this will limit the maximum number of keys to send in one batch, 0 = no limit + fetch func(keys []string) ([][]postgres.Version, []error) + cache map[string][]postgres.Version + batch *versionLoaderNoMetaBatch + wait time.Duration maxBatch int - - // INTERNAL - - // lazily created cache - cache map[string][]postgres.Version - - // the current batch. keys will continue to be collected until timeout is hit, - // then everything will be sent to the fetch method and out to the listeners - batch *versionLoaderNoMetaBatch - - // mutex to prevent races - mu sync.Mutex + mu sync.Mutex } type versionLoaderNoMetaBatch struct { + done chan struct{} keys []string data [][]postgres.Version error []error closing bool - done chan struct{} } // Load a Version by key, batching and caching will be applied automatically diff --git a/db/postgres/bootstrap_version.go b/db/postgres/bootstrap_version.go index 4038a87..3e08ea7 100644 --- a/db/postgres/bootstrap_version.go +++ b/db/postgres/bootstrap_version.go @@ -37,7 +37,7 @@ func GetBootstrapVersions(ctx context.Context, filter *models.BootstrapVersionFi Order(string(*filter.OrderBy) + " " + string(*filter.Order)) if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } @@ -62,7 +62,7 @@ func GetBootstrapVersionCount(ctx context.Context, filter *models.BootstrapVersi if filter != nil { if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } diff --git a/db/postgres/guide.go b/db/postgres/guide.go index 4406a55..bf938a9 100644 --- a/db/postgres/guide.go +++ b/db/postgres/guide.go @@ -7,10 +7,10 @@ import ( "strings" "time" + "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" - - "github.com/patrickmn/go-cache" ) func CreateGuide(ctx context.Context, guide *Guide) (*Guide, error) { @@ -85,7 +85,7 @@ func GetGuides(ctx context.Context, filter *models.GuideFilter) []Guide { Order(string(*filter.OrderBy) + " " + string(*filter.Order)) if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } if filter.TagIDs != nil && len(filter.TagIDs) > 0 { @@ -136,7 +136,7 @@ func GetGuideCount(ctx context.Context, filter *models.GuideFilter) int64 { if filter != nil { if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } diff --git a/db/postgres/mod.go b/db/postgres/mod.go index 9482b46..1c92c55 100644 --- a/db/postgres/mod.go +++ b/db/postgres/mod.go @@ -7,12 +7,12 @@ import ( "strings" "time" + "github.com/patrickmn/go-cache" + "gorm.io/gorm" + "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" - - "github.com/patrickmn/go-cache" - "gorm.io/gorm" ) func GetModByID(ctx context.Context, modID string) *Mod { @@ -89,7 +89,7 @@ func GetModCount(ctx context.Context, search string, unapproved bool) int64 { query := DBCtx(ctx).Model(Mod{}).Where("approved = ? AND denied = ?", !unapproved, false) if search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(search, " ", " & ")) } query.Count(&modCount) @@ -141,7 +141,7 @@ func GetMods(ctx context.Context, limit int, offset int, orderBy string, order s query = query.Where("approved = ? AND denied = ?", !unapproved, false) if search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(search, " ", " & ")) } query.Find(&mods) @@ -216,7 +216,7 @@ func NewModQuery(ctx context.Context, filter *models.ModFilter, unapproved bool, query = query.Preload("Tags").Preload("Versions.Arch") if filter != nil { if filter.Search != nil && *filter.Search != "" { - cleanSearch := strings.Replace(strings.TrimSpace(*filter.Search), " ", " & ", -1) + cleanSearch := strings.ReplaceAll(strings.TrimSpace(*filter.Search), " ", " & ") sub := DBCtx(ctx).Table("mods") sub = sub.Select("id, (similarity(name, ?) * 2 + similarity(short_description, ?) + similarity(full_description, ?) * 0.5) as s", cleanSearch, cleanSearch, cleanSearch) diff --git a/db/postgres/mod_archs.go b/db/postgres/mod_archs.go index 1e395a2..aa6bde9 100644 --- a/db/postgres/mod_archs.go +++ b/db/postgres/mod_archs.go @@ -45,7 +45,7 @@ func GetModArchs(ctx context.Context, filter *models.ModArchFilter) []ModArch { Order(string(*filter.OrderBy) + " " + string(*filter.Order)) if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } diff --git a/db/postgres/otel/callbacks.go b/db/postgres/otel/callbacks.go index be653c8..cc691f7 100644 --- a/db/postgres/otel/callbacks.go +++ b/db/postgres/otel/callbacks.go @@ -4,12 +4,10 @@ import ( "strings" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" - - "gorm.io/gorm" - "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" oteltrace "go.opentelemetry.io/otel/trace" + "gorm.io/gorm" ) const ( diff --git a/db/postgres/otel/config.go b/db/postgres/otel/config.go index ee01363..996e680 100644 --- a/db/postgres/otel/config.go +++ b/db/postgres/otel/config.go @@ -5,8 +5,8 @@ import ( ) type config struct { - serviceName string tracerProvider oteltrace.TracerProvider + serviceName string } // Option is used to configure the client. diff --git a/db/postgres/otel/plugin.go b/db/postgres/otel/plugin.go index 9355d7d..4769d2e 100644 --- a/db/postgres/otel/plugin.go +++ b/db/postgres/otel/plugin.go @@ -3,11 +3,10 @@ package otel import ( "fmt" - "gorm.io/gorm" - "go.opentelemetry.io/contrib" "go.opentelemetry.io/otel" oteltrace "go.opentelemetry.io/otel/trace" + "gorm.io/gorm" ) const ( diff --git a/db/postgres/postgres.go b/db/postgres/postgres.go index a81e563..cccec4e 100644 --- a/db/postgres/postgres.go +++ b/db/postgres/postgres.go @@ -17,8 +17,10 @@ import ( "github.com/satisfactorymodding/smr-api/db/postgres/otel" ) -var db *gorm.DB -var dbCache *cache.Cache +var ( + db *gorm.DB + dbCache *cache.Cache +) type UserKey struct{} diff --git a/db/postgres/postgres_types.go b/db/postgres/postgres_types.go index 1a9994a..84e0622 100644 --- a/db/postgres/postgres_types.go +++ b/db/postgres/postgres_types.go @@ -22,56 +22,47 @@ type SMRModel struct { } type User struct { + GithubID *string + GoogleID *string + FacebookID *string SMRModel - Email string `gorm:"type:varchar(256);unique_index"` Username string `gorm:"type:varchar(32)"` Avatar string JoinedFrom string - Banned bool `gorm:"default:false;not null"` - - GithubID *string - GoogleID *string - FacebookID *string - - Mods []Mod `gorm:"many2many:user_mods;"` + Mods []Mod `gorm:"many2many:user_mods;"` + Banned bool `gorm:"default:false;not null"` } type UserSession struct { SMRModel - - UserID string - User User - + UserID string Token string `gorm:"type:varchar(256);unique_index"` UserAgent string + User User } type Mod struct { + LastVersionDate *time.Time + Compatibility *CompatibilityInfo `gorm:"serializer:json"` SMRModel - - Name string `gorm:"type:varchar(32)"` - ShortDescription string `gorm:"type:varchar(128)"` - FullDescription string + CreatorID string Logo string SourceURL string - CreatorID string - Approved bool `gorm:"default:false;not null"` - Denied bool `gorm:"default:false;not null"` - Views uint + FullDescription string + ShortDescription string `gorm:"type:varchar(128)"` + Name string `gorm:"type:varchar(32)"` + ModReference string + Versions []Version + Tags []Tag `gorm:"many2many:mod_tags"` + Users []User `gorm:"many2many:user_mods;"` Downloads uint - Hotness uint Popularity uint - LastVersionDate *time.Time - ModReference string + Hotness uint + Views uint Hidden bool - Compatibility *CompatibilityInfo `gorm:"serializer:json"` - - Users []User `gorm:"many2many:user_mods;"` - - Tags []Tag `gorm:"many2many:mod_tags"` - - Versions []Version + Denied bool `gorm:"default:false;not null"` + Approved bool `gorm:"default:false;not null"` } type UserMod struct { @@ -82,40 +73,36 @@ type UserMod struct { // If updated, update dataloader type Version struct { - SMRModel - - ModID string - - Version string `gorm:"type:varchar(16)"` - SMLVersion string `gorm:"type:varchar(16)"` - Changelog string - Downloads uint - Key string - Stability string `gorm:"default:'alpha'" sql:"type:version_stability"` - Approved bool `gorm:"default:false;not null"` - Denied bool `gorm:"default:false;not null"` - Hotness uint - Arch []ModArch `gorm:"foreignKey:ModVersionID;preload:true"` Metadata *string - ModReference *string - VersionMajor *int - VersionMinor *int - VersionPatch *int - Size *int64 Hash *string + Size *int64 + VersionPatch *int + VersionMinor *int + VersionMajor *int + ModReference *string + SMRModel + Changelog string + Stability string `gorm:"default:'alpha'" sql:"type:version_stability"` + Key string + SMLVersion string `gorm:"type:varchar(16)"` + Version string `gorm:"type:varchar(16)"` + ModID string + Arch []ModArch `gorm:"foreignKey:ModVersionID;preload:true"` + Hotness uint + Downloads uint + Denied bool `gorm:"default:false;not null"` + Approved bool `gorm:"default:false;not null"` } type Guide struct { SMRModel - Name string `gorm:"type:varchar(50)"` ShortDescription string `gorm:"type:varchar(128)"` Guide string - Views uint + UserID string Tags []Tag `gorm:"many2many:guide_tags"` - - UserID string - User User + User User + Views uint } type UserGroup struct { @@ -126,16 +113,15 @@ type UserGroup struct { } type SMLVersion struct { + Date time.Time + BootstrapVersion *string SMRModel - Version string `gorm:"type:varchar(32);unique_index"` - SatisfactoryVersion int Stability string `sql:"type:version_stability"` - Date time.Time Link string - Arch []SMLArch `gorm:"foreignKey:SMLVersionID;preload:true"` Changelog string - BootstrapVersion *string + Arch []SMLArch `gorm:"foreignKey:SMLVersionID;preload:true"` + SatisfactoryVersion int } type VersionDependency struct { @@ -149,14 +135,13 @@ type VersionDependency struct { } type BootstrapVersion struct { + Date time.Time SMRModel - Version string `gorm:"type:varchar(32);unique_index"` - SatisfactoryVersion int Stability string `sql:"type:version_stability"` - Date time.Time Link string Changelog string + SatisfactoryVersion int } type Announcement struct { @@ -199,8 +184,8 @@ type ModArch struct { ModVersionID string `gorm:"column:mod_version_arch_id"` Platform string Key string - Size int64 Hash string + Size int64 } func (ModArch) TableName() string { diff --git a/db/postgres/sml_archs.go b/db/postgres/sml_archs.go index 571b7db..f8b0108 100644 --- a/db/postgres/sml_archs.go +++ b/db/postgres/sml_archs.go @@ -47,7 +47,7 @@ func GetSMLArchs(ctx context.Context, filter *models.SMLArchFilter) []SMLArch { Order(string(*filter.OrderBy) + " " + string(*filter.Order)) if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } diff --git a/db/postgres/sml_version.go b/db/postgres/sml_version.go index 0eabc79..762e923 100644 --- a/db/postgres/sml_version.go +++ b/db/postgres/sml_version.go @@ -46,7 +46,7 @@ func GetSMLVersions(ctx context.Context, filter *models.SMLVersionFilter) []SMLV Order(string(*filter.OrderBy) + " " + string(*filter.Order)) if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } @@ -72,7 +72,7 @@ func GetSMLVersionCount(ctx context.Context, filter *models.SMLVersionFilter) in if filter != nil { if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } diff --git a/db/postgres/tags.go b/db/postgres/tags.go index 252faea..3ab95d1 100644 --- a/db/postgres/tags.go +++ b/db/postgres/tags.go @@ -9,12 +9,12 @@ import ( "strings" "time" - "github.com/satisfactorymodding/smr-api/generated" - "github.com/satisfactorymodding/smr-api/util" - "github.com/finnbear/moderation" "github.com/mitchellh/hashstructure/v2" "github.com/patrickmn/go-cache" + + "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/util" ) func ValidateTagName(tag string) error { @@ -124,7 +124,7 @@ func GetTags(ctx context.Context, filter *generated.TagFilter) []Tag { if filter != nil { if filter.Search != nil && *filter.Search != "" { - cleanSearch := strings.Replace(strings.TrimSpace(*filter.Search), " ", " & ", -1) + cleanSearch := strings.ReplaceAll(strings.TrimSpace(*filter.Search), " ", " & ") sub := DBCtx(ctx).Table("tags") sub = sub.Select("id, similarity(name, ?) as s", cleanSearch, cleanSearch, cleanSearch) diff --git a/db/postgres/version.go b/db/postgres/version.go index db4fdf8..5440055 100644 --- a/db/postgres/version.go +++ b/db/postgres/version.go @@ -9,10 +9,10 @@ import ( "strings" "time" + "github.com/patrickmn/go-cache" + "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" - - "github.com/patrickmn/go-cache" ) var semverCheck = regexp.MustCompile(`^(<=|<|>|>=|\^)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) @@ -216,7 +216,7 @@ func GetVersionsNew(ctx context.Context, filter *models.VersionFilter, unapprove Order(string(*filter.OrderBy) + " " + string(*filter.Order)) if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(version) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(version) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } if filter.Fields != nil && len(filter.Fields) > 0 { @@ -248,7 +248,7 @@ func GetVersionCountNew(ctx context.Context, filter *models.VersionFilter, unapp if filter != nil { if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(version) @@ to_tsquery(?)", strings.Replace(*filter.Search, " ", " & ", -1)) + query = query.Where("to_tsvector(version) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) } } diff --git a/db/statistics.go b/db/statistics.go index 6db6114..f772341 100644 --- a/db/statistics.go +++ b/db/statistics.go @@ -5,10 +5,10 @@ import ( "regexp" "time" + "github.com/rs/zerolog/log" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/redis" - - "github.com/rs/zerolog/log" ) var keyRegex = regexp.MustCompile(`^([^:]+):([^:]+):([^:]+):([^:]+)$`) @@ -34,7 +34,7 @@ func RunAsyncStatisticLoop(ctx context.Context) { resultMap[entityType][action] = make(map[string]uint) } - resultMap[entityType][action][entityID] = resultMap[entityType][action][entityID] + 1 + resultMap[entityType][action][entityID]++ } } @@ -45,27 +45,25 @@ func RunAsyncStatisticLoop(ctx context.Context) { ctxWithTx := postgres.ContextWithDB(ctx, updateTx) switch entityType { case "mod": - switch action { - case "view": + if action == "view" { mod := postgres.GetModByID(ctxWithTx, entityID) if mod != nil { currentHotness := mod.Hotness if currentHotness > 4 { // Preserve some of the hotness - currentHotness = currentHotness / 4 + currentHotness /= 4 } updateTx.Model(&mod).UpdateColumns(postgres.Mod{Hotness: currentHotness + count}) } } case "version": - switch action { - case "download": + if action == "download" { version := postgres.GetVersion(ctxWithTx, entityID) if version != nil { currentHotness := version.Hotness if currentHotness > 4 { // Preserve some of the popularity - currentHotness = currentHotness / 4 + currentHotness /= 4 } updateTx.Model(&version).UpdateColumns(postgres.Version{Hotness: currentHotness + count}) } @@ -94,7 +92,7 @@ func RunAsyncStatisticLoop(ctx context.Context) { currentPopularity := mod.Popularity if currentPopularity > 4 { // Preserve some of the popularity - currentPopularity = currentPopularity / 4 + currentPopularity /= 4 } updateTx.Model(&mod).UpdateColumns(postgres.Mod{ Popularity: currentPopularity + row.Hotness, diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 73212cb..3327f6b 100755 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -15,7 +15,7 @@ services: - 5432:5432 minio: - image: minio/minio + image: quay.io/minio/minio ports: - 9000:9000 - 9001:9001 diff --git a/generated/custom_models.go b/generated/custom_models.go index a7288de..8498088 100755 --- a/generated/custom_models.go +++ b/generated/custom_models.go @@ -21,7 +21,7 @@ type UpdateMod struct { SourceURL *string `json:"source_url"` ModReference *string `json:"mod_reference"` Hidden *bool `json:"hidden"` + Compatibility *CompatibilityInfoInput `json:"compatibility"` Authors []UpdateUserMod `json:"authors"` TagIDs []string `json:"tagIDs" validate:"dive,min=3,max=24"` - Compatibility *CompatibilityInfoInput `json:"compatibility"` } diff --git a/gql/directive.go b/gql/directive.go index 86f735c..72657ee 100644 --- a/gql/directive.go +++ b/gql/directive.go @@ -5,13 +5,13 @@ import ( "net/http" "reflect" + "github.com/99designs/gqlgen/graphql" + "github.com/pkg/errors" + "github.com/satisfactorymodding/smr-api/auth" "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/util" - - "github.com/99designs/gqlgen/graphql" - "github.com/pkg/errors" ) func MakeDirective() generated.DirectiveRoot { @@ -37,7 +37,7 @@ type Directive struct { generated.DirectiveRoot } -func canEditMod(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) { +func canEditMod(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) dbMod := postgres.GetModByID(ctx, getArgument(ctx, field).(string)) @@ -57,7 +57,7 @@ func canEditMod(ctx context.Context, obj interface{}, next graphql.Resolver, fie return nil, errors.New("user not authorized to perform this action") } -func canEditModCompatibility(ctx context.Context, obj interface{}, next graphql.Resolver, field *string) (res interface{}, err error) { +func canEditModCompatibility(ctx context.Context, obj interface{}, next graphql.Resolver, field *string) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleEditAnyModCompatibility) || user.Has(ctx, auth.RoleEditAnyContent) { @@ -81,7 +81,7 @@ func canEditModCompatibility(ctx context.Context, obj interface{}, next graphql. return nil, errors.New("user not authorized to perform this action") } -func canEditVersion(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) { +func canEditVersion(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) dbVersion := postgres.GetVersion(ctx, getArgument(ctx, field).(string)) @@ -101,7 +101,7 @@ func canEditVersion(ctx context.Context, obj interface{}, next graphql.Resolver, return nil, errors.New("user not authorized to perform this action") } -func canEditUser(ctx context.Context, obj interface{}, next graphql.Resolver, field string, object bool) (res interface{}, err error) { +func canEditUser(ctx context.Context, obj interface{}, next graphql.Resolver, field string, object bool) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) var userID string @@ -128,7 +128,7 @@ func canEditUser(ctx context.Context, obj interface{}, next graphql.Resolver, fi return nil, errors.New("user not authorized to perform this action") } -func canEditGuide(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) { +func canEditGuide(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) dbGuide := postgres.GetGuideByID(ctx, getArgument(ctx, field).(string)) @@ -148,7 +148,7 @@ func canEditGuide(ctx context.Context, obj interface{}, next graphql.Resolver, f return nil, errors.New("user not authorized to perform this action") } -func isLoggedIn(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func isLoggedIn(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { header := ctx.Value(util.ContextHeader{}).(http.Header) authorization := header.Get("Authorization") @@ -171,7 +171,7 @@ func isLoggedIn(ctx context.Context, obj interface{}, next graphql.Resolver) (re return next(userCtx) } -func isNotLoggedIn(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func isNotLoggedIn(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { header := ctx.Value(util.ContextHeader{}).(http.Header) authorization := header.Get("Authorization") @@ -190,7 +190,7 @@ func getArgument(ctx context.Context, key string) interface{} { return graphql.GetFieldContext(ctx).Args[key] } -func canApproveMods(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canApproveMods(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleApproveMods) { @@ -200,7 +200,7 @@ func canApproveMods(ctx context.Context, obj interface{}, next graphql.Resolver) return nil, errors.New("user not authorized to perform this action") } -func canApproveVersions(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canApproveVersions(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleApproveVersions) { @@ -210,7 +210,7 @@ func canApproveVersions(ctx context.Context, obj interface{}, next graphql.Resol return nil, errors.New("user not authorized to perform this action") } -func canEditUsers(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canEditUsers(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleEditUsers) { @@ -220,7 +220,7 @@ func canEditUsers(ctx context.Context, obj interface{}, next graphql.Resolver) ( return nil, errors.New("user not authorized to perform this action") } -func canEditSMLVersions(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canEditSMLVersions(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleEditSMLVersions) { @@ -230,7 +230,7 @@ func canEditSMLVersions(ctx context.Context, obj interface{}, next graphql.Resol return nil, errors.New("user not authorized to perform this action") } -func canEditBootstrapVersions(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canEditBootstrapVersions(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleEditBootstrapVersions) { @@ -240,7 +240,7 @@ func canEditBootstrapVersions(ctx context.Context, obj interface{}, next graphql return nil, errors.New("user not authorized to perform this action") } -func canEditAnnouncements(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canEditAnnouncements(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleEditAnnouncements) { @@ -250,7 +250,7 @@ func canEditAnnouncements(ctx context.Context, obj interface{}, next graphql.Res return nil, errors.New("user not authorized to perform this action") } -func canManageTags(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func canManageTags(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) { user := ctx.Value(postgres.UserKey{}).(*postgres.User) if user.Has(ctx, auth.RoleManageTags) { diff --git a/gql/gql_types.go b/gql/gql_types.go index 5c7e948..7a7f877 100644 --- a/gql/gql_types.go +++ b/gql/gql_types.go @@ -12,18 +12,18 @@ func DBUserToGenerated(user *postgres.User) *generated.User { return nil } - Email := (*user).Email - Avatar := (*user).Avatar + Email := user.Email + Avatar := user.Avatar result := &generated.User{ - ID: (*user).ID, - Username: (*user).Username, + ID: user.ID, + Username: user.Username, Email: &Email, Avatar: &Avatar, CreatedAt: user.CreatedAt.Format(time.RFC3339Nano), - GithubID: (*user).GithubID, - GoogleID: (*user).GoogleID, - FacebookID: (*user).FacebookID, + GithubID: user.GithubID, + GoogleID: user.GoogleID, + FacebookID: user.FacebookID, } return result @@ -34,13 +34,13 @@ func DBModToGenerated(mod *postgres.Mod) *generated.Mod { return nil } - Logo := (*mod).Logo - SourceURL := (*mod).SourceURL - FullDescription := (*mod).FullDescription + Logo := mod.Logo + SourceURL := mod.SourceURL + FullDescription := mod.FullDescription var LastVersionDate string - if (*mod).LastVersionDate != nil { - LastVersionDate = (*mod).LastVersionDate.Format(time.RFC3339Nano) + if mod.LastVersionDate != nil { + LastVersionDate = mod.LastVersionDate.Format(time.RFC3339Nano) } return &generated.Mod{ diff --git a/gql/gql_utils.go b/gql/gql_utils.go index 37dd59d..5dbe22c 100644 --- a/gql/gql_utils.go +++ b/gql/gql_utils.go @@ -8,14 +8,13 @@ import ( "strings" "time" - "github.com/satisfactorymodding/smr-api/db/postgres" - - "github.com/satisfactorymodding/smr-api/generated" - "github.com/satisfactorymodding/smr-api/util" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/util" ) type TraceWrapper struct { diff --git a/gql/resolver.go b/gql/resolver.go index 3cb3c6c..51c0c37 100755 --- a/gql/resolver.go +++ b/gql/resolver.go @@ -9,45 +9,59 @@ type Resolver struct{} func (r *Resolver) Mod() generated.ModResolver { return &modResolver{r} } + func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} } + func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } + func (r *Resolver) User() generated.UserResolver { return &userResolver{r} } + func (r *Resolver) UserMod() generated.UserModResolver { return &userModResolver{r} } + func (r *Resolver) ModArch() generated.ModArchResolver { return &modlinkResolver{r} } + func (r *Resolver) Version() generated.VersionResolver { return &versionResolver{r} } + func (r *Resolver) GetMods() generated.GetModsResolver { return &getModsResolver{r} } + func (r *Resolver) GetMyMods() generated.GetMyModsResolver { return &getMyModsResolver{r} } + func (r *Resolver) GetVersions() generated.GetVersionsResolver { return &getVersionsResolver{r} } + func (r *Resolver) GetMyVersions() generated.GetMyVersionsResolver { return &getMyVersionsResolver{r} } + func (r *Resolver) Guide() generated.GuideResolver { return &guideResolver{r} } + func (r *Resolver) GetGuides() generated.GetGuidesResolver { return &getGuidesResolver{r} } + func (r *Resolver) GetSMLVersions() generated.GetSMLVersionsResolver { return &getSMLVersionsResolver{r} } + func (r *Resolver) GetBootstrapVersions() generated.GetBootstrapVersionsResolver { return &getBootstrapVersionsResolver{r} } diff --git a/gql/resolver_announcements.go b/gql/resolver_announcements.go index d1f1557..47b9d16 100644 --- a/gql/resolver_announcements.go +++ b/gql/resolver_announcements.go @@ -4,11 +4,11 @@ import ( "context" "github.com/pkg/errors" - "github.com/satisfactorymodding/smr-api/util" "gopkg.in/go-playground/validator.v9" "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/util" ) func (r *mutationResolver) CreateAnnouncement(ctx context.Context, announcement generated.NewAnnouncement) (*generated.Announcement, error) { diff --git a/gql/resolver_bootstrap_versions.go b/gql/resolver_bootstrap_versions.go index 33118c9..5a1ecfc 100644 --- a/gql/resolver_bootstrap_versions.go +++ b/gql/resolver_bootstrap_versions.go @@ -4,15 +4,14 @@ import ( "context" "time" + "github.com/99designs/gqlgen/graphql" + "github.com/pkg/errors" + "gopkg.in/go-playground/validator.v9" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" - - "github.com/pkg/errors" - - "github.com/99designs/gqlgen/graphql" - "gopkg.in/go-playground/validator.v9" ) func (r *mutationResolver) CreateBootstrapVersion(ctx context.Context, bootstrapVersion generated.NewBootstrapVersion) (*generated.BootstrapVersion, error) { @@ -25,7 +24,6 @@ func (r *mutationResolver) CreateBootstrapVersion(ctx context.Context, bootstrap } date, err := time.Parse(time.RFC3339Nano, bootstrapVersion.Date) - if err != nil { return nil, errors.Wrap(err, "failed to parse date") } @@ -40,7 +38,6 @@ func (r *mutationResolver) CreateBootstrapVersion(ctx context.Context, bootstrap } resultBootstrapVersion, err := postgres.CreateBootstrapVersion(newCtx, dbBootstrapVersion) - if err != nil { return nil, errors.Wrap(err, "failed to create bootstrap version") } @@ -111,7 +108,6 @@ func (r *getBootstrapVersionsResolver) BootstrapVersions(ctx context.Context, ob resolverContext := graphql.GetFieldContext(ctx) bootstrapVersionFilter, err := models.ProcessBootstrapVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -142,7 +138,6 @@ func (r *getBootstrapVersionsResolver) Count(ctx context.Context, obj *generated resolverContext := graphql.GetFieldContext(ctx) bootstrapVersionFilter, err := models.ProcessBootstrapVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } diff --git a/gql/resolver_guides.go b/gql/resolver_guides.go index 552ba7e..33d5252 100644 --- a/gql/resolver_guides.go +++ b/gql/resolver_guides.go @@ -4,16 +4,15 @@ import ( "context" "time" + "github.com/99designs/gqlgen/graphql" + "github.com/pkg/errors" + "gopkg.in/go-playground/validator.v9" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/redis" "github.com/satisfactorymodding/smr-api/util" - - "github.com/pkg/errors" - - "github.com/99designs/gqlgen/graphql" - "gopkg.in/go-playground/validator.v9" ) func (r *mutationResolver) CreateGuide(ctx context.Context, guide generated.NewGuide) (*generated.Guide, error) { @@ -36,7 +35,6 @@ func (r *mutationResolver) CreateGuide(ctx context.Context, guide generated.NewG dbGuide.UserID = user.ID resultGuide, err := postgres.CreateGuide(newCtx, dbGuide) - if err != nil { return nil, err } @@ -124,7 +122,6 @@ func (r *getGuidesResolver) Guides(ctx context.Context, obj *generated.GetGuides resolverContext := graphql.GetFieldContext(ctx) guideFilter, err := models.ProcessGuideFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -155,7 +152,6 @@ func (r *getGuidesResolver) Count(ctx context.Context, obj *generated.GetGuides) resolverContext := graphql.GetFieldContext(ctx) guideFilter, err := models.ProcessGuideFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } diff --git a/gql/resolver_mods.go b/gql/resolver_mods.go index f9ececb..5094f3c 100644 --- a/gql/resolver_mods.go +++ b/gql/resolver_mods.go @@ -6,6 +6,11 @@ import ( "io" "time" + "github.com/99designs/gqlgen/graphql" + "github.com/dgraph-io/ristretto" + "github.com/pkg/errors" + "gopkg.in/go-playground/validator.v9" + "github.com/satisfactorymodding/smr-api/dataloader" "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" @@ -15,12 +20,6 @@ import ( "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" "github.com/satisfactorymodding/smr-api/util/converter" - - "github.com/pkg/errors" - - "github.com/99designs/gqlgen/graphql" - "github.com/dgraph-io/ristretto" - "gopkg.in/go-playground/validator.v9" ) func (r *mutationResolver) CreateMod(ctx context.Context, mod generated.NewMod) (*generated.Mod, error) { @@ -55,7 +54,6 @@ func (r *mutationResolver) CreateMod(ctx context.Context, mod generated.NewMod) if mod.Logo != nil { file, err := io.ReadAll(mod.Logo.File) - if err != nil { return nil, errors.Wrap(err, "failed to read logo file") } @@ -70,7 +68,6 @@ func (r *mutationResolver) CreateMod(ctx context.Context, mod generated.NewMod) } resultMod, err := postgres.CreateMod(newCtx, dbMod) - if err != nil { return nil, err } @@ -103,7 +100,6 @@ func (r *mutationResolver) UpdateMod(ctx context.Context, modID string, mod gene } err := postgres.ResetModTags(newCtx, modID, mod.TagIDs) - if err != nil { return nil, err } @@ -128,13 +124,11 @@ func (r *mutationResolver) UpdateMod(ctx context.Context, modID string, mod gene if mod.Logo != nil { file, err := io.ReadAll(mod.Logo.File) - if err != nil { return nil, errors.Wrap(err, "failed to read logo file") } logoData, err := converter.ConvertAnyImageToWebp(ctx, file) - if err != nil { return nil, err } @@ -151,7 +145,6 @@ func (r *mutationResolver) UpdateMod(ctx context.Context, modID string, mod gene if mod.Authors != nil { authors, err := dataloader.For(ctx).UserModsByModID.Load(modID) - if err != nil { return nil, err } @@ -330,7 +323,6 @@ func (r *getModsResolver) Mods(ctx context.Context, obj *generated.GetMods) ([]* unapproved := resolverContext.Parent.Field.Field.Name == "getUnapprovedMods" modFilter, err := models.ProcessModFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -361,7 +353,6 @@ func (r *getModsResolver) Count(ctx context.Context, obj *generated.GetMods) (in unapproved := resolverContext.Parent.Field.Field.Name == "getUnapprovedMods" modFilter, err := models.ProcessModFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } @@ -383,7 +374,6 @@ func (r *getMyModsResolver) Mods(ctx context.Context, obj *generated.GetMyMods) unapproved := resolverContext.Parent.Field.Field.Name == "getMyUnapprovedMods" modFilter, err := models.ProcessModFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -420,7 +410,6 @@ func (r *getMyModsResolver) Count(ctx context.Context, obj *generated.GetMyMods) unapproved := resolverContext.Parent.Field.Field.Name == "getMyUnapprovedMods" modFilter, err := models.ProcessModFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } @@ -439,7 +428,6 @@ func (r *modResolver) Authors(ctx context.Context, obj *generated.Mod) ([]*gener defer wrapper.end() authors, err := dataloader.For(ctx).UserModsByModID.Load(obj.ID) - if err != nil { return nil, err } @@ -479,7 +467,6 @@ func (r *modResolver) Versions(ctx context.Context, obj *generated.Mod, filter m defer wrapper.end() versionFilter, err := models.ProcessVersionFilter(filter) - if err != nil { return nil, err } diff --git a/gql/resolver_oauth.go b/gql/resolver_oauth.go index bca2849..b8189f0 100644 --- a/gql/resolver_oauth.go +++ b/gql/resolver_oauth.go @@ -6,13 +6,13 @@ import ( "net/http" "net/url" + "github.com/pkg/errors" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/oauth" "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" - - "github.com/pkg/errors" ) func (r *queryResolver) GetOAuthOptions(ctx context.Context, callbackURL string) (*generated.OAuthOptions, error) { @@ -20,7 +20,6 @@ func (r *queryResolver) GetOAuthOptions(ctx context.Context, callbackURL string) defer wrapper.end() unescapedURL, err := url.PathUnescape(callbackURL) - if err != nil { return nil, errors.Wrap(err, "unable to unescape callback url") } @@ -43,7 +42,6 @@ func (r *mutationResolver) OAuthGithub(ctx context.Context, code string, state s } user, err := oauth.GithubCallback(code, state) - if err != nil { return nil, err } @@ -52,7 +50,6 @@ func (r *mutationResolver) OAuthGithub(ctx context.Context, code string, state s userAgent := header.Get("User-Agent") token, err := completeOAuthFlow(newCtx, user, userAgent) - if err != nil { return nil, err } @@ -71,7 +68,6 @@ func (r *mutationResolver) OAuthGoogle(ctx context.Context, code string, state s } user, err := oauth.GoogleCallback(code, state) - if err != nil { return nil, err } @@ -80,7 +76,6 @@ func (r *mutationResolver) OAuthGoogle(ctx context.Context, code string, state s userAgent := header.Get("User-Agent") token, err := completeOAuthFlow(newCtx, user, userAgent) - if err != nil { return nil, err } @@ -99,7 +94,6 @@ func (r *mutationResolver) OAuthFacebook(ctx context.Context, code string, state } user, err := oauth.FacebookCallback(code, state) - if err != nil { return nil, err } @@ -108,7 +102,6 @@ func (r *mutationResolver) OAuthFacebook(ctx context.Context, code string, state userAgent := header.Get("User-Agent") token, err := completeOAuthFlow(newCtx, user, userAgent) - if err != nil { return nil, err } @@ -126,7 +119,6 @@ func completeOAuthFlow(ctx context.Context, user *oauth.UserData, userAgent stri if avatarURL != "" && newUser { avatarData, err := util.LinkToWebp(ctx, avatarURL) - if err != nil { return nil, err } diff --git a/gql/resolver_sml_archs.go b/gql/resolver_sml_archs.go index b91ef69..be4e776 100644 --- a/gql/resolver_sml_archs.go +++ b/gql/resolver_sml_archs.go @@ -6,10 +6,9 @@ import ( "github.com/pkg/errors" "gopkg.in/go-playground/validator.v9" - "github.com/satisfactorymodding/smr-api/util" - "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" + "github.com/satisfactorymodding/smr-api/util" ) func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlArch generated.NewSMLArch) (*generated.SMLArch, error) { @@ -28,7 +27,6 @@ func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlArch generated. } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) - if err != nil { return nil, err } diff --git a/gql/resolver_sml_versions.go b/gql/resolver_sml_versions.go index a329900..c04c6c1 100644 --- a/gql/resolver_sml_versions.go +++ b/gql/resolver_sml_versions.go @@ -4,15 +4,14 @@ import ( "context" "time" + "github.com/99designs/gqlgen/graphql" + "github.com/pkg/errors" + "gopkg.in/go-playground/validator.v9" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/models" "github.com/satisfactorymodding/smr-api/util" - - "github.com/pkg/errors" - - "github.com/99designs/gqlgen/graphql" - "gopkg.in/go-playground/validator.v9" ) func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion generated.NewSMLVersion) (*generated.SMLVersion, error) { @@ -25,7 +24,6 @@ func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion gene } date, err := time.Parse(time.RFC3339Nano, smlVersion.Date) - if err != nil { return nil, errors.Wrap(err, "failed to parse date") } @@ -51,7 +49,6 @@ func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion gene } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) - if err != nil { return nil, err } @@ -118,7 +115,6 @@ func (r *mutationResolver) UpdateSMLVersion(ctx context.Context, smlVersionID st } resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArch) - if err != nil { return nil, err } @@ -178,7 +174,6 @@ func (r *getSMLVersionsResolver) SmlVersions(ctx context.Context, obj *generated resolverContext := graphql.GetFieldContext(ctx) smlVersionFilter, err := models.ProcessSMLVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -209,7 +204,6 @@ func (r *getSMLVersionsResolver) Count(ctx context.Context, obj *generated.GetSM resolverContext := graphql.GetFieldContext(ctx) smlVersionFilter, err := models.ProcessSMLVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } diff --git a/gql/resolver_tags.go b/gql/resolver_tags.go index 79d0da5..8050dcf 100644 --- a/gql/resolver_tags.go +++ b/gql/resolver_tags.go @@ -2,9 +2,10 @@ package gql import ( "github.com/pkg/errors" + "golang.org/x/net/context" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" - "golang.org/x/net/context" ) func (r *mutationResolver) CreateTag(ctx context.Context, tagName string) (*generated.Tag, error) { diff --git a/gql/resolver_users.go b/gql/resolver_users.go index a69f162..6342880 100644 --- a/gql/resolver_users.go +++ b/gql/resolver_users.go @@ -11,6 +11,9 @@ import ( "net/http" "net/url" + "github.com/pkg/errors" + "github.com/spf13/viper" + "github.com/satisfactorymodding/smr-api/auth" "github.com/satisfactorymodding/smr-api/dataloader" "github.com/satisfactorymodding/smr-api/db/postgres" @@ -18,10 +21,6 @@ import ( "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" "github.com/satisfactorymodding/smr-api/util/converter" - - "github.com/pkg/errors" - - "github.com/spf13/viper" ) func (r *mutationResolver) UpdateUser(ctx context.Context, userID string, input generated.UpdateUser) (*generated.User, error) { @@ -36,13 +35,11 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, userID string, input if input.Avatar != nil { file, err := io.ReadAll(input.Avatar.File) - if err != nil { return nil, errors.Wrap(err, "failed to read avatar file") } avatarData, err := converter.ConvertAnyImageToWebp(ctx, file) - if err != nil { return nil, err } @@ -227,7 +224,6 @@ func (r *userModResolver) User(ctx context.Context, obj *generated.UserMod) (*ge defer wrapper.end() user, err := dataloader.For(ctx).UserByID.Load(obj.UserID) - if err != nil { return nil, err } @@ -264,7 +260,6 @@ func (r *mutationResolver) DiscourseSso(ctx context.Context, sso string, sig str } nonceString, err := base64.StdEncoding.DecodeString(sso) - if err != nil { return nil, errors.Wrap(err, "failed to decode sso") } diff --git a/gql/resolver_versions.go b/gql/resolver_versions.go index bb6332c..f551b38 100644 --- a/gql/resolver_versions.go +++ b/gql/resolver_versions.go @@ -7,6 +7,11 @@ import ( "runtime/debug" "time" + "github.com/99designs/gqlgen/graphql" + "github.com/dgraph-io/ristretto" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/satisfactorymodding/smr-api/dataloader" "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" @@ -15,12 +20,6 @@ import ( "github.com/satisfactorymodding/smr-api/redis" "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" - - "github.com/pkg/errors" - - "github.com/99designs/gqlgen/graphql" - "github.com/dgraph-io/ristretto" - "github.com/rs/zerolog/log" ) func (r *mutationResolver) CreateVersion(ctx context.Context, modID string) (string, error) { @@ -72,7 +71,6 @@ func (r *mutationResolver) UploadVersionPart(ctx context.Context, modID string, // TODO Optimize fileData, err := io.ReadAll(file.File) - if err != nil { return false, errors.Wrap(err, "failed to read file") } @@ -187,6 +185,7 @@ func (r *mutationResolver) ApproveVersion(ctx context.Context, versionID string) go integrations.NewVersion(util.ReWrapCtx(ctx), dbVersion) go storage.DeleteModArch(ctx, dbVersion.ModID, mod.Name, versionID, "Combined") + go storage.DeleteModArch(ctx, dbVersion.ModID, mod.Name, dbVersion.Version, "Combined") return true, nil } @@ -273,7 +272,6 @@ func (r *getVersionsResolver) Versions(ctx context.Context, _ *generated.GetVers unapproved := resolverContext.Parent.Field.Field.Name == "getUnapprovedVersions" versionFilter, err := models.ProcessVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -310,7 +308,6 @@ func (r *getVersionsResolver) Count(ctx context.Context, _ *generated.GetVersion unapproved := resolverContext.Parent.Field.Field.Name == "getUnapprovedVersions" versionFilter, err := models.ProcessVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } @@ -382,7 +379,6 @@ func (r *getMyVersionsResolver) Versions(ctx context.Context, _ *generated.GetMy unapproved := resolverContext.Parent.Field.Field.Name == "getMyUnapprovedVersions" versionFilter, err := models.ProcessVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return nil, err } @@ -419,7 +415,6 @@ func (r *getMyVersionsResolver) Count(ctx context.Context, _ *generated.GetMyVer unapproved := resolverContext.Parent.Field.Field.Name == "getMyUnapprovedVersions" versionFilter, err := models.ProcessVersionFilter(resolverContext.Parent.Args["filter"].(map[string]interface{})) - if err != nil { return 0, err } diff --git a/gql/versions.go b/gql/versions.go index ff491f8..97e2c0a 100644 --- a/gql/versions.go +++ b/gql/versions.go @@ -6,6 +6,10 @@ import ( "io" "time" + "github.com/davecgh/go-spew/spew" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/generated" "github.com/satisfactorymodding/smr-api/integrations" @@ -13,10 +17,6 @@ import ( "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" "github.com/satisfactorymodding/smr-api/validation" - - "github.com/pkg/errors" - - "github.com/rs/zerolog/log" ) func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionID string, version generated.NewVersion) (*generated.CreateVersionResponse, error) { @@ -31,7 +31,6 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI } modFile, err := storage.GetMod(mod.ID, mod.Name, versionID) - if err != nil { storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) return nil, err @@ -39,15 +38,15 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI // TODO Optimize fileData, err := io.ReadAll(modFile) - if err != nil { storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) return nil, errors.Wrap(err, "failed reading mod file") } modInfo, err := validation.ExtractModInfo(ctx, fileData, true, true, mod.ModReference) - if err != nil { + spew.Dump(err) + l.Err(err).Msg("failed extracting mod info") storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) return nil, err } @@ -121,7 +120,6 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI postgres.Save(ctx, &dbVersion) } - //Okay, uploaded file is read and readable... let's dump it and separate, and re-save as ModName-Combined-SemVersion in ModArch separated := storage.SeparateMod(ctx, fileData, mod.ID, mod.Name, dbVersion.ID, modInfo.Version) if !separated { @@ -174,6 +172,7 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI go integrations.NewVersion(util.ReWrapCtx(ctx), dbVersion) storage.DeleteModArch(ctx, mod.ID, mod.Name, versionID, "Combined") + storage.DeleteModArch(ctx, mod.ID, mod.Name, dbVersion.Version, "Combined") } else { l.Info().Msg("Submitting version job for virus scan") jobs.SubmitJobScanModOnVirusTotalTask(ctx, mod.ID, dbVersion.ID, true) diff --git a/integrations/discord.go b/integrations/discord.go index f2ea0aa..85cfc74 100644 --- a/integrations/discord.go +++ b/integrations/discord.go @@ -10,12 +10,12 @@ import ( "runtime/debug" "strings" - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/microcosm-cc/bluemonday" "github.com/rs/zerolog/log" "github.com/russross/blackfriday" "github.com/spf13/viper" + + "github.com/satisfactorymodding/smr-api/db/postgres" ) func NewMod(ctx context.Context, mod *postgres.Mod) { @@ -58,7 +58,6 @@ func NewMod(ctx context.Context, mod *postgres.Mod) { } payloadJSON, err := json.Marshal(payload) - if err != nil { log.Err(err).Msg("error marshaling discord webhook") return @@ -143,7 +142,6 @@ func NewVersion(ctx context.Context, version *postgres.Version) { } payloadJSON, err := json.Marshal(payload) - if err != nil { log.Err(err).Msg("error marshaling discord webhook") return diff --git a/migrations/code.go b/migrations/code.go index 556a593..cbb9c98 100644 --- a/migrations/code.go +++ b/migrations/code.go @@ -5,21 +5,21 @@ import ( "os" "strings" + "github.com/lab259/go-migration" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + postgres2 "github.com/satisfactorymodding/smr-api/db/postgres" // Import all migrations _ "github.com/satisfactorymodding/smr-api/migrations/code" - - "github.com/lab259/go-migration" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" ) type codeMigrationLogger struct { log *zerolog.Logger } -func (c codeMigrationLogger) Write(p []byte) (n int, err error) { +func (c codeMigrationLogger) Write(p []byte) (int, error) { message := strings.TrimRight(string(p), "\n") if len(message) > 0 { log.Info().Msg(message) diff --git a/migrations/code/20200426221900_test_new_migration.go b/migrations/code/20200426221900_test_new_migration.go index 158e2d0..936920e 100644 --- a/migrations/code/20200426221900_test_new_migration.go +++ b/migrations/code/20200426221900_test_new_migration.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200501224200_parse_paks.go b/migrations/code/20200501224200_parse_paks.go index 158e2d0..936920e 100644 --- a/migrations/code/20200501224200_parse_paks.go +++ b/migrations/code/20200501224200_parse_paks.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200524203800_after_reference_fix.go b/migrations/code/20200524203800_after_reference_fix.go index da0efdc..f9dbec1 100644 --- a/migrations/code/20200524203800_after_reference_fix.go +++ b/migrations/code/20200524203800_after_reference_fix.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200621195500_after_id_length_fix.go b/migrations/code/20200621195500_after_id_length_fix.go index da0efdc..f9dbec1 100644 --- a/migrations/code/20200621195500_after_id_length_fix.go +++ b/migrations/code/20200621195500_after_id_length_fix.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200622003600_after_validation_disable.go b/migrations/code/20200622003600_after_validation_disable.go index 158e2d0..936920e 100644 --- a/migrations/code/20200622003600_after_validation_disable.go +++ b/migrations/code/20200622003600_after_validation_disable.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200629093800_copy_to_new_bucket.go b/migrations/code/20200629093800_copy_to_new_bucket.go index 120b9d1..9e6aac6 100644 --- a/migrations/code/20200629093800_copy_to_new_bucket.go +++ b/migrations/code/20200629093800_copy_to_new_bucket.go @@ -3,11 +3,11 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/redis/jobs" - "github.com/satisfactorymodding/smr-api/storage" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/redis/jobs" + "github.com/satisfactorymodding/smr-api/storage" ) func init() { diff --git a/migrations/code/20200707150700_after_sml_version_fix.go b/migrations/code/20200707150700_after_sml_version_fix.go index 2937bc6..3b8e25b 100644 --- a/migrations/code/20200707150700_after_sml_version_fix.go +++ b/migrations/code/20200707150700_after_sml_version_fix.go @@ -3,11 +3,11 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200829171600_after_db_dirty.go b/migrations/code/20200829171600_after_db_dirty.go index 158e2d0..936920e 100644 --- a/migrations/code/20200829171600_after_db_dirty.go +++ b/migrations/code/20200829171600_after_db_dirty.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200829225100_after_broken_datajson.go b/migrations/code/20200829225100_after_broken_datajson.go index 3ecdc05..9dde468 100644 --- a/migrations/code/20200829225100_after_broken_datajson.go +++ b/migrations/code/20200829225100_after_broken_datajson.go @@ -3,11 +3,11 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20200830011200_after_gorm_hotfix.go b/migrations/code/20200830011200_after_gorm_hotfix.go index 158e2d0..936920e 100644 --- a/migrations/code/20200830011200_after_gorm_hotfix.go +++ b/migrations/code/20200830011200_after_gorm_hotfix.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20201014162200_after_bp_fix.go b/migrations/code/20201014162200_after_bp_fix.go index 158e2d0..936920e 100644 --- a/migrations/code/20201014162200_after_bp_fix.go +++ b/migrations/code/20201014162200_after_bp_fix.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20201016202600_after_body_enable.go b/migrations/code/20201016202600_after_body_enable.go index 158e2d0..936920e 100644 --- a/migrations/code/20201016202600_after_body_enable.go +++ b/migrations/code/20201016202600_after_body_enable.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/code/20201019203800_after_remove_filter.go b/migrations/code/20201019203800_after_remove_filter.go index 158e2d0..936920e 100644 --- a/migrations/code/20201019203800_after_remove_filter.go +++ b/migrations/code/20201019203800_after_remove_filter.go @@ -3,10 +3,10 @@ package code import ( "context" - "github.com/satisfactorymodding/smr-api/migrations/utils" - "github.com/lab259/go-migration" "github.com/rs/zerolog/log" + + "github.com/satisfactorymodding/smr-api/migrations/utils" ) func init() { diff --git a/migrations/migrations.go b/migrations/migrations.go index 9113424..3999c69 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -5,14 +5,14 @@ import ( "errors" "strings" - postgres2 "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/postgres" + "github.com/rs/zerolog/log" + + postgres2 "github.com/satisfactorymodding/smr-api/db/postgres" // Import migrations _ "github.com/golang-migrate/migrate/v4/source/file" - "github.com/rs/zerolog/log" ) func RunMigrations(ctx context.Context) { @@ -30,13 +30,11 @@ func SetMigrationDir(newMigrationDir string) { func databaseMigrations(ctx context.Context) { db, _ := postgres2.DBCtx(ctx).DB() driver, err := postgres.WithInstance(db, &postgres.Config{}) - if err != nil { panic(err) } m, err := migrate.NewWithDatabaseInstance("file://"+migrationDir+"/sql", "postgres", driver) - if err != nil { panic(err) } diff --git a/models/filters.go b/models/filters.go index 688f3a9..885e9ce 100644 --- a/models/filters.go +++ b/models/filters.go @@ -4,14 +4,13 @@ import ( "reflect" "strconv" - "github.com/satisfactorymodding/smr-api/generated" - - "github.com/pkg/errors" - "github.com/99designs/gqlgen/graphql" "github.com/mitchellh/hashstructure/v2" "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" "gopkg.in/go-playground/validator.v9" + + "github.com/satisfactorymodding/smr-api/generated" ) var dataValidator = validator.New() @@ -282,7 +281,6 @@ func ApplyChanges(changes interface{}, to interface{}) error { return v, nil }, }) - if err != nil { return errors.Wrap(err, "failed to create decoder") } diff --git a/nodes/mod.go b/nodes/mod.go index 4e1dda5..725fb10 100644 --- a/nodes/mod.go +++ b/nodes/mod.go @@ -4,12 +4,12 @@ import ( "strings" "time" + "github.com/labstack/echo/v4" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/redis" "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" - - "github.com/labstack/echo/v4" ) // @Summary Retrieve a list of Mods diff --git a/nodes/mod_types.go b/nodes/mod_types.go index 09df0e8..6cb453b 100644 --- a/nodes/mod_types.go +++ b/nodes/mod_types.go @@ -7,20 +7,20 @@ import ( ) type Mod struct { - ID string `json:"id"` - Name string `json:"name"` - ShortDescription string `json:"short_description"` + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` + CreatorID string `json:"creator_id"` FullDescription string `json:"full_description"` Logo string `json:"logo"` SourceURL string `json:"source_url"` - CreatorID string `json:"creator_id"` - Approved bool `json:"approved"` + ID string `json:"id"` + ShortDescription string `json:"short_description"` + Name string `json:"name"` Views uint `json:"views"` Downloads uint `json:"downloads"` Hotness uint `json:"hotness"` Popularity uint `json:"popularity"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` + Approved bool `json:"approved"` } func ModToMod(mod *postgres.Mod, short bool) *Mod { @@ -48,16 +48,16 @@ func ModToMod(mod *postgres.Mod, short bool) *Mod { } type Version struct { + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` ID string `json:"id"` Version string `json:"version"` SMLVersion string `json:"sml_version"` Changelog string `json:"changelog"` - Downloads uint `json:"downloads"` Stability string `json:"stability"` ModID string `json:"mod_id"` + Downloads uint `json:"downloads"` Approved bool `json:"approved"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` } func VersionToVersion(version *postgres.Version) *Version { @@ -88,16 +88,16 @@ func ModUserToModUser(userMod *postgres.UserMod) *ModUser { } type SMLVersion struct { + Date time.Time `json:"date"` + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` + BootstrapVersion *string `json:"bootstrap_version"` ID string `json:"id"` Version string `json:"version"` - SatisfactoryVersion int `json:"satisfactory_version"` - BootstrapVersion *string `json:"bootstrap_version"` Stability string `json:"stability"` - Date time.Time `json:"date"` Link string `json:"link"` Changelog string `json:"changelog"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` + SatisfactoryVersion int `json:"satisfactory_version"` } func SMLVersionToSMLVersion(version *postgres.SMLVersion) *SMLVersion { diff --git a/nodes/node_types.go b/nodes/node_types.go index b4380e0..c1a8a5f 100755 --- a/nodes/node_types.go +++ b/nodes/node_types.go @@ -1,14 +1,14 @@ package nodes type GenericResponse struct { - Success bool `json:"success"` Data interface{} `json:"data,omitempty"` Error interface{} `json:"error,omitempty"` + Success bool `json:"success"` } type ErrorResponse struct { - Code int `json:"code"` Message string `json:"message"` + Code int `json:"code"` Status int `json:"-"` } diff --git a/nodes/oauth.go b/nodes/oauth.go index fdae9e8..9b8877d 100644 --- a/nodes/oauth.go +++ b/nodes/oauth.go @@ -4,12 +4,12 @@ import ( "bytes" "net/url" + "github.com/labstack/echo/v4" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/oauth" "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" - - "github.com/labstack/echo/v4" ) // @Summary Retrieve a list of OAuth methods @@ -21,7 +21,6 @@ import ( func getOAuth(c echo.Context) (interface{}, *ErrorResponse) { callbackURL := c.Param("url") unescapedURL, err := url.PathUnescape(callbackURL) - if err != nil { return nil, GenericUserError(err) } @@ -51,7 +50,6 @@ func getGithub(c echo.Context) (interface{}, *ErrorResponse) { } user, err := oauth.GithubCallback(code, state) - if err != nil { return nil, GenericUserError(err) } @@ -65,7 +63,6 @@ func getGithub(c echo.Context) (interface{}, *ErrorResponse) { if avatarURL != "" && newUser { avatarData, err := util.LinkToWebp(c.Request().Context(), avatarURL) - if err != nil { return nil, GenericUserError(err) } diff --git a/nodes/shared.go b/nodes/shared.go index 2627e61..ddcd60f 100644 --- a/nodes/shared.go +++ b/nodes/shared.go @@ -1,9 +1,9 @@ package nodes import ( - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/labstack/echo/v4" + + "github.com/satisfactorymodding/smr-api/db/postgres" ) type DataFunction func(c echo.Context) (data interface{}, err *ErrorResponse) @@ -11,7 +11,6 @@ type DataFunction func(c echo.Context) (data interface{}, err *ErrorResponse) func dataWrapper(nested DataFunction) func(c echo.Context) error { return func(c echo.Context) error { data, err := nested(c) - if err != nil { return c.JSON(err.Status, GenericResponse{ Success: false, @@ -29,7 +28,7 @@ func dataWrapper(nested DataFunction) func(c echo.Context) error { type AuthorizedDataFunction func(user *postgres.User, c echo.Context) (data interface{}, err *ErrorResponse) func authorized(nested AuthorizedDataFunction) DataFunction { - return func(c echo.Context) (data interface{}, err *ErrorResponse) { + return func(c echo.Context) (interface{}, *ErrorResponse) { user := userFromContext(c) if user == nil { diff --git a/nodes/sml.go b/nodes/sml.go index 497e83b..cc983dc 100644 --- a/nodes/sml.go +++ b/nodes/sml.go @@ -2,6 +2,7 @@ package nodes import ( "github.com/labstack/echo/v4" + "github.com/satisfactorymodding/smr-api/db/postgres" ) diff --git a/nodes/user.go b/nodes/user.go index c80e6e9..a03ba67 100644 --- a/nodes/user.go +++ b/nodes/user.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/labstack/echo/v4" + "github.com/satisfactorymodding/smr-api/db/postgres" ) diff --git a/nodes/user_types.go b/nodes/user_types.go index 08f526a..f929f53 100644 --- a/nodes/user_types.go +++ b/nodes/user_types.go @@ -7,11 +7,11 @@ import ( ) type User struct { + CreatedAt time.Time `json:"created_at"` ID string `json:"id"` Email string `json:"email"` Username string `json:"username"` Avatar string `json:"avatar"` - CreatedAt time.Time `json:"created_at"` } func UserToPrivateUser(user *postgres.User) *User { @@ -25,10 +25,10 @@ func UserToPrivateUser(user *postgres.User) *User { } type PublicUser struct { + CreatedAt time.Time `json:"created_at"` ID string `json:"id"` Username string `json:"username"` Avatar string `json:"avatar"` - CreatedAt time.Time `json:"created_at"` } func UserToPublicUser(user *postgres.User) *PublicUser { diff --git a/nodes/version.go b/nodes/version.go index b8ab977..178064f 100644 --- a/nodes/version.go +++ b/nodes/version.go @@ -4,6 +4,7 @@ import ( "time" "github.com/labstack/echo/v4" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/redis" "github.com/satisfactorymodding/smr-api/storage" diff --git a/oauth/facebook.go b/oauth/facebook.go index 69c27cb..3aa9fb3 100644 --- a/oauth/facebook.go +++ b/oauth/facebook.go @@ -5,13 +5,13 @@ import ( "io" "github.com/pkg/errors" - "github.com/satisfactorymodding/smr-api/redis" "golang.org/x/oauth2" + + "github.com/satisfactorymodding/smr-api/redis" ) func FacebookCallback(code string, state string) (*UserData, error) { redirectURI, err := redis.GetNonce(state) - if err != nil { return nil, errors.New("login expired") } @@ -19,7 +19,6 @@ func FacebookCallback(code string, state string) (*UserData, error) { urlParam := oauth2.SetAuthURLParam("redirect_uri", redirectURI) token, err := facebookAuth.Exchange(ctx, code, urlParam) - if err != nil { return nil, errors.Wrap(err, "failed to exchange code") } @@ -27,13 +26,11 @@ func FacebookCallback(code string, state string) (*UserData, error) { client := facebookAuth.Client(ctx, token) resp, err := client.Get("https://graph.facebook.com/v5.0/me?fields=email,short_name,id,picture{url}") - if err != nil { return nil, errors.Wrap(err, "failed to get user data") } bytes, err := io.ReadAll(resp.Body) - if err != nil { return nil, errors.Wrap(err, "failed to read response body") } diff --git a/oauth/github.go b/oauth/github.go index 581f410..7e479e9 100644 --- a/oauth/github.go +++ b/oauth/github.go @@ -5,20 +5,18 @@ import ( "io" "strconv" - "github.com/satisfactorymodding/smr-api/redis" - "github.com/pkg/errors" + + "github.com/satisfactorymodding/smr-api/redis" ) func GithubCallback(code string, state string) (*UserData, error) { _, err := redis.GetNonce(state) - if err != nil { return nil, errors.New("login expired") } token, err := githubAuth.Exchange(ctx, code) - if err != nil { return nil, errors.Wrap(err, "failed to exchange code") } @@ -26,13 +24,11 @@ func GithubCallback(code string, state string) (*UserData, error) { client := githubAuth.Client(ctx, token) resp, err := client.Get("https://api.github.com/user") - if err != nil { return nil, errors.Wrap(err, "failed to get user data") } bytes, err := io.ReadAll(resp.Body) - if err != nil { return nil, errors.Wrap(err, "failed to read user data") } diff --git a/oauth/google.go b/oauth/google.go index c23494b..beab7e2 100644 --- a/oauth/google.go +++ b/oauth/google.go @@ -4,23 +4,20 @@ import ( "encoding/json" "io" - "github.com/satisfactorymodding/smr-api/redis" - "github.com/pkg/errors" - "github.com/spf13/viper" "golang.org/x/oauth2" + + "github.com/satisfactorymodding/smr-api/redis" ) func GoogleCallback(code string, state string) (*UserData, error) { _, err := redis.GetNonce(state) - if err != nil { return nil, errors.New("login expired") } token, err := googleAuth.Exchange(ctx, code, oauth2.SetAuthURLParam("redirect_uri", viper.GetString("frontend.url"))) - if err != nil { return nil, errors.Wrap(err, "failed to exchange code") } @@ -28,13 +25,11 @@ func GoogleCallback(code string, state string) (*UserData, error) { client := googleAuth.Client(ctx, token) resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") - if err != nil { return nil, errors.Wrap(err, "failed to get user info") } bytes, err := io.ReadAll(resp.Body) - if err != nil { return nil, errors.Wrap(err, "failed to read user info") } diff --git a/oauth/oauth.go b/oauth/oauth.go index b12be4b..9c9ad39 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -3,21 +3,23 @@ package oauth import ( "context" - "github.com/satisfactorymodding/smr-api/redis" - "github.com/satisfactorymodding/smr-api/util" - "github.com/spf13/viper" "golang.org/x/oauth2" "golang.org/x/oauth2/facebook" "golang.org/x/oauth2/github" "golang.org/x/oauth2/google" + + "github.com/satisfactorymodding/smr-api/redis" + "github.com/satisfactorymodding/smr-api/util" ) var ctx = context.Background() -var githubAuth *oauth2.Config -var googleAuth *oauth2.Config -var facebookAuth *oauth2.Config +var ( + githubAuth *oauth2.Config + googleAuth *oauth2.Config + facebookAuth *oauth2.Config +) type Site string diff --git a/redis/jobs/consumers/consumer_copy_object_from_old_bucket.go b/redis/jobs/consumers/consumer_copy_object_from_old_bucket.go index 2767d12..e86cbb5 100644 --- a/redis/jobs/consumers/consumer_copy_object_from_old_bucket.go +++ b/redis/jobs/consumers/consumer_copy_object_from_old_bucket.go @@ -3,12 +3,11 @@ package consumers import ( "encoding/json" - "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" - "github.com/satisfactorymodding/smr-api/storage" - "github.com/pkg/errors" - "github.com/vmihailenco/taskq/v3" + + "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" + "github.com/satisfactorymodding/smr-api/storage" ) func init() { diff --git a/redis/jobs/consumers/consumer_copy_object_to_old_bucket.go b/redis/jobs/consumers/consumer_copy_object_to_old_bucket.go index 2ce9c05..49b1fdb 100644 --- a/redis/jobs/consumers/consumer_copy_object_to_old_bucket.go +++ b/redis/jobs/consumers/consumer_copy_object_to_old_bucket.go @@ -3,12 +3,11 @@ package consumers import ( "encoding/json" - "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" - "github.com/satisfactorymodding/smr-api/storage" - "github.com/pkg/errors" - "github.com/vmihailenco/taskq/v3" + + "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" + "github.com/satisfactorymodding/smr-api/storage" ) func init() { diff --git a/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go b/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go index f024084..b3d7a76 100644 --- a/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go +++ b/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go @@ -10,17 +10,16 @@ import ( "path" "time" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/vmihailenco/taskq/v3" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/integrations" "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/util" "github.com/satisfactorymodding/smr-api/validation" - - "github.com/pkg/errors" - - "github.com/rs/zerolog/log" - "github.com/vmihailenco/taskq/v3" ) func init() { @@ -47,13 +46,11 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { response, _ := http.Get(link) fileData, err := io.ReadAll(response.Body) - if err != nil { return errors.Wrap(err, "failed to read mod file") } archive, err := zip.NewReader(bytes.NewReader(fileData), int64(len(fileData))) - if err != nil { return errors.Wrap(err, "failed to unzip mod file") } @@ -63,7 +60,6 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { for _, file := range archive.File { if path.Ext(file.Name) == ".dll" || path.Ext(file.Name) == ".so" { open, err := file.Open() - if err != nil { return errors.Wrap(err, "failed to open mod file") } @@ -74,7 +70,6 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { } success, err := validation.ScanFiles(ctx, toScan, names) - if err != nil { return err } @@ -98,6 +93,7 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { } go storage.DeleteModArch(ctx, mod.ID, mod.Name, version.ID, "Combined") + go storage.DeleteModArch(ctx, mod.ID, mod.Name, version.Version, "Combined") go postgres.Delete(ctx, modArch) return nil diff --git a/redis/jobs/consumers/consumer_update_db_from_mod_version_file.go b/redis/jobs/consumers/consumer_update_db_from_mod_version_file.go index 6a72370..b6670cb 100644 --- a/redis/jobs/consumers/consumer_update_db_from_mod_version_file.go +++ b/redis/jobs/consumers/consumer_update_db_from_mod_version_file.go @@ -4,11 +4,10 @@ import ( "context" "encoding/json" - "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" - "github.com/pkg/errors" - "github.com/vmihailenco/taskq/v3" + + "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" ) func init() { diff --git a/redis/jobs/consumers/consumer_update_db_from_mod_version_json_file.go b/redis/jobs/consumers/consumer_update_db_from_mod_version_json_file.go index ad4c859..d392d83 100644 --- a/redis/jobs/consumers/consumer_update_db_from_mod_version_json_file.go +++ b/redis/jobs/consumers/consumer_update_db_from_mod_version_json_file.go @@ -4,11 +4,10 @@ import ( "context" "encoding/json" - "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" - "github.com/pkg/errors" - "github.com/vmihailenco/taskq/v3" + + "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" ) func init() { diff --git a/redis/jobs/consumers/utils.go b/redis/jobs/consumers/utils.go index 042c87f..9a8ebab 100644 --- a/redis/jobs/consumers/utils.go +++ b/redis/jobs/consumers/utils.go @@ -6,13 +6,12 @@ import ( "io" "net/http" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/satisfactorymodding/smr-api/db/postgres" "github.com/satisfactorymodding/smr-api/storage" "github.com/satisfactorymodding/smr-api/validation" - - "github.com/pkg/errors" - - "github.com/rs/zerolog/log" ) func UpdateModDataFromStorage(ctx context.Context, modID string, versionID string, metadata bool) error { @@ -25,7 +24,6 @@ func UpdateModDataFromStorage(ctx context.Context, modID string, versionID strin response, _ := http.Get(link) fileData, err := io.ReadAll(response.Body) - if err != nil { return errors.Wrap(err, "failed to read response body") } @@ -37,7 +35,6 @@ func UpdateModDataFromStorage(ctx context.Context, modID string, versionID strin } info, err := validation.ExtractModInfo(ctx, fileData, metadata, false, mod.ModReference) - if err != nil { log.Warn().Err(err).Msgf("[%s] Failed updating mod, likely outdated", versionID) // Outdated version diff --git a/redis/jobs/jobs.go b/redis/jobs/jobs.go index d0a70e2..0ce35aa 100644 --- a/redis/jobs/jobs.go +++ b/redis/jobs/jobs.go @@ -6,14 +6,14 @@ import ( "fmt" "time" - "github.com/vmihailenco/taskq/extra/taskqotel/v3" - "github.com/go-redis/redis/v8" "github.com/rs/zerolog/log" - "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" "github.com/spf13/viper" + "github.com/vmihailenco/taskq/extra/taskqotel/v3" "github.com/vmihailenco/taskq/v3" "github.com/vmihailenco/taskq/v3/redisq" + + "github.com/satisfactorymodding/smr-api/redis/jobs/tasks" ) var queue taskq.Queue @@ -53,7 +53,6 @@ func SubmitJobUpdateDBFromModVersionFileTask(ctx context.Context, modID string, }) err := queue.Add(tasks.UpdateDBFromModVersionFileTask.WithArgs(ctx, task)) - if err != nil { log.Err(err).Msg("error adding task") } @@ -66,7 +65,6 @@ func SubmitJobUpdateDBFromModVersionJSONFileTask(ctx context.Context, modID stri }) err := queue.Add(tasks.UpdateDBFromModVersionJSONFileTask.WithArgs(ctx, task)) - if err != nil { log.Err(err).Msg("error adding task") } @@ -78,7 +76,6 @@ func SubmitJobCopyObjectFromOldBucketTask(ctx context.Context, key string) { }) err := queue.Add(tasks.CopyObjectFromOldBucketTask.WithArgs(ctx, task)) - if err != nil { log.Err(err).Msg("error adding task") } @@ -90,7 +87,6 @@ func SubmitJobCopyObjectToOldBucketTask(ctx context.Context, key string) { }) err := queue.Add(tasks.CopyObjectToOldBucketTask.WithArgs(ctx, task)) - if err != nil { log.Err(err).Msg("error adding task") } @@ -104,7 +100,6 @@ func SubmitJobScanModOnVirusTotalTask(ctx context.Context, modID string, version }) err := queue.Add(tasks.ScanModOnVirusTotalTask.WithArgs(ctx, task)) - if err != nil { log.Err(err).Msg("error adding task") } diff --git a/redis/jobs/tasks/tasks.go b/redis/jobs/tasks/tasks.go index 34b71c6..77b199c 100755 --- a/redis/jobs/tasks/tasks.go +++ b/redis/jobs/tasks/tasks.go @@ -2,11 +2,13 @@ package tasks import "github.com/vmihailenco/taskq/v3" -var UpdateDBFromModVersionFileTask *taskq.Task -var UpdateDBFromModVersionJSONFileTask *taskq.Task -var CopyObjectFromOldBucketTask *taskq.Task -var CopyObjectToOldBucketTask *taskq.Task -var ScanModOnVirusTotalTask *taskq.Task +var ( + UpdateDBFromModVersionFileTask *taskq.Task + UpdateDBFromModVersionJSONFileTask *taskq.Task + CopyObjectFromOldBucketTask *taskq.Task + CopyObjectToOldBucketTask *taskq.Task + ScanModOnVirusTotalTask *taskq.Task +) type UpdateDBFromModVersionFileData struct { ModID string `json:"mod_id"` diff --git a/redis/redis.go b/redis/redis.go index 052facc..d5b829c 100644 --- a/redis/redis.go +++ b/redis/redis.go @@ -9,14 +9,13 @@ import ( "strconv" "time" - "github.com/satisfactorymodding/smr-api/generated" - - "github.com/pkg/errors" - "github.com/cespare/xxhash" "github.com/go-redis/redis" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" + + "github.com/satisfactorymodding/smr-api/generated" ) var client *redis.Client diff --git a/storage/b2.go b/storage/b2.go index 98d574c..10ec7ad 100644 --- a/storage/b2.go +++ b/storage/b2.go @@ -7,16 +7,15 @@ import ( "strconv" "strings" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/satisfactorymodding/smr-api/redis" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" ) type B2 struct { @@ -35,7 +34,6 @@ func initializeB2(ctx context.Context, config Config) *B2 { } newSession, err := session.NewSession(s3Config) - if err != nil { log.Err(err).Msg("failed to create S3 session") return nil @@ -58,7 +56,6 @@ func (b2o *B2) Get(key string) (io.ReadCloser, error) { Bucket: aws.String(b2o.Config.Bucket), Key: aws.String(cleanedKey), }) - if err != nil { return nil, errors.Wrap(err, "failed to get object") } @@ -75,7 +72,6 @@ func (b2o *B2) Put(ctx context.Context, key string, body io.ReadSeeker) (string, Bucket: aws.String(b2o.Config.Bucket), Key: aws.String(cleanedKey), }) - if err != nil { return cleanedKey, errors.Wrap(err, "failed to upload file") } @@ -100,7 +96,6 @@ func (b2o *B2) StartMultipartUpload(key string) error { Bucket: aws.String(b2o.Config.Bucket), Key: aws.String(cleanedKey), }) - if err != nil { return errors.Wrap(err, "failed to create multipart upload") } @@ -121,7 +116,6 @@ func (b2o *B2) UploadPart(key string, part int64, data io.ReadSeeker) error { PartNumber: aws.Int64(part), UploadId: aws.String(id), }) - if err != nil { return errors.Wrap(err, "failed to upload part") } @@ -173,7 +167,6 @@ func (b2o *B2) Delete(key string) error { KeyMarker: aws.String(cleanedKey), Prefix: aws.String(cleanedKey), }) - if err != nil { return errors.Wrap(err, "failed to list object versions") } @@ -220,7 +213,6 @@ func (b2o *B2) Meta(key string) (*ObjectMeta, error) { Bucket: aws.String(b2o.Config.Bucket), Key: aws.String(cleanedKey), }) - if err != nil { return nil, errors.Wrap(err, "failed to get object meta") } diff --git a/storage/s3.go b/storage/s3.go index 4a2b6d3..d8c09c7 100644 --- a/storage/s3.go +++ b/storage/s3.go @@ -7,17 +7,16 @@ import ( "strconv" "strings" - "github.com/satisfactorymodding/smr-api/redis" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" + "github.com/satisfactorymodding/smr-api/redis" ) type S3 struct { @@ -36,7 +35,6 @@ func initializeS3(ctx context.Context, config Config) *S3 { } newSession, err := session.NewSession(s3Config) - if err != nil { log.Err(err).Msg("failed to create S3 session") return nil @@ -59,7 +57,6 @@ func (s3o *S3) Get(key string) (io.ReadCloser, error) { Bucket: aws.String(s3o.Config.Bucket), Key: aws.String(cleanedKey), }) - if err != nil { return nil, errors.Wrap(err, "failed to get object") } @@ -77,7 +74,6 @@ func (s3o *S3) Put(ctx context.Context, key string, body io.ReadSeeker) (string, Bucket: aws.String(viper.GetString("storage.bucket")), Key: aws.String(cleanedKey), }) - if err != nil { return cleanedKey, errors.Wrap(err, "failed to upload file") } @@ -88,7 +84,7 @@ func (s3o *S3) Put(ctx context.Context, key string, body io.ReadSeeker) (string, func (s3o *S3) SignGet(key string) (string, error) { // Public Bucket cleanedKey := strings.TrimPrefix(key, "/") - return fmt.Sprintf("%s/file/%s/%s", s3o.BaseURL, viper.GetString("storage.bucket"), cleanedKey), nil + return fmt.Sprintf(viper.GetString("storage.keypath"), s3o.BaseURL, viper.GetString("storage.bucket"), cleanedKey), nil } func (s3o *S3) SignPut(key string) (string, error) { @@ -102,7 +98,6 @@ func (s3o *S3) StartMultipartUpload(key string) error { Bucket: aws.String(viper.GetString("storage.bucket")), Key: aws.String(cleanedKey), }) - if err != nil { return errors.Wrap(err, "failed to create multipart upload") } @@ -123,7 +118,6 @@ func (s3o *S3) UploadPart(key string, part int64, data io.ReadSeeker) error { PartNumber: aws.Int64(part), UploadId: aws.String(id), }) - if err != nil { return errors.Wrap(err, "failed to upload part") } @@ -175,8 +169,20 @@ func (s3o *S3) Delete(key string) error { KeyMarker: aws.String(cleanedKey), Prefix: aws.String(cleanedKey), }) - if err != nil { + if strings.Contains(err.Error(), "NotImplemented") { + _, err = s3o.S3Client.DeleteObject(&s3.DeleteObjectInput{ + Bucket: aws.String(viper.GetString("storage.bucket")), + Key: aws.String(cleanedKey), + }) + + if err != nil { + return errors.Wrap(err, "failed to delete objects") + } + + return nil + } + return errors.Wrap(err, "failed to list object versions") } @@ -222,7 +228,6 @@ func (s3o *S3) Meta(key string) (*ObjectMeta, error) { Bucket: aws.String(viper.GetString("storage.bucket")), Key: aws.String(cleanedKey), }) - if err != nil { return nil, errors.Wrap(err, "failed to get object meta") } diff --git a/storage/storage.go b/storage/storage.go index ea4acdd..af214dd 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -11,12 +11,11 @@ import ( "strings" "github.com/avast/retry-go/v3" - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" "github.com/spf13/viper" + + "github.com/satisfactorymodding/smr-api/db/postgres" ) type Storage interface { @@ -144,7 +143,6 @@ func UploadModLogo(ctx context.Context, modID string, data io.ReadSeeker) (bool, key := fmt.Sprintf("/images/mods/%s/logo.webp", modID) key, err := storage.Put(ctx, key, data) - if err != nil { log.Err(err).Msg("failed to upload mod logo") return false, "" @@ -172,7 +170,6 @@ func UploadUserAvatar(ctx context.Context, userID string, data io.ReadSeeker) (b log.Err(err).Msgf("failed to upload user avatar, retrying [%d]", n) }), ) - if err != nil { log.Err(err).Msg("failed to upload user avatar") return false, "" @@ -187,7 +184,6 @@ func GenerateDownloadLink(key string) string { } url, err := storage.SignGet(key) - if err != nil { return "" } @@ -285,6 +281,8 @@ func DeleteVersion(ctx context.Context, modID string, name string, versionID str cleanName := cleanModName(name) key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+versionID) + + log.Info().Str("key", key).Msg("deleting version") if err := storage.Delete(key); err != nil { log.Err(err).Msg("failed to delete version") return false @@ -302,7 +300,7 @@ func DeleteMod(ctx context.Context, modID string, name string, versionID string) query := postgres.GetModVersion(ctx, modID, versionID) - if len(query.Arch) != 0 { + if query != nil && len(query.Arch) != 0 { for _, link := range query.Arch { if success := DeleteModArch(ctx, modID, cleanName, versionID, link.Platform); !success { return false @@ -311,6 +309,7 @@ func DeleteMod(ctx context.Context, modID string, name string, versionID string) } else { key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+versionID) + log.Info().Str("key", key).Msg("deleting mod") if err := storage.Delete(key); err != nil { log.Ctx(ctx).Err(err).Msg("failed to delete version") return false @@ -328,6 +327,7 @@ func DeleteModArch(ctx context.Context, modID string, name string, versionID str cleanName := cleanModName(name) key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+platform+"-"+versionID) + log.Info().Str("key", key).Msg("deleting mod arch") if err := storage.Delete(key); err != nil { log.Ctx(ctx).Err(err).Msg("failed to delete version link") return false @@ -397,9 +397,7 @@ func EncodeName(name string) string { } func SeparateMod(ctx context.Context, body []byte, modID, name string, versionID string, modVersion string) bool { - //read combined file zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) - if err != nil { return false } @@ -444,14 +442,12 @@ func SeparateMod(ctx context.Context, body []byte, modID, name string, versionID func WriteZipFile(ctx context.Context, file *zip.File, platform string, zipWriter *zip.Writer) error { fileName := strings.ReplaceAll(file.Name, platform+"/", "") zipFile, err := zipWriter.Create(fileName) - if err != nil { log.Ctx(ctx).Err(err).Msg("Failed to create smod file for " + platform) return errors.Wrap(err, "Failed to open smod file for "+platform) } rawFile, err := file.Open() - if err != nil { log.Ctx(ctx).Err(err).Msg("Failed to open smod file for " + platform) return errors.Wrap(err, "Failed to open smod file for "+platform) @@ -477,7 +473,6 @@ func WriteZipFile(ctx context.Context, file *zip.File, platform string, zipWrite func WriteModArch(ctx context.Context, key string, versionID string, platform string, buffer *bytes.Buffer) error { _, err := storage.Put(ctx, key, bytes.NewReader(buffer.Bytes())) - if err != nil { log.Ctx(ctx).Err(err).Msg("failed to write smod: " + key) return errors.Wrap(err, "Failed to load smod:"+key) diff --git a/storage/wasabi.go b/storage/wasabi.go index 3a9e957..3a0a015 100644 --- a/storage/wasabi.go +++ b/storage/wasabi.go @@ -5,12 +5,11 @@ import ( "io" "time" - "github.com/pkg/errors" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -30,7 +29,6 @@ func initializeWasabi(ctx context.Context, config Config) *Wasabi { } newSession, err := session.NewSession(s3Config) - if err != nil { log.Err(err).Msg("failed to create session") return nil @@ -49,7 +47,6 @@ func (wasabi *Wasabi) Get(key string) (io.ReadCloser, error) { Bucket: wasabi.Bucket, Key: aws.String(key), }) - if err != nil { return nil, errors.Wrap(err, "failed to get object") } @@ -63,7 +60,6 @@ func (wasabi *Wasabi) Put(ctx context.Context, key string, body io.ReadSeeker) ( Bucket: wasabi.Bucket, Key: aws.String(key), }) - if err != nil { return key, errors.Wrap(err, "failed to put object") } @@ -78,7 +74,6 @@ func (wasabi *Wasabi) SignGet(key string) (string, error) { }) urlStr, err := req.Presign(15 * time.Minute) - if err != nil { return "", errors.Wrap(err, "failed to sign url") } @@ -93,7 +88,6 @@ func (wasabi *Wasabi) SignPut(key string) (string, error) { }) urlStr, err := req.Presign(15 * time.Minute) - if err != nil { return "", errors.Wrap(err, "failed to sign url") } diff --git a/util/analytics.go b/util/analytics.go index 8804533..0b92639 100755 --- a/util/analytics.go +++ b/util/analytics.go @@ -25,13 +25,13 @@ func serveReverseProxy(target string, res http.ResponseWriter, req *http.Request req.URL.RawQuery = query.Encode() - req.URL.Path = strings.Replace(req.URL.Path, "analytics/cozzect", "collect", -1) - req.URL.Path = strings.Replace(req.URL.Path, "analytics/r/cozzect", "r/collect", -1) - req.URL.Path = strings.Replace(req.URL.Path, "analytics/j/cozzect", "j/collect", -1) + req.URL.Path = strings.ReplaceAll(req.URL.Path, "analytics/cozzect", "collect") + req.URL.Path = strings.ReplaceAll(req.URL.Path, "analytics/r/cozzect", "r/collect") + req.URL.Path = strings.ReplaceAll(req.URL.Path, "analytics/j/cozzect", "j/collect") - req.RequestURI = strings.Replace(req.RequestURI, "analytics/cozzect", "collect", -1) - req.RequestURI = strings.Replace(req.RequestURI, "analytics/r/cozzect", "r/collect", -1) - req.RequestURI = strings.Replace(req.RequestURI, "analytics/j/cozzect", "j/collect", -1) + req.RequestURI = strings.ReplaceAll(req.RequestURI, "analytics/cozzect", "collect") + req.RequestURI = strings.ReplaceAll(req.RequestURI, "analytics/r/cozzect", "r/collect") + req.RequestURI = strings.ReplaceAll(req.RequestURI, "analytics/j/cozzect", "j/collect") req.Header.Set("X-Forwarded-Host", req.Header.Get("Host")) req.Host = redirectURL.Host diff --git a/util/converter/converter_linux.go b/util/converter/converter_linux.go index 0687354..c4b48c8 100644 --- a/util/converter/converter_linux.go +++ b/util/converter/converter_linux.go @@ -5,26 +5,23 @@ import ( "context" "image" + "github.com/chai2010/webp" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + giftowebp "github.com/sizeofint/gif-to-webp" + // GIF Support _ "image/gif" - // JPEG Support _ "image/jpeg" - // PNG Support _ "image/png" - - "github.com/chai2010/webp" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" - giftowebp "github.com/sizeofint/gif-to-webp" ) var converter = giftowebp.NewConverter() func ConvertAnyImageToWebp(ctx context.Context, imageAsBytes []byte) ([]byte, error) { imageData, imageType, err := image.Decode(bytes.NewReader(imageAsBytes)) - if err != nil { message := "error converting image to webp" log.Err(err).Msg(message) @@ -35,7 +32,6 @@ func ConvertAnyImageToWebp(ctx context.Context, imageAsBytes []byte) ([]byte, er if imageType == "gif" { webpBin, err := converter.Convert(imageAsBytes) - if err != nil { message := "error converting image to webp" log.Err(err).Msg(message) diff --git a/util/image.go b/util/image.go index eddbe7a..65f8cdb 100644 --- a/util/image.go +++ b/util/image.go @@ -2,17 +2,19 @@ package util import ( "context" + "io" + "net/http" + + "github.com/pkg/errors" + + "github.com/satisfactorymodding/smr-api/util/converter" + // GIF Support _ "image/gif" // JPEG Support _ "image/jpeg" // PNG Support _ "image/png" - "io" - "net/http" - - "github.com/pkg/errors" - "github.com/satisfactorymodding/smr-api/util/converter" ) func LinkToWebp(ctx context.Context, url string) ([]byte, error) { @@ -27,7 +29,6 @@ func LinkToWebp(ctx context.Context, url string) ([]byte, error) { } imageAsBytes, err := io.ReadAll(resp.Body) - if err != nil { return nil, errors.New("invalid url") } diff --git a/util/random.go b/util/random.go index 0a2ab87..f04696b 100644 --- a/util/random.go +++ b/util/random.go @@ -12,8 +12,10 @@ import ( var seededRand = rand.New(rand.NewSource(time.Now().UnixNano())) -var randBuffer = [4]byte{} -var randMutex = sync.Mutex{} +var ( + randBuffer = [4]byte{} + randMutex = sync.Mutex{} +) const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" diff --git a/util/security.go b/util/security.go index 537d56c..56590c2 100644 --- a/util/security.go +++ b/util/security.go @@ -9,8 +9,10 @@ import ( "golang.org/x/crypto/ed25519" ) -var privateKey ed25519.PrivateKey -var pasetoV2 *paseto.V2 +var ( + privateKey ed25519.PrivateKey + pasetoV2 *paseto.V2 +) func InitializeSecurity() { var err error @@ -29,7 +31,6 @@ func GenerateUserToken() string { } token, err := pasetoV2.Sign(privateKey, jsonToken, nil) - if err != nil { panic(err) } diff --git a/validation/paks.go b/validation/paks.go index 3e758e6..6996eae 100644 --- a/validation/paks.go +++ b/validation/paks.go @@ -7,10 +7,10 @@ import ( "strings" "github.com/Vilsol/ue4pak/parser" + "github.com/rs/zerolog/log" // Import satisfactory-specific types _ "github.com/Vilsol/ue4pak/parser/games/satisfactory" - "github.com/rs/zerolog/log" ) var classInheritance = map[string]string{ @@ -168,14 +168,15 @@ var classInheritance = map[string]string{ "BodySetup": "", } -func AttemptExtractDataFromPak(ctx context.Context, reader parser.PakReader) (data map[string]map[string][]interface{}, err error) { +func AttemptExtractDataFromPak(ctx context.Context, reader parser.PakReader) (map[string]map[string][]interface{}, error) { + var err error defer func() { if r := recover(); r != nil { err = fmt.Errorf("%s\n%s", r, string(debug.Stack())) } }() - return ExtractDataFromPak(ctx, reader), nil + return ExtractDataFromPak(ctx, reader), err } // TODO Extract Images diff --git a/validation/paks_test.go b/validation/paks_test.go index d4167cb..2a8bbc3 100644 --- a/validation/paks_test.go +++ b/validation/paks_test.go @@ -32,7 +32,6 @@ func TestExtractDataFromPak(t *testing.T) { fmt.Println("Parsing file:", f) data, err := os.ReadFile(f) - if err != nil { panic(err) } @@ -49,7 +48,7 @@ func TestExtractDataFromPak(t *testing.T) { } else { marshal, _ := json.MarshalIndent(pakData, "", " ") - if err := os.WriteFile(f+".json", marshal, 0644); err != nil { + if err := os.WriteFile(f+".json", marshal, 0o644); err != nil { t.Error(err) } } diff --git a/validation/validation.go b/validation/validation.go index f58e9fa..839317b 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -26,29 +26,30 @@ type ModObject struct { } type ModInfo struct { + Dependencies map[string]string `json:"dependencies"` + OptionalDependencies map[string]string `json:"optional_dependencies"` + Semver *semver.Version `json:"-"` ModReference string `json:"mod_reference"` Version string `json:"version"` + Hash string `json:"-"` + SMLVersion string `json:"sml_version"` Objects []ModObject `json:"objects"` - Dependencies map[string]string `json:"dependencies"` - OptionalDependencies map[string]string `json:"optional_dependencies"` Metadata []map[string]map[string][]interface{} `json:"-"` Size int64 `json:"-"` - Hash string `json:"-"` - Semver *semver.Version `json:"-"` - SMLVersion string `json:"sml_version"` } -var dataJSONSchema gojsonschema.JSONLoader -var uPluginJSONSchema gojsonschema.JSONLoader +var ( + dataJSONSchema gojsonschema.JSONLoader + uPluginJSONSchema gojsonschema.JSONLoader +) func InitializeValidator() { absPath, err := filepath.Abs("static/data-json-schema.json") - if err != nil { panic(err) } - dataJSONSchema = gojsonschema.NewReferenceLoader("file://" + strings.Replace(absPath, "\\", "/", -1)) + dataJSONSchema = gojsonschema.NewReferenceLoader("file://" + strings.ReplaceAll(absPath, "\\", "/")) absPath, err = filepath.Abs("static/uplugin-json-schema.json") @@ -56,7 +57,7 @@ func InitializeValidator() { panic(err) } - uPluginJSONSchema = gojsonschema.NewReferenceLoader("file://" + strings.Replace(absPath, "\\", "/", -1)) + uPluginJSONSchema = gojsonschema.NewReferenceLoader("file://" + strings.ReplaceAll(absPath, "\\", "/")) } func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withValidation bool, modReference string) (*ModInfo, error) { @@ -65,7 +66,6 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal } archive, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) - if err != nil { return nil, errors.New("invalid zip archive") } @@ -112,14 +112,12 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal for _, archiveFile := range archive.File { if obj.Path == archiveFile.Name { data, err := archiveFile.Open() - if err != nil { log.Err(err).Msg("failed opening archive file") break } pakData, err := io.ReadAll(data) - if err != nil { log.Err(err).Msg("failed reading archive file") break @@ -130,7 +128,6 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal } pak, err := AttemptExtractDataFromPak(ctx, reader) - if err != nil { log.Err(err).Msg("failed parsing archive file") break @@ -156,7 +153,6 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal modInfo.Hash = hex.EncodeToString(hash.Sum(nil)) version, err := semver.StrictNewVersion(modInfo.Version) - if err != nil { log.Err(err).Msg("error parsing semver") return nil, errors.Wrap(err, "error parsing semver") @@ -178,13 +174,11 @@ func validateDataJSON(archive *zip.Reader, dataFile *zip.File, withValidation bo } dataJSON, err := io.ReadAll(rc) - if err != nil { return nil, errors.New("invalid zip archive") } result, err := gojsonschema.Validate(dataJSONSchema, gojsonschema.NewBytesLoader(dataJSON)) - if err != nil { return nil, errors.New("data.json doesn't follow schema. please view the help page. (" + err.Error() + ")") } @@ -255,15 +249,15 @@ func validateDataJSON(archive *zip.Reader, dataFile *zip.File, withValidation bo type UPlugin struct { SemVersion *string `json:"SemVersion"` - Version int64 `json:"Version"` Plugins []Plugin `json:"Plugins"` + Version int64 `json:"Version"` } type Plugin struct { - Name string `json:"Name"` - SemVersion string `json:"SemVersion"` BIsBasePlugin *bool `json:"bIsBasePlugin"` BIsOptional *bool `json:"bIsOptional"` + Name string `json:"Name"` + SemVersion string `json:"SemVersion"` } func validateUPluginJSON(archive *zip.Reader, uPluginFile *zip.File, withValidation bool, modReference string) (*ModInfo, error) { @@ -277,13 +271,11 @@ func validateUPluginJSON(archive *zip.Reader, uPluginFile *zip.File, withValidat } uPluginJSON, err := io.ReadAll(rc) - if err != nil { return nil, errors.New("invalid zip archive") } result, err := gojsonschema.Validate(uPluginJSONSchema, gojsonschema.NewBytesLoader(uPluginJSON)) - if err != nil { return nil, errors.New(uPluginFile.Name + " doesn't follow schema. please view the help page. (" + err.Error() + ")") } diff --git a/validation/virustotal.go b/validation/virustotal.go index 9c74887..f9104ed 100644 --- a/validation/virustotal.go +++ b/validation/virustotal.go @@ -5,9 +5,8 @@ import ( "io" "time" - "github.com/pkg/errors" - "github.com/VirusTotal/vt-go" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) @@ -24,18 +23,17 @@ func InitializeVirusTotal() { type AnalysisResults struct { Attributes struct { - Status string `json:"status"` - Stats *struct { + Stats *struct { Suspicious *int `json:"suspicious,omitempty"` Malicious *int `json:"malicious,omitempty"` } `json:"stats,omitempty"` + Status string `json:"status"` } `json:"attributes,omitempty"` } func ScanFiles(ctx context.Context, files []io.Reader, names []string) (bool, error) { for i, file := range files { scan, err := client.NewFileScanner().Scan(file, names[i], nil) - if err != nil { return false, errors.Wrap(err, "failed to scan file") } From 751b75354f5c2e7341d27ce503f614b7c69fa355 Mon Sep 17 00:00:00 2001 From: Chris Wall <47161774+porisius@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:50:08 -0400 Subject: [PATCH 12/21] change: mod.ID -> mod.ModReference in webhooks (#35) --- integrations/discord.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/discord.go b/integrations/discord.go index 85cfc74..81347f6 100644 --- a/integrations/discord.go +++ b/integrations/discord.go @@ -43,7 +43,7 @@ func NewMod(ctx context.Context, mod *postgres.Mod) { "embeds": []interface{}{ map[string]interface{}{ "title": "**" + mod.Name + "**", - "url": "https://ficsit.app/mod/" + mod.ID, + "url": "https://ficsit.app/mod/" + mod.ModReference, "color": 16750592, "description": mod.ShortDescription, "fields": []interface{}{ @@ -116,7 +116,7 @@ func NewVersion(ctx context.Context, version *postgres.Version) { "embeds": []interface{}{ map[string]interface{}{ "title": "**" + mod.Name + " v" + version.Version + "**", - "url": "https://ficsit.app/mod/" + mod.ID + "/version/" + version.ID, + "url": "https://ficsit.app/mod/" + mod.ModReference + "/version/" + version.ID, "color": 16750592, "description": "New Version Available!", "fields": []interface{}{ From 2c09e07d19b54f3c73b4239e421a0723a117f7c9 Mon Sep 17 00:00:00 2001 From: Rob B Date: Thu, 19 Oct 2023 16:05:39 -0400 Subject: [PATCH 13/21] feat: fields and features for dedicated server support (#28) * fix: update converter_windows to match linux counterpart * feat: validate multi-target plugin, detect targets and plugin type * fix: keep the original uploaded file, remove "combined" arch * refactor: separate ModArch creation from storage * chore: rename all "arch/platform" consistently, remove ID from VersionTargets The "arch/platform" entities are now called targets The "platform" field is now targetName VersionTarget and SMLVersionTarget have VersionID and TargetName as their primary keys, rather than a separate ID Removed unnecessary and unused GQL endpoints related to Targets * feat: backwards compatibility for Version fields Link, Hash and Size These fields in the db store the original uploaded file, which for single-target plugins (WindowsNoEditor) is valid, but for multi-target plugins is not Return the WindowsNoEditor target information, if available, otherwise return the version's * chore: lint * fix: update a missed target->target_name * chore: update more missed link->target * feat: VirusTotal parallelization * chore: rename WindowsNoEditor to Windows * fix: don't automatically preload version targets * chore: simplify Version.Link resolver * feat: return error message when uploading legacy mod formats * feat: enum for TargetName * fix: delete SML version targets when removed * chore: lint * feat: migrate existing mods to use targets * fix: skip migrating versions that already have targets * chore: remove some debug code * chore: temporarily require single-target format * feat: new metadata parser, sml engine versions, minimal mod version REST endpoint * chore: reorder go generate steps * chore: download protoc dependencies * chore: field alignment --------- Co-authored-by: mircearoata Co-authored-by: porisius <47161774+porisius@users.noreply.github.com> Co-authored-by: Vilsol --- .envrc | 1 + .github/workflows/build.yml | 16 +- .github/workflows/release.yml | 5 +- Dockerfile | 6 +- README.md | 8 + config.sample.json | 7 +- config/config.go | 2 + dataloader/loaders.go | 4 +- db/postgres/mod.go | 8 +- db/postgres/mod_archs.go | 122 ---------- db/postgres/postgres_types.go | 49 ++-- db/postgres/sml_archs.go | 99 --------- db/postgres/sml_version.go | 25 +-- db/postgres/version.go | 62 ++++-- docker-compose-dev.yml | 7 +- go.mod | 1 + go.sum | 1 + gql/gql_types.go | 52 ++--- gql/resolver.go | 4 +- gql/resolver_mod_archs.go | 34 --- gql/resolver_sml_archs.go | 101 --------- gql/resolver_sml_versions.go | 93 +++----- gql/resolver_versions.go | 74 +++++- gql/versions.go | 132 ++++++++--- gqlgen.yml | 18 +- .../sql/000022_update_mod_targets.down.sql | 64 ++++++ .../sql/000022_update_mod_targets.up.sql | 31 +++ ...00023_migrate_versions_to_targets.down.sql | 15 ++ .../000023_migrate_versions_to_targets.up.sql | 13 ++ .../000024_add_sml_engine_version.down.sql | 2 + .../sql/000024_add_sml_engine_version.up.sql | 2 + models/filters.go | 82 ------- nodes/mod.go | 47 +++- nodes/mod_types.go | 82 ++++++- nodes/routes.go | 6 +- nodes/version.go | 22 +- proto/parser/.gitignore | 1 + proto/parser/parser.proto | 17 ++ .../consumer_scan_mod_on_virus_total.go | 9 +- schemas/mod_archs.graphql | 38 ---- schemas/sml_archs.graphql | 46 ---- schemas/sml_version.graphql | 25 ++- schemas/version.graphql | 10 +- schemas/version_target.graphql | 5 + shell.nix | 11 + storage/b2.go | 4 + storage/s3.go | 29 +++ storage/storage.go | 159 ++++++------- storage/wasabi.go | 4 + tools.go | 1 + util/converter/converter_windows.go | 27 ++- util/flags.go | 13 ++ validation/extractor.go | 92 ++++++++ validation/validation.go | 210 +++++++++++++++--- validation/virustotal.go | 92 +++++--- 55 files changed, 1174 insertions(+), 916 deletions(-) create mode 100644 .envrc delete mode 100644 db/postgres/mod_archs.go delete mode 100644 db/postgres/sml_archs.go delete mode 100644 gql/resolver_mod_archs.go delete mode 100644 gql/resolver_sml_archs.go create mode 100644 migrations/sql/000022_update_mod_targets.down.sql create mode 100644 migrations/sql/000022_update_mod_targets.up.sql create mode 100644 migrations/sql/000023_migrate_versions_to_targets.down.sql create mode 100644 migrations/sql/000023_migrate_versions_to_targets.up.sql create mode 100644 migrations/sql/000024_add_sml_engine_version.down.sql create mode 100644 migrations/sql/000024_add_sml_engine_version.up.sql create mode 100644 proto/parser/.gitignore create mode 100644 proto/parser/parser.proto delete mode 100644 schemas/mod_archs.graphql delete mode 100644 schemas/sml_archs.graphql create mode 100644 schemas/version_target.graphql create mode 100644 shell.nix create mode 100644 util/flags.go create mode 100644 validation/extractor.go diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..65326bb --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe34539..332f139 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,10 @@ jobs: uses: actions/checkout@v3 - name: Download dependencies - run: sudo apt update && sudo apt install -y build-essential libpng-dev + run: | + sudo apt update && sudo apt install -y build-essential libpng-dev protobuf-compiler + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 - name: Go Generate run: go generate -tags tools -x ./... @@ -39,7 +42,10 @@ jobs: uses: actions/checkout@v3 - name: Download dependencies - run: sudo apt update && sudo apt install -y build-essential libpng-dev + run: | + sudo apt update && sudo apt install -y build-essential libpng-dev protobuf-compiler + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 - name: Go Generate run: go generate -tags tools -x ./... @@ -47,6 +53,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: + version: v1.49.0 skip-pkg-cache: true skip-build-cache: true args: --timeout 5m @@ -64,7 +71,10 @@ jobs: uses: actions/checkout@v3 - name: Download dependencies - run: sudo apt update && sudo apt install -y build-essential libpng-dev + run: | + sudo apt update && sudo apt install -y build-essential libpng-dev protobuf-compiler + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 - name: Go Generate run: go generate -tags tools -x ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e54877..fc5a0ef 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,10 @@ jobs: fetch-depth: 0 - name: Download dependencies - run: sudo apt update && sudo apt install -y build-essential libpng-dev + run: | + sudo apt update && sudo apt install -y build-essential libpng-dev protobuf-compiler + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v3 diff --git a/Dockerfile b/Dockerfile index d1d66b8..83c9552 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ -FROM golang:1.18-alpine AS builder +FROM golang:1.19-alpine3.18 AS builder -RUN apk add --no-cache git build-base libpng-dev +RUN apk add --no-cache git build-base libpng-dev protoc +RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 +RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 WORKDIR $GOPATH/src/github.com/satisfactorymodding/smr-api/ diff --git a/README.md b/README.md index 269a593..220b9da 100755 --- a/README.md +++ b/README.md @@ -49,6 +49,14 @@ Main configuration options: The config format can be seen in `config/config.go` (each dot means a new level of nesting). +After startup requires the following minio commands to be executed: + +```shell +mc alias set local http://localhost:9000 minio minio123 +mc admin user svcacct add local minio --access-key REPLACE_ME_KEY --secret-key REPLACE_ME_SECRET +mc anonymous set public local/smr +``` + ## Contributing Before contributing, please run the [linter](https://golangci-lint.run/) to ensure the code is clean and well-formed: diff --git a/config.sample.json b/config.sample.json index 6003768..f865d27 100644 --- a/config.sample.json +++ b/config.sample.json @@ -27,7 +27,8 @@ "key": "REPLACE_ME_KEY", "secret": "REPLACE_ME_SECRET", "endpoint": "http://localhost:9000", - "base_url": "http://localhost:9000" + "base_url": "http://localhost:9000", + "keypath": "%s/%s/%s" }, "oauth": { @@ -52,5 +53,9 @@ "frontend": { "url": "http://localhost:4200" + }, + + "feature_flags": { + "allow_multi_target_upload": false } } \ No newline at end of file diff --git a/config/config.go b/config/config.go index ad26119..8e0277c 100644 --- a/config/config.go +++ b/config/config.go @@ -100,4 +100,6 @@ func initializeDefaults() { viper.SetDefault("frontend.url", "") viper.SetDefault("virustotal.key", "") + + viper.SetDefault("feature_flags.allow_multi_target_upload", false) } diff --git a/dataloader/loaders.go b/dataloader/loaders.go index 8fc0303..861ed84 100644 --- a/dataloader/loaders.go +++ b/dataloader/loaders.go @@ -109,7 +109,7 @@ func Middleware() func(handlerFunc echo.HandlerFunc) echo.HandlerFunc { var entities []postgres.Version reqCtx := c.Request().Context() - postgres.DBCtx(reqCtx).Preload("Arch").Where("approved = ? AND denied = ? AND mod_id IN ?", true, false, fetchIds).Order("created_at desc").Find(&entities) + postgres.DBCtx(reqCtx).Preload("Targets").Where("approved = ? AND denied = ? AND mod_id IN ?", true, false, fetchIds).Order("created_at desc").Find(&entities) for _, entity := range entities { byID[entity.ModID] = append(byID[entity.ModID], entity) @@ -145,7 +145,7 @@ func Middleware() func(handlerFunc echo.HandlerFunc) echo.HandlerFunc { var entities []postgres.Version reqCtx := c.Request().Context() - postgres.DBCtx(reqCtx).Preload("Arch").Select( + postgres.DBCtx(reqCtx).Preload("Targets").Select( "id", "created_at", "updated_at", diff --git a/db/postgres/mod.go b/db/postgres/mod.go index 1c92c55..438ada7 100644 --- a/db/postgres/mod.go +++ b/db/postgres/mod.go @@ -26,7 +26,7 @@ func GetModByID(ctx context.Context, modID string) *Mod { func GetModByIDNoCache(ctx context.Context, modID string) *Mod { var mod Mod - DBCtx(ctx).Preload("Tags").Preload("Versions.Arch").Find(&mod, "id = ?", modID) + DBCtx(ctx).Preload("Tags").Preload("Versions.Targets").Find(&mod, "id = ?", modID) if mod.ID == "" { return nil @@ -44,7 +44,7 @@ func GetModByReference(ctx context.Context, modReference string) *Mod { } var mod Mod - DBCtx(ctx).Preload("Tags").Preload("Versions.Arch").Find(&mod, "mod_reference = ?", modReference) + DBCtx(ctx).Preload("Tags").Preload("Versions.Targets").Find(&mod, "mod_reference = ?", modReference) if mod.ID == "" { return nil @@ -213,7 +213,7 @@ func NewModQuery(ctx context.Context, filter *models.ModFilter, unapproved bool, } query = query.Where("approved = ? AND denied = ?", !unapproved, false) - query = query.Preload("Tags").Preload("Versions.Arch") + query = query.Preload("Tags").Preload("Versions.Targets") if filter != nil { if filter.Search != nil && *filter.Search != "" { cleanSearch := strings.ReplaceAll(strings.TrimSpace(*filter.Search), " ", " & ") @@ -270,7 +270,7 @@ func GetModByIDOrReference(ctx context.Context, modIDOrReference string) *Mod { } var mod Mod - DBCtx(ctx).Preload("Tags").Preload("Versions.Arch").Find(&mod, "mod_reference = ? OR id = ?", modIDOrReference, modIDOrReference) + DBCtx(ctx).Preload("Tags").Preload("Versions.Targets").Find(&mod, "mod_reference = ? OR id = ?", modIDOrReference, modIDOrReference) if mod.ID == "" { return nil diff --git a/db/postgres/mod_archs.go b/db/postgres/mod_archs.go deleted file mode 100644 index aa6bde9..0000000 --- a/db/postgres/mod_archs.go +++ /dev/null @@ -1,122 +0,0 @@ -package postgres - -import ( - "context" - "strings" - - "github.com/patrickmn/go-cache" - - "github.com/satisfactorymodding/smr-api/models" - "github.com/satisfactorymodding/smr-api/util" -) - -func CreateModArch(ctx context.Context, modArch *ModArch) (*ModArch, error) { - modArch.ID = util.GenerateUniqueID() - DBCtx(ctx).Create(&modArch) - return modArch, nil -} - -func GetModArch(ctx context.Context, modArchID string) *ModArch { - cacheKey := "GetModArch_" + modArchID - - if modArch, ok := dbCache.Get(cacheKey); ok { - return modArch.(*ModArch) - } - - var modArch ModArch - DBCtx(ctx).Find(&modArch, "id = ?", modArchID) - - if modArch.ID == "" { - return nil - } - - dbCache.Set(cacheKey, &modArch, cache.DefaultExpiration) - - return &modArch -} - -func GetModArchs(ctx context.Context, filter *models.ModArchFilter) []ModArch { - var modArchs []ModArch - query := DBCtx(ctx) - - if filter != nil { - query = query.Limit(*filter.Limit). - Offset(*filter.Offset). - Order(string(*filter.OrderBy) + " " + string(*filter.Order)) - - if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) - } - } - - query.Find(&modArchs) - return modArchs -} - -func GetVersionModArchs(ctx context.Context, versionID string) []ModArch { - var modArchs []ModArch - query := DBCtx(ctx).Find(&modArchs, "mod_version_arch_id = ?", versionID) - - query.Find(&modArchs) - return modArchs -} - -func GetModArchByID(ctx context.Context, modArchID string) *ModArch { - cacheKey := "GetModArch_" + modArchID - - if modArch, ok := dbCache.Get(cacheKey); ok { - return modArch.(*ModArch) - } - - var modArch ModArch - DBCtx(ctx).Find(&modArch, "id = ?", modArchID) - - if modArch.ID == "" { - return nil - } - - dbCache.Set(cacheKey, &modArch, cache.DefaultExpiration) - - return &modArch -} - -func GetModArchsByID(ctx context.Context, modArchIds []string) []ModArch { - var modArchs []ModArch - - DBCtx(ctx).Find(&modArchs, "id in (?)", modArchIds) - - if len(modArchIds) != len(modArchs) { - return nil - } - - return modArchs -} - -func GetModArchByPlatform(ctx context.Context, versionID string, platform string) *ModArch { - cacheKey := "GetModArch_" + versionID + "_" + platform - if modplatform, ok := dbCache.Get(cacheKey); ok { - return modplatform.(*ModArch) - } - - var modplatform ModArch - DBCtx(ctx).First(&modplatform, "mod_version_arch_id = ? AND platform = ?", versionID, platform) - - if modplatform.ModVersionID == "" { - return nil - } - - dbCache.Set(cacheKey, &modplatform, cache.DefaultExpiration) - - return &modplatform -} - -func GetModArchDownload(ctx context.Context, versionID string, platform string) string { - var modPlatform ModArch - DBCtx(ctx).First(&modPlatform, "mod_version_arch_id = ? AND platform = ?", versionID, platform) - - if modPlatform.ModVersionID == "" { - return "" - } - - return modPlatform.Key -} diff --git a/db/postgres/postgres_types.go b/db/postgres/postgres_types.go index 84e0622..c09660e 100644 --- a/db/postgres/postgres_types.go +++ b/db/postgres/postgres_types.go @@ -87,13 +87,27 @@ type Version struct { SMLVersion string `gorm:"type:varchar(16)"` Version string `gorm:"type:varchar(16)"` ModID string - Arch []ModArch `gorm:"foreignKey:ModVersionID;preload:true"` + Targets []VersionTarget `gorm:"foreignKey:VersionID"` Hotness uint Downloads uint Denied bool `gorm:"default:false;not null"` Approved bool `gorm:"default:false;not null"` } +type TinyVersion struct { + Hash *string + Size *int64 + SMRModel + SMLVersion string `gorm:"type:varchar(16)"` + Version string `gorm:"type:varchar(16)"` + Targets []VersionTarget `gorm:"foreignKey:VersionID;preload:true"` + Dependencies []VersionDependency `gorm:"foreignKey:VersionID"` +} + +func (TinyVersion) TableName() string { + return "versions" +} + type Guide struct { SMRModel Name string `gorm:"type:varchar(50)"` @@ -120,7 +134,8 @@ type SMLVersion struct { Stability string `sql:"type:version_stability"` Link string Changelog string - Arch []SMLArch `gorm:"foreignKey:SMLVersionID;preload:true"` + EngineVersion string + Targets []SMLVersionTarget `gorm:"foreignKey:VersionID"` SatisfactoryVersion int } @@ -179,26 +194,16 @@ type Compatibility struct { Note string } -type ModArch struct { - ID string `gorm:"primary_key;type:varchar(16)"` - ModVersionID string `gorm:"column:mod_version_arch_id"` - Platform string - Key string - Hash string - Size int64 -} - -func (ModArch) TableName() string { - return "mod_archs" -} - -type SMLArch struct { - ID string `gorm:"primary_key;type:varchar(14)"` - SMLVersionID string `gorm:"column:sml_version_arch_id"` - Platform string - Link string +type VersionTarget struct { + VersionID string `gorm:"primary_key;type:varchar(14)"` + TargetName string `gorm:"primary_key;type:varchar(16)"` + Key string + Hash string + Size int64 } -func (SMLArch) TableName() string { - return "sml_archs" +type SMLVersionTarget struct { + VersionID string `gorm:"primary_key;type:varchar(14)"` + TargetName string `gorm:"primary_key;type:varchar(16)"` + Link string } diff --git a/db/postgres/sml_archs.go b/db/postgres/sml_archs.go deleted file mode 100644 index f8b0108..0000000 --- a/db/postgres/sml_archs.go +++ /dev/null @@ -1,99 +0,0 @@ -package postgres - -import ( - "context" - "strings" - - "github.com/patrickmn/go-cache" - - "github.com/satisfactorymodding/smr-api/models" - "github.com/satisfactorymodding/smr-api/util" -) - -func CreateSMLArch(ctx context.Context, smlArch *SMLArch) (*SMLArch, error) { - smlArch.ID = util.GenerateUniqueID() - - DBCtx(ctx).Create(&smlArch) - - return smlArch, nil -} - -func GetSMLArch(ctx context.Context, smlLinkID string) *SMLArch { - cacheKey := "GetSMLArch_" + smlLinkID - - if smlArch, ok := dbCache.Get(cacheKey); ok { - return smlArch.(*SMLArch) - } - - var smlArch SMLArch - DBCtx(ctx).Find(&smlArch, "id = ?", smlLinkID) - - if smlArch.ID == "" { - return nil - } - - dbCache.Set(cacheKey, &smlArch, cache.DefaultExpiration) - - return &smlArch -} - -func GetSMLArchs(ctx context.Context, filter *models.SMLArchFilter) []SMLArch { - var smlLinks []SMLArch - query := DBCtx(ctx) - - if filter != nil { - query = query.Limit(*filter.Limit). - Offset(*filter.Offset). - Order(string(*filter.OrderBy) + " " + string(*filter.Order)) - - if filter.Search != nil && *filter.Search != "" { - query = query.Where("to_tsvector(name) @@ to_tsquery(?)", strings.ReplaceAll(*filter.Search, " ", " & ")) - } - } - - query.Find(&smlLinks) - return smlLinks -} - -func GetSMLArchByID(ctx context.Context, smlLinkID string) []SMLArch { - var smlArchs []SMLArch - - DBCtx(ctx).Find(&smlArchs, "id in ?", smlLinkID) - - if len(smlArchs) != 0 { - return nil - } - - return smlArchs -} - -func GetSMLArchsByID(ctx context.Context, smlArchIds []string) []SMLArch { - var smlArchs []SMLArch - - DBCtx(ctx).Find(&smlArchs, "id in (?)", smlArchIds) - - if len(smlArchIds) != len(smlArchs) { - return nil - } - - return smlArchs -} - -func GetSMLArchBySMLID(ctx context.Context, smlVersionID string) []SMLArch { - var smlArchs []SMLArch - - DBCtx(ctx).Find(&smlArchs, "sml_version_arch_id = ?", smlVersionID) - - return smlArchs -} - -func GetSMLArchDownload(ctx context.Context, smlVersionID string, platform string) string { - var smlPlatform SMLArch - DBCtx(ctx).First(&smlPlatform, "sml_version_arch_id = ? AND platform = ?", smlVersionID, platform) - - if smlPlatform.ID == "" { - return "" - } - - return smlPlatform.Link -} diff --git a/db/postgres/sml_version.go b/db/postgres/sml_version.go index 762e923..46f6e21 100644 --- a/db/postgres/sml_version.go +++ b/db/postgres/sml_version.go @@ -13,21 +13,12 @@ func CreateSMLVersion(ctx context.Context, smlVersion *SMLVersion) (*SMLVersion, DBCtx(ctx).Create(&smlVersion) - for _, link := range smlVersion.Arch { - DBCtx(ctx).Create(&SMLArch{ - ID: util.GenerateUniqueID(), - SMLVersionID: smlVersion.ID, - Platform: link.Platform, - Link: link.Link, - }) - } - return smlVersion, nil } func GetSMLVersionByID(ctx context.Context, smlVersionID string) *SMLVersion { var smlVersion SMLVersion - DBCtx(ctx).Preload("Arch").Find(&smlVersion, "id in (?)", smlVersionID) + DBCtx(ctx).Preload("Targets").Find(&smlVersion, "id in (?)", smlVersionID) if smlVersion.ID == "" { return nil @@ -50,14 +41,14 @@ func GetSMLVersions(ctx context.Context, filter *models.SMLVersionFilter) []SMLV } } - query.Preload("Arch").Find(&smlVersions) + query.Preload("Targets").Find(&smlVersions) return smlVersions } func GetSMLVersionsByID(ctx context.Context, smlVersionIds []string) []SMLVersion { var smlVersions []SMLVersion - DBCtx(ctx).Preload("Arch").Find(&smlVersions, "id in (?)", smlVersionIds) + DBCtx(ctx).Preload("Targets").Find(&smlVersions, "id in (?)", smlVersionIds) if len(smlVersionIds) != len(smlVersions) { return nil @@ -83,9 +74,17 @@ func GetSMLVersionCount(ctx context.Context, filter *models.SMLVersionFilter) in func GetSMLLatestVersions(ctx context.Context) *[]SMLVersion { var smlVersions []SMLVersion - DBCtx(ctx).Preload("Arch").Select("distinct on (stability) *"). + DBCtx(ctx).Preload("Targets").Select("distinct on (stability) *"). Order("stability, created_at desc"). Find(&smlVersions) return &smlVersions } + +func GetSMLVersionTargets(ctx context.Context, smlVersionID string) []SMLVersionTarget { + var smlVersionTargets []SMLVersionTarget + + DBCtx(ctx).Find(&smlVersionTargets, "version_id = ?", smlVersionID) + + return smlVersionTargets +} diff --git a/db/postgres/version.go b/db/postgres/version.go index 5440055..d70aee8 100644 --- a/db/postgres/version.go +++ b/db/postgres/version.go @@ -24,7 +24,7 @@ func GetVersionsByID(ctx context.Context, versionIds []string) []Version { } var versions []Version - DBCtx(ctx).Preload("Arch").Find(&versions, "id in (?)", versionIds) + DBCtx(ctx).Preload("Targets").Find(&versions, "id in (?)", versionIds) if len(versionIds) != len(versions) { return nil @@ -43,7 +43,7 @@ func GetModLatestVersions(ctx context.Context, modID string, unapproved bool) *[ var versions []Version - DBCtx(ctx).Preload("Arch").Select("distinct on (mod_id, stability) *"). + DBCtx(ctx).Preload("Targets").Select("distinct on (mod_id, stability) *"). Where("mod_id = ?", modID). Where("approved = ? AND denied = ?", !unapproved, false). Order("mod_id, stability, created_at desc"). @@ -62,7 +62,7 @@ func GetModsLatestVersions(ctx context.Context, modIds []string, unapproved bool var versions []Version - DBCtx(ctx).Preload("Arch").Select("distinct on (mod_id, stability) *"). + DBCtx(ctx).Preload("Targets").Select("distinct on (mod_id, stability) *"). Where("mod_id in (?)", modIds). Where("approved = ? AND denied = ?", !unapproved, false). Order("mod_id, stability, created_at desc"). @@ -80,7 +80,25 @@ func GetModVersions(ctx context.Context, modID string, limit int, offset int, or } var versions []Version - DBCtx(ctx).Preload("Arch").Limit(limit).Offset(offset).Order(orderBy+" "+order).Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) + DBCtx(ctx).Preload("Targets").Limit(limit).Offset(offset).Order(orderBy+" "+order).Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) + + dbCache.Set(cacheKey, versions, cache.DefaultExpiration) + + return versions +} + +func GetAllModVersionsWithDependencies(ctx context.Context, modID string) []TinyVersion { + cacheKey := "GetAllModVersionsWithDependencies_" + modID + if versions, ok := dbCache.Get(cacheKey); ok { + return versions.([]TinyVersion) + } + + var versions []TinyVersion + DBCtx(ctx).Debug(). + Preload("Dependencies"). + Preload("Targets"). + Where("approved = ? AND denied = ?", true, false). + Find(&versions, "mod_id = ?", modID) dbCache.Set(cacheKey, versions, cache.DefaultExpiration) @@ -98,7 +116,7 @@ func GetModVersionsNew(ctx context.Context, modID string, filter *models.Version } var versions []Version - query := DBCtx(ctx).Preload("Arch") + query := DBCtx(ctx).Preload("Targets") if filter != nil { query = query.Limit(*filter.Limit). @@ -106,7 +124,7 @@ func GetModVersionsNew(ctx context.Context, modID string, filter *models.Version Order(string(*filter.OrderBy) + " " + string(*filter.Order)) } - query.Preload("Arch").Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) + query.Preload("Targets").Where("approved = ? AND denied = ?", !unapproved, false).Find(&versions, "mod_id = ?", modID) if cacheKey != "" { dbCache.Set(cacheKey, versions, cache.DefaultExpiration) @@ -122,7 +140,7 @@ func GetModVersion(ctx context.Context, modID string, versionID string) *Version } var version Version - DBCtx(ctx).Preload("Arch").First(&version, "mod_id = ? AND id = ?", modID, versionID) + DBCtx(ctx).Preload("Targets").First(&version, "mod_id = ? AND id = ?", modID, versionID) if version.ID == "" { return nil @@ -140,7 +158,7 @@ func GetModVersionByName(ctx context.Context, modID string, versionName string) } var version Version - DBCtx(ctx).Preload("Arch").First(&version, "mod_id = ? AND version = ?", modID, versionName) + DBCtx(ctx).Preload("Targets").First(&version, "mod_id = ? AND version = ?", modID, versionName) if version.ID == "" { return nil @@ -186,7 +204,7 @@ func GetVersion(ctx context.Context, versionID string) *Version { } var version Version - DBCtx(ctx).Preload("Arch").First(&version, "id = ?", versionID) + DBCtx(ctx).Preload("Targets").First(&version, "id = ?", versionID) if version.ID == "" { return nil @@ -208,7 +226,7 @@ func GetVersionsNew(ctx context.Context, filter *models.VersionFilter, unapprove } var versions []Version - query := DBCtx(ctx).Preload("Arch").Where("approved = ? AND denied = ?", !unapproved, false) + query := DBCtx(ctx).Preload("Targets").Where("approved = ? AND denied = ?", !unapproved, false) if filter != nil { query = query.Limit(*filter.Limit). @@ -224,7 +242,7 @@ func GetVersionsNew(ctx context.Context, filter *models.VersionFilter, unapprove } } - query.Preload("Arch").Find(&versions) + query.Preload("Targets").Find(&versions) if cacheKey != "" { dbCache.Set(cacheKey, versions, cache.DefaultExpiration) @@ -261,6 +279,24 @@ func GetVersionCountNew(ctx context.Context, filter *models.VersionFilter, unapp return versionCount } +func GetVersionTarget(ctx context.Context, versionID string, target string) *VersionTarget { + cacheKey := "GetVersionTarget_" + versionID + "_" + target + if versionTarget, ok := dbCache.Get(cacheKey); ok { + return versionTarget.(*VersionTarget) + } + + var versionTarget VersionTarget + DBCtx(ctx).First(&versionTarget, "version_id = ? AND target_name = ?", versionID, target) + + if versionTarget.VersionID == "" { + return nil + } + + dbCache.Set(cacheKey, &versionTarget, cache.DefaultExpiration) + + return &versionTarget +} + func GetVersionDependencies(ctx context.Context, versionID string) []VersionDependency { var versionDependencies []VersionDependency DBCtx(ctx).Where("version_id = ?", versionID).Find(&versionDependencies) @@ -288,7 +324,7 @@ func GetModVersionsConstraint(ctx context.Context, modID string, constraint stri return nil } - query := DBCtx(ctx).Preload("Arch").Where("mod_id", modID) + query := DBCtx(ctx).Preload("Targets").Where("mod_id", modID) /* <=1.2.3 @@ -357,6 +393,6 @@ func GetModVersionsConstraint(ctx context.Context, modID string, constraint stri } var versions []Version - query.Preload("Arch").Find(&versions) + query.Preload("Targets").Find(&versions) return versions } diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 3327f6b..6b30231 100755 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -25,4 +25,9 @@ services: MINIO_ROOT_USER: minio MINIO_ROOT_PASSWORD: minio123 MINIO_ACCESS_KEY: REPLACE_ME_KEY - MINIO_SECRET_KEY: REPLACE_ME_SECRET \ No newline at end of file + MINIO_SECRET_KEY: REPLACE_ME_SECRET + + pak_parser: + image: ghcr.io/vilsol/ficsit-pak-parser:v0.0.3 + ports: + - 50051:50051 \ No newline at end of file diff --git a/go.mod b/go.mod index 2a6ed37..43a278e 100755 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/net v0.0.0-20220728030405-41545e8bf201 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c gopkg.in/go-playground/validator.v9 v9.31.0 gorm.io/driver/postgres v1.3.5 gorm.io/gorm v1.23.5 diff --git a/go.sum b/go.sum index 42c970c..7828d76 100755 --- a/go.sum +++ b/go.sum @@ -1677,6 +1677,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/gql/gql_types.go b/gql/gql_types.go index 7a7f877..6d8d396 100644 --- a/gql/gql_types.go +++ b/gql/gql_types.go @@ -85,7 +85,7 @@ func DBVersionToGenerated(version *postgres.Version) *generated.Version { Changelog: version.Changelog, Downloads: int(version.Downloads), Stability: generated.VersionStabilities(version.Stability), - Arch: DBModArchsToGeneratedSlice(version.Arch), + Targets: DBVersionTargetsToGeneratedSlice(version.Targets), Approved: version.Approved, UpdatedAt: version.UpdatedAt.Format(time.RFC3339Nano), CreatedAt: version.CreatedAt.Format(time.RFC3339Nano), @@ -134,11 +134,12 @@ func DBSMLVersionToGenerated(smlVersion *postgres.SMLVersion) *generated.SMLVers BootstrapVersion: smlVersion.BootstrapVersion, Stability: generated.VersionStabilities(smlVersion.Stability), Link: smlVersion.Link, - Arch: DBSMLArchsToGeneratedSlice(smlVersion.Arch), + Targets: DBSMLVersionTargetToGeneratedSlice(smlVersion.Targets), Changelog: smlVersion.Changelog, Date: smlVersion.Date.Format(time.RFC3339Nano), UpdatedAt: smlVersion.UpdatedAt.Format(time.RFC3339Nano), CreatedAt: smlVersion.CreatedAt.Format(time.RFC3339Nano), + EngineVersion: smlVersion.EngineVersion, } } @@ -211,47 +212,46 @@ func DBTagsToGeneratedSlice(tags []postgres.Tag) []*generated.Tag { return converted } -func DBModArchToGenerated(modArch *postgres.ModArch) *generated.ModArch { - if modArch == nil { +func DBVersionTargetToGenerated(versionTarget *postgres.VersionTarget) *generated.VersionTarget { + if versionTarget == nil { return nil } - size := int(modArch.Size) + hash := versionTarget.Hash + size := int(versionTarget.Size) - return &generated.ModArch{ - ID: modArch.ID, - ModVersionID: modArch.ModVersionID, - Platform: modArch.Platform, - Hash: &modArch.Hash, - Size: &size, + return &generated.VersionTarget{ + VersionID: versionTarget.VersionID, + TargetName: generated.TargetName(versionTarget.TargetName), + Hash: &hash, + Size: &size, } } -func DBModArchsToGeneratedSlice(modArchs []postgres.ModArch) []*generated.ModArch { - converted := make([]*generated.ModArch, len(modArchs)) - for i, modArch := range modArchs { - converted[i] = DBModArchToGenerated(&modArch) +func DBVersionTargetsToGeneratedSlice(versionTargets []postgres.VersionTarget) []*generated.VersionTarget { + converted := make([]*generated.VersionTarget, len(versionTargets)) + for i, versionTarget := range versionTargets { + converted[i] = DBVersionTargetToGenerated(&versionTarget) } return converted } -func DBSMLArchToGenerated(smlArch *postgres.SMLArch) *generated.SMLArch { - if smlArch == nil { +func DBSMLVersionTargetToGenerated(smlVersionTarget *postgres.SMLVersionTarget) *generated.SMLVersionTarget { + if smlVersionTarget == nil { return nil } - return &generated.SMLArch{ - ID: smlArch.ID, - SMLVersionID: smlArch.SMLVersionID, - Platform: smlArch.Platform, - Link: smlArch.Link, + return &generated.SMLVersionTarget{ + VersionID: smlVersionTarget.VersionID, + TargetName: generated.TargetName(smlVersionTarget.TargetName), + Link: smlVersionTarget.Link, } } -func DBSMLArchsToGeneratedSlice(smlLinks []postgres.SMLArch) []*generated.SMLArch { - converted := make([]*generated.SMLArch, len(smlLinks)) - for i, smlArch := range smlLinks { - converted[i] = DBSMLArchToGenerated(&smlArch) +func DBSMLVersionTargetToGeneratedSlice(smlVersionTargets []postgres.SMLVersionTarget) []*generated.SMLVersionTarget { + converted := make([]*generated.SMLVersionTarget, len(smlVersionTargets)) + for i, smlVersionTarget := range smlVersionTargets { + converted[i] = DBSMLVersionTargetToGenerated(&smlVersionTarget) } return converted } diff --git a/gql/resolver.go b/gql/resolver.go index 51c0c37..4354780 100755 --- a/gql/resolver.go +++ b/gql/resolver.go @@ -26,8 +26,8 @@ func (r *Resolver) UserMod() generated.UserModResolver { return &userModResolver{r} } -func (r *Resolver) ModArch() generated.ModArchResolver { - return &modlinkResolver{r} +func (r *Resolver) VersionTarget() generated.VersionTargetResolver { + return &versionTargetResolver{r} } func (r *Resolver) Version() generated.VersionResolver { diff --git a/gql/resolver_mod_archs.go b/gql/resolver_mod_archs.go deleted file mode 100644 index 0181328..0000000 --- a/gql/resolver_mod_archs.go +++ /dev/null @@ -1,34 +0,0 @@ -package gql - -import ( - "context" - - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/satisfactorymodding/smr-api/generated" -) - -type modlinkResolver struct{ *Resolver } - -func (r *modlinkResolver) Asset(_ context.Context, obj *generated.ModArch) (string, error) { - return "/v1/version/" + obj.ModVersionID + "/" + obj.Platform + "/download", nil -} - -func (r *queryResolver) GetModArch(ctx context.Context, modArchID string) (*generated.ModArch, error) { - wrapper, newCtx := WrapQueryTrace(ctx, "getModArch") - defer wrapper.end() - modArch := postgres.GetModArch(newCtx, modArchID) - return DBModArchToGenerated(modArch), nil -} - -func (r *queryResolver) GetModArchs(ctx context.Context, filter map[string]interface{}) (*generated.GetModArchs, error) { - wrapper, _ := WrapQueryTrace(ctx, "getModArchs") - defer wrapper.end() - return &generated.GetModArchs{}, nil -} - -func (r *queryResolver) GetModArchByID(ctx context.Context, modArchID string) (*generated.ModArch, error) { - wrapper, newCtx := WrapQueryTrace(ctx, "getModArchByID") - defer wrapper.end() - modArch := postgres.GetModArchByID(newCtx, modArchID) - return DBModArchToGenerated(modArch), nil -} diff --git a/gql/resolver_sml_archs.go b/gql/resolver_sml_archs.go deleted file mode 100644 index be4e776..0000000 --- a/gql/resolver_sml_archs.go +++ /dev/null @@ -1,101 +0,0 @@ -package gql - -import ( - "context" - - "github.com/pkg/errors" - "gopkg.in/go-playground/validator.v9" - - "github.com/satisfactorymodding/smr-api/db/postgres" - "github.com/satisfactorymodding/smr-api/generated" - "github.com/satisfactorymodding/smr-api/util" -) - -func (r *mutationResolver) CreateSMLArch(ctx context.Context, smlArch generated.NewSMLArch) (*generated.SMLArch, error) { - wrapper, newCtx := WrapMutationTrace(ctx, "createSMLArch") - defer wrapper.end() - - val := ctx.Value(util.ContextValidator{}).(*validator.Validate) - if err := val.Struct(&smlArch); err != nil { - return nil, errors.Wrap(err, "validation failed") - } - - dbSMLArchs := &postgres.SMLArch{ - ID: util.GenerateUniqueID(), - Platform: smlArch.Platform, - Link: smlArch.Link, - } - - resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) - if err != nil { - return nil, err - } - - return DBSMLArchToGenerated(resultSMLArch), nil -} - -func (r *mutationResolver) DeleteSMLArch(ctx context.Context, linksID string) (bool, error) { - wrapper, newCtx := WrapMutationTrace(ctx, "deleteSMLArch") - defer wrapper.end() - - dbSMLArch := postgres.GetSMLArchByID(newCtx, linksID) - - if dbSMLArch == nil { - return false, errors.New("SML Link not found") - } - - postgres.Delete(newCtx, &dbSMLArch) - - return true, nil -} - -func (r *mutationResolver) UpdateSMLArch(ctx context.Context, smlLinkID string, smlArch generated.UpdateSMLArch) (*generated.SMLArch, error) { - wrapper, newCtx := WrapMutationTrace(ctx, "updateSMLArch") - defer wrapper.end() - val := ctx.Value(util.ContextValidator{}).(*validator.Validate) - if err := val.Struct(&smlArch); err != nil { - return nil, errors.Wrap(err, "validation failed") - } - - dbSMLArch := postgres.GetSMLArch(newCtx, smlLinkID) - - if dbSMLArch == nil { - return nil, errors.New("sml link not found") - } - - SetStringINNOE(&smlArch.Platform, &dbSMLArch.Platform) - SetStringINNOE(&smlArch.Link, &dbSMLArch.Link) - - postgres.Save(newCtx, &dbSMLArch) - - return DBSMLArchToGenerated(dbSMLArch), nil -} - -func (r *queryResolver) GetSMLArch(ctx context.Context, smlLinkID string) (*generated.SMLArch, error) { - wrapper, newCtx := WrapQueryTrace(ctx, "getSMLArch") - defer wrapper.end() - - smlArch := postgres.GetSMLArch(newCtx, smlLinkID) - - return DBSMLArchToGenerated(smlArch), nil -} - -func (r *queryResolver) GetSMLArchs(ctx context.Context, filter map[string]interface{}) (*generated.GetSMLArchs, error) { - wrapper, _ := WrapQueryTrace(ctx, "getSMLArchs") - defer wrapper.end() - return &generated.GetSMLArchs{}, nil -} - -func (r *queryResolver) GetSMLArchBySMLID(ctx context.Context, smlVersionID string) ([]postgres.SMLArch, error) { - wrapper, newCtx := WrapQueryTrace(ctx, "GetSMLArchBySMLID") - defer wrapper.end() - smlArch := postgres.GetSMLArchBySMLID(newCtx, smlVersionID) - return smlArch, nil -} - -func (r *queryResolver) GetSMLDownload(ctx context.Context, smlVersionID string, platform string) (string, error) { - wrapper, newCtx := WrapQueryTrace(ctx, "getSMLDownload") - defer wrapper.end() - smlArch := postgres.GetSMLArchDownload(newCtx, smlVersionID, platform) - return smlArch, nil -} diff --git a/gql/resolver_sml_versions.go b/gql/resolver_sml_versions.go index c04c6c1..a5859f5 100644 --- a/gql/resolver_sml_versions.go +++ b/gql/resolver_sml_versions.go @@ -36,24 +36,17 @@ func (r *mutationResolver) CreateSMLVersion(ctx context.Context, smlVersion gene Link: smlVersion.Link, Changelog: smlVersion.Changelog, Date: date, + EngineVersion: smlVersion.EngineVersion, } resultSMLVersion, err := postgres.CreateSMLVersion(newCtx, dbSMLVersion) - for _, smlArch := range smlVersion.Arch { - dbSMLArchs := &postgres.SMLArch{ - ID: util.GenerateUniqueID(), - SMLVersionID: resultSMLVersion.ID, - Platform: smlArch.Platform, - Link: smlArch.Link, - } - - resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArchs) - if err != nil { - return nil, err - } - - DBSMLArchToGenerated(resultSMLArch) + for _, smlVersionTarget := range smlVersion.Targets { + postgres.Save(newCtx, &postgres.SMLVersionTarget{ + VersionID: resultSMLVersion.ID, + TargetName: string(smlVersionTarget.TargetName), + Link: smlVersionTarget.Link, + }) } if err != nil { @@ -72,6 +65,30 @@ func (r *mutationResolver) UpdateSMLVersion(ctx context.Context, smlVersionID st return nil, errors.Wrap(err, "validation failed") } + dbSMLTargets := postgres.GetSMLVersionTargets(newCtx, smlVersionID) + + for _, dbSMLTarget := range dbSMLTargets { + found := false + + for _, smlTarget := range smlVersion.Targets { + if dbSMLTarget.TargetName == string(smlTarget.TargetName) { + found = true + } + } + + if !found { + postgres.Delete(newCtx, &dbSMLTarget) + } + } + + for _, smlTarget := range smlVersion.Targets { + postgres.Save(newCtx, &postgres.SMLVersionTarget{ + VersionID: smlVersionID, + TargetName: string(smlTarget.TargetName), + Link: smlTarget.Link, + }) + } + dbSMLVersion := postgres.GetSMLVersionByID(newCtx, smlVersionID) if dbSMLVersion == nil { @@ -85,43 +102,7 @@ func (r *mutationResolver) UpdateSMLVersion(ctx context.Context, smlVersionID st SetStringINNOE(smlVersion.Link, &dbSMLVersion.Link) SetStringINNOE(smlVersion.Changelog, &dbSMLVersion.Changelog) SetDateINN(smlVersion.Date, &dbSMLVersion.Date) - - dbSMLArch := postgres.GetSMLArchBySMLID(newCtx, smlVersionID) - - if len(dbSMLArch) == len(smlVersion.Arch) { - for i, smlArch := range smlVersion.Arch { - SetStringINNOE(&smlArch.Platform, &dbSMLArch[i].Platform) - SetStringINNOE(&smlArch.Link, &dbSMLArch[i].Link) - - postgres.Save(newCtx, dbSMLArch) - } - } else { - for _, smlArch := range dbSMLVersion.Arch { - dbSMLArch := postgres.GetSMLArchBySMLID(newCtx, smlVersionID) - - if dbSMLVersion == nil { - return nil, errors.New("smlArch not found" + smlArch.Platform) - } - - postgres.Delete(newCtx, &dbSMLArch) - } - - for _, smlArch := range smlVersion.Arch { - dbSMLArch := &postgres.SMLArch{ - ID: util.GenerateUniqueID(), - SMLVersionID: smlVersionID, - Platform: smlArch.Platform, - Link: smlArch.Link, - } - - resultSMLArch, err := postgres.CreateSMLArch(newCtx, dbSMLArch) - if err != nil { - return nil, err - } - - DBSMLArchToGenerated(resultSMLArch) - } - } + SetStringINNOE(smlVersion.EngineVersion, &dbSMLVersion.EngineVersion) postgres.Save(newCtx, &dbSMLVersion) @@ -138,14 +119,10 @@ func (r *mutationResolver) DeleteSMLVersion(ctx context.Context, smlVersionID st return false, errors.New("smlVersion not found") } - for _, smlArch := range dbSMLVersion.Arch { - dbSMLArch := postgres.GetSMLArch(newCtx, smlArch.ID) - - if dbSMLVersion == nil { - return false, errors.New("smlArch not found") - } + dbSMLVersionTargets := postgres.GetSMLVersionTargets(newCtx, smlVersionID) - postgres.Delete(newCtx, &dbSMLArch) + for _, dbSMLVersionTarget := range dbSMLVersionTargets { + postgres.Delete(newCtx, &dbSMLVersionTarget) } postgres.Delete(newCtx, &dbSMLVersion) diff --git a/gql/resolver_versions.go b/gql/resolver_versions.go index f551b38..4d2d216 100644 --- a/gql/resolver_versions.go +++ b/gql/resolver_versions.go @@ -184,8 +184,6 @@ func (r *mutationResolver) ApproveVersion(ctx context.Context, versionID string) postgres.Save(newCtx, &mod) go integrations.NewVersion(util.ReWrapCtx(ctx), dbVersion) - go storage.DeleteModArch(ctx, dbVersion.ModID, mod.Name, versionID, "Combined") - go storage.DeleteModArch(ctx, dbVersion.ModID, mod.Name, dbVersion.Version, "Combined") return true, nil } @@ -321,7 +319,27 @@ func (r *getVersionsResolver) Count(ctx context.Context, _ *generated.GetVersion type versionResolver struct{ *Resolver } -func (r *versionResolver) Link(_ context.Context, obj *generated.Version) (string, error) { +func findWindowsTarget(obj *generated.Version) *generated.VersionTarget { + var windowsTarget *generated.VersionTarget + for _, target := range obj.Targets { + if target.TargetName == "Windows" { + windowsTarget = target + break + } + } + return windowsTarget +} + +func (r *versionResolver) Link(ctx context.Context, obj *generated.Version) (string, error) { + wrapper, _ := WrapQueryTrace(ctx, "Version.link") + defer wrapper.end() + + windowsTarget := findWindowsTarget(obj) + if windowsTarget != nil { + link, _ := r.VersionTarget().Link(ctx, windowsTarget) + return link, nil + } + return "/v1/version/" + obj.ID + "/download", nil } @@ -332,6 +350,50 @@ func (r *versionResolver) Mod(ctx context.Context, obj *generated.Version) (*gen return DBModToGenerated(postgres.GetModByID(newCtx, obj.ModID)), nil } +func (r *versionResolver) Hash(ctx context.Context, obj *generated.Version) (*string, error) { + wrapper, _ := WrapQueryTrace(ctx, "Version.hash") + defer wrapper.end() + + hash := "" + + windowsTarget := findWindowsTarget(obj) + if windowsTarget == nil { + if obj.Hash == nil { + return nil, nil + } + hash = *obj.Hash + } else { + if windowsTarget.Hash == nil { + return nil, nil + } + hash = *windowsTarget.Hash + } + + return &hash, nil +} + +func (r *versionResolver) Size(ctx context.Context, obj *generated.Version) (*int, error) { + wrapper, _ := WrapQueryTrace(ctx, "Version.size") + defer wrapper.end() + + size := 0 + + windowsTarget := findWindowsTarget(obj) + if windowsTarget == nil { + if obj.Size == nil { + return nil, nil + } + size = *obj.Size + } else { + if windowsTarget.Size == nil { + return nil, nil + } + size = *windowsTarget.Size + } + + return &size, nil +} + var versionDependencyCache, _ = ristretto.NewCache(&ristretto.Config{ NumCounters: 1e6, // number of keys to track frequency of (1M). MaxCost: 1e6, // maximum cost of cache (1M). @@ -369,6 +431,12 @@ func (r *versionResolver) Dependencies(ctx context.Context, obj *generated.Versi return converted, nil } +type versionTargetResolver struct{ *Resolver } + +func (r *versionTargetResolver) Link(_ context.Context, obj *generated.VersionTarget) (string, error) { + return "/v1/version/" + obj.VersionID + "/" + string(obj.TargetName) + "/download", nil +} + type getMyVersionsResolver struct{ *Resolver } func (r *getMyVersionsResolver) Versions(ctx context.Context, _ *generated.GetMyVersions) ([]*generated.Version, error) { diff --git a/gql/versions.go b/gql/versions.go index 97e2c0a..de9a28b 100644 --- a/gql/versions.go +++ b/gql/versions.go @@ -6,7 +6,6 @@ import ( "io" "time" - "github.com/davecgh/go-spew/spew" "github.com/pkg/errors" "github.com/rs/zerolog/log" @@ -45,10 +44,8 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI modInfo, err := validation.ExtractModInfo(ctx, fileData, true, true, mod.ModReference) if err != nil { - spew.Dump(err) - l.Err(err).Msg("failed extracting mod info") storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) - return nil, err + return nil, errors.Wrap(err, "failed extracting mod info") } if modInfo.ModReference != mod.ModReference { @@ -56,6 +53,16 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI return nil, errors.New("data.json mod_reference does not match mod reference") } + if modInfo.Type == validation.DataJSON { + storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) + return nil, errors.New("data.json mods are obsolete and not allowed") + } + + if modInfo.Type == validation.MultiTargetUEPlugin && !util.FlagEnabled(util.FeatureFlagAllowMultiTargetUpload) { + storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) + return nil, errors.New("multi-target mods are not allowed") + } + versionMajor := int(modInfo.Semver.Major()) versionMinor := int(modInfo.Semver.Minor()) versionPatch := int(modInfo.Semver.Patch()) @@ -67,6 +74,8 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI ModID: mod.ID, Stability: string(version.Stability), ModReference: &modInfo.ModReference, + Size: &modInfo.Size, + Hash: &modInfo.Hash, VersionMajor: &versionMajor, VersionMinor: &versionMinor, VersionPatch: &versionPatch, @@ -120,50 +129,68 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI postgres.Save(ctx, &dbVersion) } - separated := storage.SeparateMod(ctx, fileData, mod.ID, mod.Name, dbVersion.ID, modInfo.Version) + if modInfo.Type == validation.MultiTargetUEPlugin { + targets := make([]*postgres.VersionTarget, 0) - if !separated { - for modID, condition := range modInfo.Dependencies { - dependency := postgres.VersionDependency{ - VersionID: dbVersion.ID, - ModID: modID, - Condition: condition, - Optional: false, + for _, target := range modInfo.Targets { + dbVersionTarget := &postgres.VersionTarget{ + VersionID: dbVersion.ID, + TargetName: target, } - postgres.DeleteForced(ctx, &dependency) + postgres.Save(ctx, dbVersionTarget) + + targets = append(targets, dbVersionTarget) } - for modID, condition := range modInfo.OptionalDependencies { - dependency := postgres.VersionDependency{ - VersionID: dbVersion.ID, - ModID: modID, - Condition: condition, - Optional: true, + separateSuccess := true + for _, target := range targets { + log.Info().Str("target", target.TargetName).Str("mod", mod.Name).Str("version", dbVersion.Version).Msg("separating mod") + success, key, hash, size := storage.SeparateModTarget(ctx, fileData, mod.ID, mod.Name, dbVersion.Version, target.TargetName) + + if !success { + separateSuccess = false + break } - postgres.DeleteForced(ctx, &dependency) + target.Key = key + target.Hash = hash + target.Size = size + + postgres.Save(ctx, target) } - postgres.DeleteForced(ctx, &dbVersion) - storage.DeleteMod(ctx, mod.ID, mod.Name, versionID) + if !separateSuccess { + removeMod(ctx, modInfo, mod, dbVersion) - for _, dbModArch := range dbVersion.Arch { - postgres.DeleteForced(ctx, &dbModArch) + return nil, errors.New("failed to separate mod") } + } + + success, key := storage.RenameVersion(ctx, mod.ID, mod.Name, versionID, modInfo.Version) + + if !success { + removeMod(ctx, modInfo, mod, dbVersion) + return nil, errors.New("failed to upload mod") } - dbModArch := postgres.GetModArchByPlatform(ctx, dbVersion.ID, "WindowsNoEditor") + if modInfo.Type == validation.UEPlugin { + dbVersionTarget := &postgres.VersionTarget{ + VersionID: dbVersion.ID, + TargetName: "Windows", + Key: key, + Hash: *dbVersion.Hash, + Size: *dbVersion.Size, + } + + postgres.Save(ctx, dbVersionTarget) + } - dbVersion.Key = dbModArch.Key - dbVersion.Hash = &dbModArch.Hash - dbVersion.Size = &dbModArch.Size + dbVersion.Key = key postgres.Save(ctx, &dbVersion) postgres.Save(ctx, &mod) - storage.DeleteVersion(ctx, mod.ID, mod.Name, versionID) - if autoApproved { mod := postgres.GetModByID(ctx, dbVersion.ModID) now := time.Now() @@ -171,8 +198,6 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI postgres.Save(ctx, &mod) go integrations.NewVersion(util.ReWrapCtx(ctx), dbVersion) - storage.DeleteModArch(ctx, mod.ID, mod.Name, versionID, "Combined") - storage.DeleteModArch(ctx, mod.ID, mod.Name, dbVersion.Version, "Combined") } else { l.Info().Msg("Submitting version job for virus scan") jobs.SubmitJobScanModOnVirusTotalTask(ctx, mod.ID, dbVersion.ID, true) @@ -183,3 +208,46 @@ func FinalizeVersionUploadAsync(ctx context.Context, mod *postgres.Mod, versionI Version: DBVersionToGenerated(dbVersion), }, nil } + +func removeMod(ctx context.Context, modInfo *validation.ModInfo, mod *postgres.Mod, dbVersion *postgres.Version) { + for modID, condition := range modInfo.Dependencies { + dependency := postgres.VersionDependency{ + VersionID: dbVersion.ID, + ModID: modID, + Condition: condition, + Optional: false, + } + + postgres.DeleteForced(ctx, &dependency) + } + + for modID, condition := range modInfo.OptionalDependencies { + dependency := postgres.VersionDependency{ + VersionID: dbVersion.ID, + ModID: modID, + Condition: condition, + Optional: true, + } + + postgres.DeleteForced(ctx, &dependency) + } + + for _, target := range modInfo.Targets { + dbVersionTarget := postgres.VersionTarget{ + VersionID: dbVersion.ID, + TargetName: target, + } + + postgres.DeleteForced(ctx, &dbVersionTarget) + } + + // For UEPlugin mods, a Windows target is created. + // However, that happens after the last possible call to this function, therefore we can ignore it + + postgres.DeleteForced(ctx, &dbVersion) + + storage.DeleteMod(ctx, mod.ID, mod.Name, dbVersion.ID) + for _, target := range modInfo.Targets { + storage.DeleteModTarget(ctx, mod.ID, mod.Name, dbVersion.ID, target) + } +} diff --git a/gqlgen.yml b/gqlgen.yml index ecb1118..7c2148e 100755 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -20,10 +20,6 @@ models: UpdateMod: model: github.com/satisfactorymodding/smr-api/generated.UpdateMod - ModArchFilter: - model: "map[string]interface{}" - SMLArchFilter: - model: "map[string]interface{}" VersionFilter: model: "map[string]interface{}" ModFilter: @@ -57,11 +53,6 @@ models: latestVersions: resolver: true - ModArch: - fields: - asset: - resolver: true - UserMod: fields: user: @@ -77,6 +68,15 @@ models: resolver: true dependencies: resolver: true + size: + resolver: true + hash: + resolver: true + + VersionTarget: + fields: + link: + resolver: true GetMods: fields: diff --git a/migrations/sql/000022_update_mod_targets.down.sql b/migrations/sql/000022_update_mod_targets.down.sql new file mode 100644 index 0000000..6a19693 --- /dev/null +++ b/migrations/sql/000022_update_mod_targets.down.sql @@ -0,0 +1,64 @@ +-- ID generation -- +-- This is not as random as the original ID, but it should be good enough -- +Create or replace function update_mod_platform_down_random_string(length integer) returns text as +$$ +declare + chars text[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}'; + result text := ''; + i integer := 0; +begin + if length < 0 then + raise exception 'Given length cannot be less than 0'; + end if; + for i in 1..length loop + result := result || chars[1+random()*(array_length(chars, 1)-1)]; + end loop; + return result; +end; +$$ language plpgsql; + +-- Mod version targets -- +ALTER TABLE version_targets RENAME TO mod_archs; + +ALTER TABLE mod_archs + RENAME COLUMN version_id TO mod_version_arch_id; +ALTER TABLE mod_archs + RENAME COLUMN target_name TO platform; +ALTER TABLE mod_archs + ADD COLUMN id varchar(14); + +UPDATE mod_archs SET id = update_mod_platform_down_random_string(14) WHERE true; + +ALTER TABLE mod_archs + ALTER COLUMN id SET NOT NULL; + +ALTER TABLE mod_archs + DROP CONSTRAINT version_targets_version_id_fkey, + DROP CONSTRAINT version_targets_pkey, + ADD CONSTRAINT mod_archs_pkey PRIMARY KEY (id); + +CREATE INDEX IF NOT EXISTS idx_mod_arch_id ON mod_archs (mod_version_arch_id, platform); + +-- SML version targets -- +ALTER TABLE sml_version_targets RENAME TO sml_archs; + +ALTER TABLE sml_archs + RENAME COLUMN version_id TO sml_version_arch_id; +ALTER TABLE sml_archs + RENAME COLUMN target_name TO platform; +ALTER TABLE sml_archs + ADD COLUMN id varchar(14); + +UPDATE sml_archs SET id = update_mod_platform_down_random_string(14) WHERE true; + +ALTER TABLE sml_archs + ALTER COLUMN id SET NOT NULL; + +ALTER TABLE sml_archs + DROP CONSTRAINT sml_version_targets_version_id_fkey, + DROP CONSTRAINT sml_version_targets_pkey, + ADD CONSTRAINT sml_archs_pkey PRIMARY KEY (id); + +CREATE INDEX IF NOT EXISTS idx_sml_archs_id ON sml_archs (sml_version_arch_id, platform); + +DROP FUNCTION update_mod_platform_down_random_string(length integer); \ No newline at end of file diff --git a/migrations/sql/000022_update_mod_targets.up.sql b/migrations/sql/000022_update_mod_targets.up.sql new file mode 100644 index 0000000..687af42 --- /dev/null +++ b/migrations/sql/000022_update_mod_targets.up.sql @@ -0,0 +1,31 @@ +-- Mod version targets -- +ALTER TABLE mod_archs RENAME TO version_targets; + +DROP INDEX idx_mod_arch_id; + +ALTER TABLE version_targets + RENAME COLUMN mod_version_arch_id TO version_id; +ALTER TABLE version_targets + RENAME COLUMN platform TO target_name; +ALTER TABLE version_targets + DROP COLUMN id; + +ALTER TABLE version_targets + ADD CONSTRAINT version_targets_version_id_fkey FOREIGN KEY (version_id) REFERENCES versions (id), + ADD CONSTRAINT version_targets_pkey PRIMARY KEY (version_id, target_name); + +ALTER TABLE sml_archs RENAME TO sml_version_targets; + +-- SML version targets -- +DROP INDEX idx_sml_archs_id; + +ALTER TABLE sml_version_targets + RENAME COLUMN sml_version_arch_id TO version_id; +ALTER TABLE sml_version_targets + RENAME COLUMN platform TO target_name; +ALTER TABLE sml_version_targets + DROP COLUMN id; + +ALTER TABLE sml_version_targets + ADD CONSTRAINT sml_version_targets_version_id_fkey FOREIGN KEY (version_id) REFERENCES sml_versions (id), + ADD CONSTRAINT sml_version_targets_pkey PRIMARY KEY (version_id, target_name); \ No newline at end of file diff --git a/migrations/sql/000023_migrate_versions_to_targets.down.sql b/migrations/sql/000023_migrate_versions_to_targets.down.sql new file mode 100644 index 0000000..702a1db --- /dev/null +++ b/migrations/sql/000023_migrate_versions_to_targets.down.sql @@ -0,0 +1,15 @@ +--Mod Targets-- +DELETE FROM version_targets + USING versions + WHERE version_targets.version_id = versions.id AND + version_targets.target_name = 'Windows' AND + version_targets.key = versions.key AND + version_targets.hash = versions.hash AND + version_targets.size = versions.size; + +--SML Targets-- +DELETE FROM sml_version_targets + USING sml_versions + WHERE sml_version_targets.version_id = sml_versions.id AND + sml_version_targets.target_name = 'Windows' AND + sml_version_targets.link = replace(sml_versions.link, '/tag/', '/download/') || '/SML.zip'; diff --git a/migrations/sql/000023_migrate_versions_to_targets.up.sql b/migrations/sql/000023_migrate_versions_to_targets.up.sql new file mode 100644 index 0000000..94e32af --- /dev/null +++ b/migrations/sql/000023_migrate_versions_to_targets.up.sql @@ -0,0 +1,13 @@ +-- Mod Targets -- +INSERT INTO version_targets (version_id, target_name, key, hash, size) +SELECT id, 'Windows', key, hash, size +FROM versions +WHERE NOT EXISTS(SELECT 1 FROM version_targets WHERE version_targets.version_id = versions.id) +ON CONFLICT DO NOTHING; + +-- SML Targets -- +INSERT INTO sml_version_targets (version_id, target_name, link) +SELECT id, 'Windows', replace(link, '/tag/', '/download/') || '/SML.zip' +FROM sml_versions +WHERE version LIKE '3%' +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/migrations/sql/000024_add_sml_engine_version.down.sql b/migrations/sql/000024_add_sml_engine_version.down.sql new file mode 100644 index 0000000..87f13a5 --- /dev/null +++ b/migrations/sql/000024_add_sml_engine_version.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE sml_versions + DROP COLUMN engine_version; \ No newline at end of file diff --git a/migrations/sql/000024_add_sml_engine_version.up.sql b/migrations/sql/000024_add_sml_engine_version.up.sql new file mode 100644 index 0000000..3a018fe --- /dev/null +++ b/migrations/sql/000024_add_sml_engine_version.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE sml_versions + ADD COLUMN IF NOT EXISTS engine_version varchar(16) default '4.26'; \ No newline at end of file diff --git a/models/filters.go b/models/filters.go index 885e9ce..c7fd5d6 100644 --- a/models/filters.go +++ b/models/filters.go @@ -328,85 +328,3 @@ func ProcessBootstrapVersionFilter(filter map[string]interface{}) (*BootstrapVer return base, nil } - -type SMLArchFilter struct { - Limit *int `json:"limit" validate:"omitempty,min=1,max=100"` - Offset *int `json:"offset" validate:"omitempty,min=0"` - OrderBy *generated.SMLArchFields `json:"order_by"` - Order *generated.Order `json:"order"` - Search *string `json:"search" validate:"omitempty,min=3"` - Ids []string `json:"ids" validate:"omitempty,max=100"` -} - -func DefaultSMLArchFilter() *SMLArchFilter { - limit := 10 - offset := 0 - order := generated.OrderDesc - orderBy := generated.SMLArchFieldsPlatform - return &SMLArchFilter{ - Limit: &limit, - Offset: &offset, - Ids: nil, - Order: &order, - OrderBy: &orderBy, - } -} - -func ProcessSMLArchFilter(filter map[string]interface{}) (*SMLArchFilter, error) { - base := DefaultSMLArchFilter() - - if filter == nil { - return base, nil - } - - if err := ApplyChanges(filter, base); err != nil { - return nil, err - } - - if err := dataValidator.Struct(base); err != nil { - return nil, errors.Wrap(err, "failed to validate SMLArchFilter") - } - - return base, nil -} - -type ModArchFilter struct { - Limit *int `json:"limit" validate:"omitempty,min=1,max=100"` - Offset *int `json:"offset" validate:"omitempty,min=0"` - OrderBy *generated.ModArchFields `json:"order_by"` - Order *generated.Order `json:"order"` - Search *string `json:"search" validate:"omitempty,min=3"` - Ids []string `json:"ids" validate:"omitempty,max=100"` -} - -func DefaultModArchFilter() *ModArchFilter { - limit := 10 - offset := 0 - order := generated.OrderDesc - orderBy := generated.ModArchFieldsPlatform - return &ModArchFilter{ - Limit: &limit, - Offset: &offset, - Ids: nil, - Order: &order, - OrderBy: &orderBy, - } -} - -func ProcessModArchFilter(filter map[string]interface{}) (*ModArchFilter, error) { - base := DefaultModArchFilter() - - if filter == nil { - return base, nil - } - - if err := ApplyChanges(filter, base); err != nil { - return nil, err - } - - if err := dataValidator.Struct(base); err != nil { - return nil, errors.Wrap(err, "failed to validate ModArchFilter") - } - - return base, nil -} diff --git a/nodes/mod.go b/nodes/mod.go index 725fb10..4fdeeb3 100644 --- a/nodes/mod.go +++ b/nodes/mod.go @@ -289,20 +289,20 @@ func downloadModVersion(c echo.Context) error { return c.Redirect(302, storage.GenerateDownloadLink(version.Key)) } -// @Summary Download a Mod Version by Platform +// @Summary Download a Mod Version by TargetName // @Tags Mod -// @Description Download a mod version by mod ID and version ID and Platform +// @Description Download a mod version by mod ID and version ID and TargetName // @Accept json // @Produce json // @Param modId path string true "Mod ID" // @Param versionId path string true "Version ID" -// @Param versionId path string true "Platform" +// @Param target path string true "TargetName" // @Success 200 -// @Router /mod/{modId}/versions/{versionId}/{platform}/download [get] -func downloadModVersionArch(c echo.Context) error { +// @Router /mod/{modId}/versions/{versionId}/{target}/download [get] +func downloadModVersionTarget(c echo.Context) error { modID := c.Param("modId") versionID := c.Param("versionId") - platform := c.Param("platform") + target := c.Param("target") mod := postgres.GetModByID(c.Request().Context(), modID) @@ -316,15 +316,42 @@ func downloadModVersionArch(c echo.Context) error { return c.String(404, "version not found, modID:"+modID+" versionID:"+versionID) } - arch := postgres.GetModArchByPlatform(c.Request().Context(), versionID, platform) + versionTarget := postgres.GetVersionTarget(c.Request().Context(), versionID, target) - if arch == nil { - return c.String(404, "platform not found, modID:"+modID+" versionID:"+versionID+" platform:"+platform) + if versionTarget == nil { + return c.String(404, "target not found, modID:"+modID+" versionID:"+versionID+" target:"+target) } if redis.CanIncrement(c.RealIP(), "download", "version:"+versionID, time.Hour*4) { postgres.IncrementVersionDownloads(c.Request().Context(), version) } - return c.Redirect(302, storage.GenerateDownloadLink(arch.Key)) + return c.Redirect(302, storage.GenerateDownloadLink(versionTarget.Key)) +} + +// @Summary Retrieve all Mod Versions +// @Tags Mod +// @Description Retrieve all mod versions by mod ID +// @Accept json +// @Produce json +// @Param modId path string true "Mod ID" +// @Success 200 +// @Router /mod/{modId}/versions/all [get] +func getAllModVersions(c echo.Context) (interface{}, *ErrorResponse) { + modID := c.Param("modId") + + mod := postgres.GetModByID(c.Request().Context(), modID) + + if mod == nil { + return nil, &ErrorModNotFound + } + + versions := postgres.GetAllModVersionsWithDependencies(c.Request().Context(), mod.ID) + + converted := make([]*Version, len(versions)) + for k, v := range versions { + converted[k] = TinyVersionToVersion(&v) + } + + return converted, nil } diff --git a/nodes/mod_types.go b/nodes/mod_types.go index 6cb453b..25cab07 100644 --- a/nodes/mod_types.go +++ b/nodes/mod_types.go @@ -48,16 +48,60 @@ func ModToMod(mod *postgres.Mod, short bool) *Mod { } type Version struct { - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` - ID string `json:"id"` - Version string `json:"version"` - SMLVersion string `json:"sml_version"` - Changelog string `json:"changelog"` - Stability string `json:"stability"` - ModID string `json:"mod_id"` - Downloads uint `json:"downloads"` - Approved bool `json:"approved"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ID string `json:"id,omitempty"` + Version string `json:"version,omitempty"` + SMLVersion string `json:"sml_version,omitempty"` + Changelog string `json:"changelog,omitempty"` + Stability string `json:"stability,omitempty"` + ModID string `json:"mod_id,omitempty"` + Dependencies []VersionDependency `json:"dependencies,omitempty"` + Targets []VersionTarget `json:"targets,omitempty"` + Downloads uint `json:"downloads,omitempty"` + Approved bool `json:"approved,omitempty"` +} + +type VersionDependency struct { + ModID string `json:"mod_id"` + Condition string `json:"condition"` + Optional bool `json:"optional"` +} + +type VersionTarget struct { + VersionID string `json:"version_id"` + TargetName string `json:"target_name"` + Key string `json:"key"` + Hash string `json:"hash"` + Size int64 `json:"size"` +} + +func TinyVersionToVersion(version *postgres.TinyVersion) *Version { + var dependencies []VersionDependency + if version.Dependencies != nil { + dependencies = make([]VersionDependency, len(version.Dependencies)) + for i, v := range version.Dependencies { + dependencies[i] = VersionDependencyToVersionDependency(v) + } + } + + var targets []VersionTarget + if version.Targets != nil { + targets = make([]VersionTarget, len(version.Targets)) + for i, v := range version.Targets { + targets[i] = VersionTargetToVersionTarget(v) + } + } + + return &Version{ + UpdatedAt: version.UpdatedAt, + CreatedAt: version.CreatedAt, + ID: version.ID, + Version: version.Version, + SMLVersion: version.SMLVersion, + Dependencies: dependencies, + Targets: targets, + } } func VersionToVersion(version *postgres.Version) *Version { @@ -75,6 +119,24 @@ func VersionToVersion(version *postgres.Version) *Version { } } +func VersionDependencyToVersionDependency(version postgres.VersionDependency) VersionDependency { + return VersionDependency{ + ModID: version.ModID, + Condition: version.Condition, + Optional: version.Optional, + } +} + +func VersionTargetToVersionTarget(version postgres.VersionTarget) VersionTarget { + return VersionTarget{ + VersionID: version.VersionID, + TargetName: version.TargetName, + Key: version.Key, + Hash: version.Hash, + Size: version.Size, + } +} + type ModUser struct { UserID string `json:"user_id"` Role string `json:"role"` diff --git a/nodes/routes.go b/nodes/routes.go index 8cd75b8..b6c6cf6 100755 --- a/nodes/routes.go +++ b/nodes/routes.go @@ -13,9 +13,11 @@ func RegisterModRoutes(router *echo.Group) { router.GET("/:modId/versions", dataWrapper(getModVersions)) router.GET("/:modId/authors", dataWrapper(getModAuthors)) + router.GET("/:modId/versions/all", dataWrapper(getAllModVersions)) + router.GET("/:modId/versions/:versionId", dataWrapper(getModVersion)) router.GET("/:modId/versions/:versionId/download", downloadModVersion) - router.GET("/:modId/versions/:versionId/:platform/download", downloadModVersionArch) + router.GET("/:modId/versions/:versionId/:target/download", downloadModVersionTarget) } func RegisterModsRoutes(router *echo.Group) { @@ -48,7 +50,7 @@ func RegisterUsersRoutes(router *echo.Group) { func RegisterVersionRoutes(router *echo.Group) { router.GET("/:versionId", dataWrapper(getVersion)) router.GET("/:versionId/download", downloadVersion) - router.GET("/:versionId/:platform/download", downloadModArch) + router.GET("/:versionId/:target/download", downloadModTarget) } func RegisterSMLRoutes(router *echo.Group) { diff --git a/nodes/version.go b/nodes/version.go index 178064f..1da2eee 100644 --- a/nodes/version.go +++ b/nodes/version.go @@ -54,19 +54,19 @@ func downloadVersion(c echo.Context) error { return c.Redirect(302, storage.GenerateDownloadLink(version.Key)) } -// @Summary Download a Platform +// @Summary Download a TargetName // @Tags Version -// @Tags Platform -// @Description Download a mod version by version ID and Platform +// @Tags TargetName +// @Description Download a mod version by version ID and TargetName // @Accept json // @Produce json // @Param versionId path string true "Version ID" -// @Param versionId path string true "Version ID" +// @Param target path string true "TargetName" // @Success 200 -// @Router /versions/{versionId}/{platform}/download [get] -func downloadModArch(c echo.Context) error { +// @Router /versions/{versionId}/{target}/download [get] +func downloadModTarget(c echo.Context) error { versionID := c.Param("versionId") - platform := c.Param("platform") + target := c.Param("target") version := postgres.GetVersion(c.Request().Context(), versionID) @@ -74,15 +74,15 @@ func downloadModArch(c echo.Context) error { return c.String(404, "version not found, versionID:"+versionID) } - arch := postgres.GetModArchByPlatform(c.Request().Context(), versionID, platform) + versionTarget := postgres.GetVersionTarget(c.Request().Context(), versionID, target) - if arch == nil { - return c.String(404, "platform not found, versionID:"+versionID+" platform:"+platform) + if versionTarget == nil { + return c.String(404, "target not found, versionID:"+versionID+" target:"+target) } if redis.CanIncrement(c.RealIP(), "download", "version:"+versionID, time.Hour*4) { postgres.IncrementVersionDownloads(c.Request().Context(), version) } - return c.Redirect(302, storage.GenerateDownloadLink(arch.Key)) + return c.Redirect(302, storage.GenerateDownloadLink(versionTarget.Key)) } diff --git a/proto/parser/.gitignore b/proto/parser/.gitignore new file mode 100644 index 0000000..9b0b440 --- /dev/null +++ b/proto/parser/.gitignore @@ -0,0 +1 @@ +*.pb.go \ No newline at end of file diff --git a/proto/parser/parser.proto b/proto/parser/parser.proto new file mode 100644 index 0000000..913d5f6 --- /dev/null +++ b/proto/parser/parser.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +option go_package = "github.com/satisfactorymodding/smr-api/proto/parser"; + +service Parser { + rpc Parse (ParseRequest) returns (stream AssetResponse); +} + +message ParseRequest { + bytes zip_data = 1; + string engine_version = 2; +} + +message AssetResponse { + string path = 1; + bytes data = 2; +} \ No newline at end of file diff --git a/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go b/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go index b3d7a76..32ca821 100644 --- a/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go +++ b/redis/jobs/consumers/consumer_scan_mod_on_virus_total.go @@ -38,10 +38,7 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { log.Info().Msgf("starting virus scan of mod %s version %s", task.ModID, task.VersionID) version := postgres.GetVersion(ctx, task.VersionID) - mod := postgres.GetModByID(ctx, version.ModID) - - modArch := postgres.GetModArchByPlatform(ctx, task.VersionID, "Combined") - link := storage.GenerateDownloadLink(modArch.Key) + link := storage.GenerateDownloadLink(version.Key) response, _ := http.Get(link) @@ -92,9 +89,5 @@ func ScanModOnVirusTotalConsumer(ctx context.Context, payload []byte) error { go integrations.NewVersion(util.ReWrapCtx(ctx), version) } - go storage.DeleteModArch(ctx, mod.ID, mod.Name, version.ID, "Combined") - go storage.DeleteModArch(ctx, mod.ID, mod.Name, version.Version, "Combined") - go postgres.Delete(ctx, modArch) - return nil } diff --git a/schemas/mod_archs.graphql b/schemas/mod_archs.graphql deleted file mode 100644 index 648fc18..0000000 --- a/schemas/mod_archs.graphql +++ /dev/null @@ -1,38 +0,0 @@ -### Types - -scalar ModArchID - -type ModArch { - id: ModArchID! - ModVersionID: String! - platform: String! - asset: String! - size: Int - hash: String -} - -enum ModArchFields { - platform -} - -type GetModArchs { - arch: [ModArch!]! -} - -### Inputs - -input ModArchFilter { - limit: Int - offset: Int - order_by: ModArchFields - order: Order - search: String - ids: [String!] -} - -### Queries - -extend type Query { - getModArch(id: ModArchID!): ModArch - getModArchs(filter: ModArchFilter): GetModArchs! -} \ No newline at end of file diff --git a/schemas/sml_archs.graphql b/schemas/sml_archs.graphql deleted file mode 100644 index e3cdb32..0000000 --- a/schemas/sml_archs.graphql +++ /dev/null @@ -1,46 +0,0 @@ -### Types - -scalar SMLArchID - -type SMLArch { - id: SMLArchID! - SMLVersionID: String! - platform: String! - link: String! -} - -enum SMLArchFields { - platform -} - -type GetSMLArchs { - arch: [SMLArch!]! -} - -### Inputs - -input SMLArchFilter { - limit: Int - offset: Int - order_by: SMLArchFields - order: Order - search: String - ids: [String!] -} - -input NewSMLArch { - platform: String! - link: String! -} - -input UpdateSMLArch { - platform: String! - link: String! -} - -### Queries - -extend type Query { - getSMLArch(smlVersionId: SMLVersionID!): SMLArch - getSMLArchs(filter: SMLArchFilter): GetSMLArchs! -} \ No newline at end of file diff --git a/schemas/sml_version.graphql b/schemas/sml_version.graphql index ef207ca..9299c3f 100755 --- a/schemas/sml_version.graphql +++ b/schemas/sml_version.graphql @@ -8,15 +8,22 @@ type SMLVersion { satisfactory_version: Int! stability: VersionStabilities! link: String! - arch: [SMLArch]! + targets: [SMLVersionTarget]! changelog: String! date: Date! bootstrap_version: String + engine_version: String! updated_at: Date! created_at: Date! } +type SMLVersionTarget { + VersionID: SMLVersionID! + targetName: TargetName! + link: String! +} + type GetSMLVersions { sml_versions: [SMLVersion!]! count: Int! @@ -37,10 +44,11 @@ input NewSMLVersion { satisfactory_version: Int! stability: VersionStabilities! link: String! - arch: [NewSMLArch!]! + targets: [NewSMLVersionTarget!]! changelog: String! date: Date! bootstrap_version: String + engine_version: String! } input UpdateSMLVersion { @@ -48,10 +56,21 @@ input UpdateSMLVersion { satisfactory_version: Int stability: VersionStabilities link: String - arch: [UpdateSMLArch]! + targets: [UpdateSMLVersionTarget]! changelog: String date: Date bootstrap_version: String + engine_version: String +} + +input NewSMLVersionTarget { + targetName: TargetName! + link: String! +} + +input UpdateSMLVersionTarget { + targetName: TargetName! + link: String! } input SMLVersionFilter { diff --git a/schemas/version.graphql b/schemas/version.graphql index 2513639..168d0de 100755 --- a/schemas/version.graphql +++ b/schemas/version.graphql @@ -32,7 +32,7 @@ type Version { updated_at: Date! created_at: Date! link: String! - arch: [ModArch]! + targets: [VersionTarget]! metadata: String size: Int hash: String @@ -41,6 +41,14 @@ type Version { dependencies: [VersionDependency!]! } +type VersionTarget { + VersionID: VersionID! + targetName: TargetName! + link: String! + size: Int + hash: String +} + type CreateVersionResponse { auto_approved: Boolean! version: Version diff --git a/schemas/version_target.graphql b/schemas/version_target.graphql new file mode 100644 index 0000000..2d77e3d --- /dev/null +++ b/schemas/version_target.graphql @@ -0,0 +1,5 @@ +enum TargetName { + Windows, + WindowsServer, + LinuxServer +} \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..216a4f8 --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + nativeBuildInputs = with pkgs.buildPackages; [ + libwebp + go + protobuf + protoc-gen-go-grpc + minio-client + ]; +} diff --git a/storage/b2.go b/storage/b2.go index 10ec7ad..a616608 100644 --- a/storage/b2.go +++ b/storage/b2.go @@ -222,3 +222,7 @@ func (b2o *B2) Meta(key string) (*ObjectMeta, error) { ContentType: data.ContentType, }, nil } + +func (b2o *B2) List(key string) ([]Object, error) { + return nil, nil // no-op +} diff --git a/storage/s3.go b/storage/s3.go index d8c09c7..b9d9752 100644 --- a/storage/s3.go +++ b/storage/s3.go @@ -203,6 +203,15 @@ func (s3o *S3) Delete(key string) error { } if len(objects) == 0 { + _, err = s3o.S3Client.DeleteObject(&s3.DeleteObjectInput{ + Bucket: aws.String(viper.GetString("storage.bucket")), + Key: aws.String(cleanedKey), + }) + + if err != nil { + return errors.Wrap(err, "failed to delete objects") + } + return nil } @@ -237,3 +246,23 @@ func (s3o *S3) Meta(key string) (*ObjectMeta, error) { ContentType: data.ContentType, }, nil } + +func (s3o *S3) List(prefix string) ([]Object, error) { + objects, err := s3o.S3Client.ListObjects(&s3.ListObjectsInput{ + Bucket: aws.String(viper.GetString("storage.bucket")), + Prefix: aws.String(prefix), + }) + if err != nil { + return nil, errors.Wrap(err, "failed to list objects") + } + + out := make([]Object, len(objects.Contents)) + for i, obj := range objects.Contents { + out[i] = Object{ + Key: obj.Key, + LastModified: obj.LastModified, + } + } + + return out, nil +} diff --git a/storage/storage.go b/storage/storage.go index af214dd..5597aaa 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -9,13 +9,12 @@ import ( "fmt" "io" "strings" + "time" "github.com/avast/retry-go/v3" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" - - "github.com/satisfactorymodding/smr-api/db/postgres" ) type Storage interface { @@ -29,6 +28,7 @@ type Storage interface { Rename(from string, to string) error Delete(key string) error Meta(key string) (*ObjectMeta, error) + List(key string) ([]Object, error) } type ObjectMeta struct { @@ -36,6 +36,11 @@ type ObjectMeta struct { ContentType *string } +type Object struct { + Key *string + LastModified *time.Time +} + type Config struct { Type string `json:"type"` Bucket string `json:"bucket"` @@ -273,7 +278,7 @@ func RenameVersion(ctx context.Context, modID string, name string, versionID str return true, fmt.Sprintf("/mods/%s/%s.smod", modID, EncodeName(cleanName)+"-"+version) } -func DeleteVersion(ctx context.Context, modID string, name string, versionID string) bool { +func DeleteMod(ctx context.Context, modID string, name string, versionID string) bool { if storage == nil { return false } @@ -291,45 +296,17 @@ func DeleteVersion(ctx context.Context, modID string, name string, versionID str return true } -func DeleteMod(ctx context.Context, modID string, name string, versionID string) bool { +func DeleteModTarget(ctx context.Context, modID string, name string, versionID string, target string) bool { if storage == nil { return false } cleanName := cleanModName(name) + key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+target+"-"+versionID) - query := postgres.GetModVersion(ctx, modID, versionID) - - if query != nil && len(query.Arch) != 0 { - for _, link := range query.Arch { - if success := DeleteModArch(ctx, modID, cleanName, versionID, link.Platform); !success { - return false - } - } - } else { - key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+versionID) - - log.Info().Str("key", key).Msg("deleting mod") - if err := storage.Delete(key); err != nil { - log.Ctx(ctx).Err(err).Msg("failed to delete version") - return false - } - } - - return true -} - -func DeleteModArch(ctx context.Context, modID string, name string, versionID string, platform string) bool { - if storage == nil { - return false - } - - cleanName := cleanModName(name) - key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+platform+"-"+versionID) - - log.Info().Str("key", key).Msg("deleting mod arch") + log.Info().Str("key", key).Msg("deleting mod target") if err := storage.Delete(key); err != nil { - log.Ctx(ctx).Err(err).Msg("failed to delete version link") + log.Err(err).Msg("failed to delete version target") return false } @@ -396,105 +373,107 @@ func EncodeName(name string) string { return result } -func SeparateMod(ctx context.Context, body []byte, modID, name string, versionID string, modVersion string) bool { +func SeparateModTarget(ctx context.Context, body []byte, modID, name, modVersion, target string) (bool, string, string, int64) { zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) if err != nil { - return false + return false, "", "", 0 } - ModPlatforms := []string{"Combined", "WindowsNoEditor", "WindowsServer", "LinuxServer"} cleanName := cleanModName(name) - bufPlatform := bytes.NewBuffer(body) - - for _, ModPlatform := range ModPlatforms { - if ModPlatform != "Combined" { - bufPlatform = new(bytes.Buffer) - zipWriter := zip.NewWriter(bufPlatform) - for _, file := range zipReader.File { - if strings.HasPrefix(file.Name, ".pdb") || strings.HasPrefix(file.Name, ".debug") || !strings.Contains(file.Name, ModPlatform) { - continue - } - - err = WriteZipFile(ctx, file, ModPlatform, zipWriter) - - if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to write zip to " + ModPlatform + " smod") - return false - } - } + buf := new(bytes.Buffer) + zipWriter := zip.NewWriter(buf) - zipWriter.Close() + for _, file := range zipReader.File { + if !strings.HasPrefix(file.Name, target+"/") && file.Name != target+"/" { + continue } - key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+ModPlatform+"-"+modVersion) + err = copyModFileToArchZip(file, zipWriter, strings.TrimPrefix(file.Name, target+"/")) - err = WriteModArch(ctx, key, versionID, ModPlatform, bufPlatform) if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to save " + ModPlatform + " smod") - return false + log.Err(err).Msg("failed to add file to " + target + " archive") + return false, "", "", 0 } } - return true + zipWriter.Close() + + key := fmt.Sprintf("/mods/%s/%s.smod", modID, cleanName+"-"+target+"-"+modVersion) + + _, err = storage.Put(ctx, key, bytes.NewReader(buf.Bytes())) + if err != nil { + log.Err(err).Msg("failed to save " + target + " archive") + return false, "", "", 0 + } + + hash := sha256.New() + hash.Write(buf.Bytes()) + + return true, key, hex.EncodeToString(hash.Sum(nil)), int64(buf.Len()) } -func WriteZipFile(ctx context.Context, file *zip.File, platform string, zipWriter *zip.Writer) error { - fileName := strings.ReplaceAll(file.Name, platform+"/", "") - zipFile, err := zipWriter.Create(fileName) +func copyModFileToArchZip(file *zip.File, zipWriter *zip.Writer, newName string) error { + fileHeader := file.FileHeader + fileHeader.Name = newName + + zipFile, err := zipWriter.CreateHeader(&fileHeader) if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to create smod file for " + platform) - return errors.Wrap(err, "Failed to open smod file for "+platform) + return errors.Wrap(err, "failed to create file") } rawFile, err := file.Open() if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to open smod file for " + platform) - return errors.Wrap(err, "Failed to open smod file for "+platform) + return errors.Wrap(err, "failed to open file") } + defer rawFile.Close() buf := new(bytes.Buffer) _, err = buf.ReadFrom(rawFile) if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to read from buffer for " + platform) - return errors.Wrap(err, "Failed to read from buffer for "+platform) + return errors.Wrap(err, "failed to read file") } _, err = zipFile.Write(buf.Bytes()) if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to write to smod file: " + platform) - return errors.Wrap(err, "Failed to write smod file for "+platform) + return errors.Wrap(err, "failed to write file") } return nil } -func WriteModArch(ctx context.Context, key string, versionID string, platform string, buffer *bytes.Buffer) error { - _, err := storage.Put(ctx, key, bytes.NewReader(buffer.Bytes())) +func DeleteOldModAssets(modReference string, before time.Time) { + list, err := storage.List(fmt.Sprintf("/assets/mods/%s", modReference)) if err != nil { - log.Ctx(ctx).Err(err).Msg("failed to write smod: " + key) - return errors.Wrap(err, "Failed to load smod:"+key) + log.Err(err).Msg("failed to list assets") + return } - hash := sha256.New() - hash.Write(buffer.Bytes()) + for _, object := range list { + if object.Key == nil { + continue + } - dbModArch := &postgres.ModArch{ - ModVersionID: versionID, - Platform: platform, - Key: key, - Hash: hex.EncodeToString(hash.Sum(nil)), - Size: int64(len(buffer.Bytes())), + if object.LastModified == nil || object.LastModified.Before(before) { + if err := storage.Delete(*object.Key); err != nil { + log.Err(err).Str("key", *object.Key).Msg("failed deleting old asset") + return + } + } } +} - _, err = postgres.CreateModArch(ctx, dbModArch) +func UploadModAsset(ctx context.Context, modReference string, path string, data []byte) { + if storage == nil { + return + } + + key := fmt.Sprintf("/assets/mods/%s/%s", modReference, strings.TrimPrefix(path, "/")) + _, err := storage.Put(ctx, key, bytes.NewReader(data)) if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to create ModArch: " + versionID + "-" + platform) - return errors.Wrap(err, "Failed to create ModArch: "+versionID+"-"+platform) + log.Err(err).Str("path", path).Msg("failed to upload mod asset") } - - return nil } diff --git a/storage/wasabi.go b/storage/wasabi.go index 3a0a015..c35b864 100644 --- a/storage/wasabi.go +++ b/storage/wasabi.go @@ -118,3 +118,7 @@ func (wasabi *Wasabi) Delete(key string) error { func (wasabi *Wasabi) Meta(key string) (*ObjectMeta, error) { return nil, errors.New("Unsupported") } + +func (wasabi *Wasabi) List(key string) ([]Object, error) { + return nil, errors.New("Unsupported") +} diff --git a/tools.go b/tools.go index 0a80bbc..bf72045 100644 --- a/tools.go +++ b/tools.go @@ -6,5 +6,6 @@ package smr import _ "github.com/99designs/gqlgen" import _ "github.com/swaggo/swag/cmd/swag" +//go:generate protoc -I./proto --go_out=./proto --go_opt=paths=source_relative --go-grpc_out=./proto --go-grpc_opt=paths=source_relative proto/parser/parser.proto //go:generate go run github.com/99designs/gqlgen generate //go:generate go run github.com/swaggo/swag/cmd/swag init --generalInfo cmd/api/serve.go diff --git a/util/converter/converter_windows.go b/util/converter/converter_windows.go index 02a77a7..9ab1eb7 100755 --- a/util/converter/converter_windows.go +++ b/util/converter/converter_windows.go @@ -2,28 +2,39 @@ package converter import ( "bytes" + "context" + "image" + "github.com/chai2010/webp" "github.com/pkg/errors" "github.com/rs/zerolog/log" - "image" + + // GIF Support _ "image/gif" + // JPEG Support _ "image/jpeg" + // PNG Support _ "image/png" ) -func ConvertAnyImageToWebp(imageAsBytes []byte) ([]byte, error) { - imageData, _, err := image.Decode(bytes.NewReader(imageAsBytes)) - +func ConvertAnyImageToWebp(ctx context.Context, imageAsBytes []byte) ([]byte, error) { + imageData, imageType, err := image.Decode(bytes.NewReader(imageAsBytes)) if err != nil { - err := errors.Wrap(err, "error converting image to webp") - log.Error(err) - return nil, err + message := "error converting image to webp" + log.Err(err).Msg(message) + return nil, errors.Wrap(err, message) } result := bytes.NewBuffer(make([]byte, 0)) + if imageType == "gif" { + message := "converting gif to webp not supported on windows" + log.Err(err).Msg(message) + return nil, errors.Wrap(err, message) + } + if err := webp.Encode(result, imageData, nil); err != nil { - return nil, err + return nil, errors.Wrap(err, "error converting image to webp") } return result.Bytes(), nil diff --git a/util/flags.go b/util/flags.go new file mode 100644 index 0000000..196968c --- /dev/null +++ b/util/flags.go @@ -0,0 +1,13 @@ +package util + +import "github.com/spf13/viper" + +type FeatureFlag string + +const ( + FeatureFlagAllowMultiTargetUpload = "allow_multi_target_upload" +) + +func FlagEnabled(flag FeatureFlag) bool { + return viper.GetBool("feature_flags." + string(flag)) +} diff --git a/validation/extractor.go b/validation/extractor.go new file mode 100644 index 0000000..9526e7b --- /dev/null +++ b/validation/extractor.go @@ -0,0 +1,92 @@ +package validation + +import ( + "encoding/json" + "fmt" + "regexp" +) + +func ExtractMetadata(raw []byte) (map[string]map[string][]interface{}, error) { + meta := make(map[string][]map[string]interface{}) + + if err := json.Unmarshal(raw, &meta); err != nil { + return nil, fmt.Errorf("failed extracting meta: %w", err) + } + + out := make(map[string]map[string][]interface{}) + + for fileName, data := range meta { + bpTypes := make(map[string]string) + for i, obj := range data { + if i == 0 && obj["Type"] != "BlueprintGeneratedClass" { + break + } + + if obj["Type"] == "BlueprintGeneratedClass" { + superName := obj["SuperStruct"].(map[string]interface{})["ObjectName"].(string) + _, objName := splitName(superName) + bpTypes[obj["Name"].(string)] = objName + continue + } + + if obj["Properties"] != nil { + classType := obj["Type"].(string) + if _, ok := ignoredClasses[classType]; ok { + continue + } + + if _, ok := out[fileName]; !ok { + out[fileName] = make(map[string][]interface{}) + } + + typ := bpTypes[classType] + if typ == "" { + typ = classType + } + + out[fileName][typ] = append(out[fileName][typ], rewriteRecursive(obj["Properties"])) + } + } + } + + return out, nil +} + +var objNameRegex = regexp.MustCompile(`^(.+?)'(.+?)'$`) + +func splitName(n string) (string, string) { + matches := objNameRegex.FindStringSubmatch(n) + return matches[1], matches[2] +} + +func rewriteRecursive(obj interface{}) interface{} { + switch b := obj.(type) { + case map[string]interface{}: + if mapHas("CultureInvariantString", b) { + return b["CultureInvariantString"] + } else if mapHas("ObjectName", b) && mapHas("ObjectPath", b) { + _, val := splitName(b["ObjectName"].(string)) + return val + } else if mapHas("AssetPathName", b) && mapHas("SubPathString", b) { + return b["AssetPathName"] + } else { + newOut := make(map[string]interface{}) + for k, v := range b { + newOut[k] = rewriteRecursive(v) + } + return newOut + } + case []interface{}: + newOut := make([]interface{}, len(b)) + for i, v := range b { + newOut[i] = rewriteRecursive(v) + } + return newOut + } + return obj +} + +func mapHas(key string, mp map[string]interface{}) bool { + _, ok := mp[key] + return ok +} diff --git a/validation/validation.go b/validation/validation.go index 839317b..813eb35 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -9,22 +9,40 @@ import ( "encoding/json" "fmt" "io" + "path" "path/filepath" + "sort" "strconv" "strings" + "time" "github.com/Masterminds/semver/v3" - "github.com/Vilsol/ue4pak/parser" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/xeipuuv/gojsonschema" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/satisfactorymodding/smr-api/db/postgres" + "github.com/satisfactorymodding/smr-api/proto/parser" + "github.com/satisfactorymodding/smr-api/storage" ) +var AllowedTargets = []string{"Windows", "WindowsServer", "LinuxServer"} + type ModObject struct { Path string `json:"path"` Type string `json:"type"` } +type ModType int + +const ( + DataJSON ModType = iota + UEPlugin = 1 + MultiTargetUEPlugin = 2 +) + type ModInfo struct { Dependencies map[string]string `json:"dependencies"` OptionalDependencies map[string]string `json:"optional_dependencies"` @@ -35,7 +53,9 @@ type ModInfo struct { SMLVersion string `json:"sml_version"` Objects []ModObject `json:"objects"` Metadata []map[string]map[string][]interface{} `json:"-"` + Targets []string `json:"-"` Size int64 `json:"-"` + Type ModType `json:"-"` } var ( @@ -78,7 +98,7 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal dataFile = v break } - if v.Name == "WindowsNoEditor/"+modReference+".uplugin" { + if v.Name == modReference+".uplugin" { uPlugin = v break } @@ -101,44 +121,90 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal } if modInfo == nil { - return nil, errors.New("missing WindowsNoEditor/" + modReference + ".uplugin or data.json") + // Neither data.json nor .uplugin found, try multi-target .uplugin + modInfo, err = validateMultiTargetPlugin(archive, withValidation, modReference) + if err != nil { + return nil, err + } + } + + if modInfo == nil { + return nil, errors.New("missing " + modReference + ".uplugin or data.json") } if withMetadata { // Extract all possible metadata - modInfo.Metadata = make([]map[string]map[string][]interface{}, 0) - for _, obj := range modInfo.Objects { - if strings.ToLower(obj.Type) == "pak" { - for _, archiveFile := range archive.File { - if obj.Path == archiveFile.Name { - data, err := archiveFile.Open() - if err != nil { - log.Err(err).Msg("failed opening archive file") - break - } - - pakData, err := io.ReadAll(data) - if err != nil { - log.Err(err).Msg("failed reading archive file") - break - } - - reader := &parser.PakByteReader{ - Bytes: pakData, - } - - pak, err := AttemptExtractDataFromPak(ctx, reader) - if err != nil { - log.Err(err).Msg("failed parsing archive file") - break - } - - modInfo.Metadata = append(modInfo.Metadata, pak) - break - } + conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, errors.Wrap(err, "failed to connect to metadata server") + } + defer conn.Close() + + engineVersion := "4.26" + + //nolint + if postgres.DBCtx(nil) != nil { + smlVersions := postgres.GetSMLVersions(ctx, nil) + + // Sort decrementing by version + sort.Slice(smlVersions, func(a, b int) bool { + return semver.MustParse(smlVersions[a].Version).Compare(semver.MustParse(smlVersions[b].Version)) > 0 + }) + + for _, version := range smlVersions { + constraint, err := semver.NewConstraint(modInfo.SMLVersion) + if err != nil { + return nil, errors.Wrap(err, "failed to create semver constraint") + } + + if constraint.Check(semver.MustParse(version.Version)) { + engineVersion = version.EngineVersion + break } } } + + parserClient := parser.NewParserClient(conn) + stream, err := parserClient.Parse(ctx, &parser.ParseRequest{ + ZipData: body, + EngineVersion: engineVersion, + }, + grpc.MaxCallSendMsgSize(1024*1024*1024), // 1GB + grpc.MaxCallRecvMsgSize(1024*1024*1024), // 1GB + ) + if err != nil { + return nil, errors.Wrap(err, "failed to parse mod") + } + + defer func(stream parser.Parser_ParseClient) { + err := stream.CloseSend() + if err != nil { + log.Ctx(ctx).Err(err).Msg("failed closing parser stream") + } + }(stream) + + beforeUpload := time.Now().Add(-time.Minute) + for { + asset, err := stream.Recv() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return nil, errors.Wrap(err, "failed reading parser stream") + } + + if asset.Path == "metadata.json" { + out, err := ExtractMetadata(asset.Data) + if err != nil { + return nil, err + } + modInfo.Metadata = append(modInfo.Metadata, out) + } + + storage.UploadModAsset(ctx, modInfo.ModReference, asset.GetPath(), asset.GetData()) + } + + storage.DeleteOldModAssets(modInfo.ModReference, beforeUpload) } modInfo.Size = int64(len(body)) @@ -244,6 +310,8 @@ func validateDataJSON(archive *zip.Reader, dataFile *zip.File, withValidation bo } } + modInfo.Type = DataJSON + return &modInfo, nil } @@ -357,5 +425,81 @@ func validateUPluginJSON(archive *zip.Reader, uPluginFile *zip.File, withValidat return nil, errors.New(uPluginFile.Name + " doesn't contain SML as a dependency.") } + modInfo.Type = UEPlugin + return &modInfo, nil } + +func validateMultiTargetPlugin(archive *zip.Reader, withValidation bool, modReference string) (*ModInfo, error) { + var targets []string + var uPluginFiles []*zip.File + for _, file := range archive.File { + if path.Base(file.Name) == modReference+".uplugin" && path.Dir(file.Name) != "." { + targets = append(targets, path.Dir(file.Name)) + uPluginFiles = append(uPluginFiles, file) + } + } + + if withValidation { + for _, target := range targets { + found := false + for _, allowedTarget := range AllowedTargets { + if target == allowedTarget { + found = true + break + } + } + if !found { + return nil, errors.New("multi-target plugin contains invalid target: " + target) + } + } + + for _, file := range archive.File { + found := false + for _, target := range targets { + if strings.HasPrefix(file.Name, target+"/") { + found = true + break + } + } + if !found { + return nil, errors.New("multi-target plugin contains file outside of target directories: " + file.Name) + } + } + } + + if len(uPluginFiles) == 0 { + return nil, errors.New("multi-target plugin doesn't contain any .uplugin files") + } + + if withValidation { + var lastData []byte + for _, uPluginFile := range uPluginFiles { + file, err := uPluginFile.Open() + if err != nil { + return nil, errors.Wrap(err, "failed to open .uplugin file") + } + data, err := io.ReadAll(file) + file.Close() + if err != nil { + return nil, errors.Wrap(err, "failed to read .uplugin file") + } + + if lastData != nil && !bytes.Equal(lastData, data) { + return nil, errors.New("multi-target plugin contains different .uplugin files") + } + lastData = data + } + } + + // All the .uplugin files should be the same at this point (assuming validation is enabled) + modInfo, err := validateUPluginJSON(archive, uPluginFiles[0], withValidation, modReference) + if err != nil { + return nil, errors.Wrap(err, "failed to validate multi-target plugin") + } + + modInfo.Targets = targets + modInfo.Type = MultiTargetUEPlugin + + return modInfo, nil +} diff --git a/validation/virustotal.go b/validation/virustotal.go index f9104ed..bbfe8dc 100644 --- a/validation/virustotal.go +++ b/validation/virustotal.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "golang.org/x/sync/errgroup" ) var client *vt.Client @@ -32,44 +33,83 @@ type AnalysisResults struct { } func ScanFiles(ctx context.Context, files []io.Reader, names []string) (bool, error) { - for i, file := range files { - scan, err := client.NewFileScanner().Scan(file, names[i], nil) - if err != nil { - return false, errors.Wrap(err, "failed to scan file") + errs, gctx := errgroup.WithContext(context.Background()) + fileCount := len(files) + + c := make(chan bool) + + for i := 0; i < fileCount; i++ { + count := i + errs.Go(func() error { + ok, err := scanFile(gctx, files[count], names[count]) + if err != nil { + return errors.Wrap(err, "failed to scan file") + } + c <- ok + return nil + }) + } + go func() { + _ = errs.Wait() + close(c) + }() + + success := true + for res := range c { + if !res { + success = false + break } + } - analysisID := scan.ID() + if err := errs.Wait(); err != nil { + return false, errors.Wrap(err, "failed to scan file") + } - log.Info().Msgf("uploaded virus scan for file %s and analysis ID: %s", names[i], analysisID) + return success, nil +} - for { - time.Sleep(time.Second * 15) +func scanFile(ctx context.Context, file io.Reader, name string) (bool, error) { + scan, err := client.NewFileScanner().Scan(file, name, nil) + if err != nil { + return false, errors.Wrap(err, "failed to scan file") + } - var target AnalysisResults - _, err = client.GetData(vt.URL("analyses/%s", analysisID), &target) + analysisID := scan.ID() - if err != nil { - return false, errors.Wrap(err, "failed to get analysis results") - } + log.Info().Msgf("uploaded virus scan for file %s and analysis ID: %s", name, analysisID) - if target.Attributes.Status != "completed" { - continue - } + for { + time.Sleep(time.Second * 15) - if target.Attributes.Stats == nil { - return false, nil - } + var target AnalysisResults + _, err = client.GetData(vt.URL("analyses/%s", analysisID), &target) - if target.Attributes.Stats.Malicious == nil || target.Attributes.Stats.Suspicious == nil { - return false, nil - } + if err != nil { + return false, errors.Wrap(err, "failed to get analysis results") + } - if *target.Attributes.Stats.Malicious > 0 || *target.Attributes.Stats.Suspicious > 0 { - return false, nil - } + if target.Attributes.Status != "completed" { + continue + } - break + if target.Attributes.Stats == nil { + log.Error().Msgf("no stats available. failing file: %s", name) + return false, nil + } + + if target.Attributes.Stats.Malicious == nil || target.Attributes.Stats.Suspicious == nil { + log.Error().Msgf("unable to determine malicious or suspicious File: %s", name) + return false, nil } + + // Why 1? Well because some company made a shitty AI and it flags random mods. + if *target.Attributes.Stats.Malicious > 1 || *target.Attributes.Stats.Suspicious > 1 { + log.Error().Msgf("suspicious or malicious file found: %s", name) + return false, nil + } + + break } return true, nil From 11cb63bd6c00eb711bcd9034e0aa2125a25b6df0 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Fri, 20 Oct 2023 03:10:47 +0300 Subject: [PATCH 14/21] fix: configurable extractor address --- config/config.go | 2 ++ validation/validation.go | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 8e0277c..1fd6ea3 100644 --- a/config/config.go +++ b/config/config.go @@ -102,4 +102,6 @@ func initializeDefaults() { viper.SetDefault("virustotal.key", "") viper.SetDefault("feature_flags.allow_multi_target_upload", false) + + viper.SetDefault("extractor_host", "localhost:50051") } diff --git a/validation/validation.go b/validation/validation.go index 813eb35..df043c2 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/spf13/viper" "io" "path" "path/filepath" @@ -134,7 +135,7 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal if withMetadata { // Extract all possible metadata - conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.Dial(viper.GetString("extractor_host"), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, errors.Wrap(err, "failed to connect to metadata server") } From 9dab1f4b779c598b119c1a295cffc674f3fc47ab Mon Sep 17 00:00:00 2001 From: Vilsol Date: Fri, 20 Oct 2023 03:19:20 +0300 Subject: [PATCH 15/21] chore: lint --- validation/validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validation.go b/validation/validation.go index df043c2..d872335 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -8,7 +8,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/spf13/viper" "io" "path" "path/filepath" @@ -20,6 +19,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/pkg/errors" "github.com/rs/zerolog/log" + "github.com/spf13/viper" "github.com/xeipuuv/gojsonschema" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" From 9f7a8609551b06a19bf736024b3f5dc0585f1f2d Mon Sep 17 00:00:00 2001 From: Vilsol Date: Fri, 20 Oct 2023 04:34:54 +0300 Subject: [PATCH 16/21] fix: logging, error checks --- validation/validation.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/validation/validation.go b/validation/validation.go index d872335..af9cad4 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -188,12 +188,15 @@ func ExtractModInfo(ctx context.Context, body []byte, withMetadata bool, withVal for { asset, err := stream.Recv() if err != nil { - if errors.Is(err, io.EOF) { + //nolint + if errors.Is(err, io.EOF) || err == io.EOF { break } return nil, errors.Wrap(err, "failed reading parser stream") } + log.Ctx(ctx).Info().Str("path", asset.GetPath()).Msg("received asset from parser") + if asset.Path == "metadata.json" { out, err := ExtractMetadata(asset.Data) if err != nil { From 1f2409f74de88a6e4e89fb365bdfc460c3909071 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Fri, 20 Oct 2023 05:16:36 +0300 Subject: [PATCH 17/21] fix: print feature flags --- api.go | 1 + util/flags.go | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/api.go b/api.go index d6745b4..ed58933 100644 --- a/api.go +++ b/api.go @@ -79,6 +79,7 @@ func Initialize(baseCtx context.Context) context.Context { auth.InitializeAuth() jobs.InitializeJobs(ctx) validation.InitializeVirusTotal() + util.PrintFeatureFlags() return ctx } diff --git a/util/flags.go b/util/flags.go index 196968c..7d6edea 100644 --- a/util/flags.go +++ b/util/flags.go @@ -1,6 +1,9 @@ package util -import "github.com/spf13/viper" +import ( + "github.com/rs/zerolog/log" + "github.com/spf13/viper" +) type FeatureFlag string @@ -11,3 +14,9 @@ const ( func FlagEnabled(flag FeatureFlag) bool { return viper.GetBool("feature_flags." + string(flag)) } + +func PrintFeatureFlags() { + for _, flag := range []FeatureFlag{FeatureFlagAllowMultiTargetUpload} { + log.Info().Str("flag", string(flag)).Bool("enabled", FlagEnabled(flag)).Msg("flag") + } +} From 54d096cb34082c4a7bee6d79b67aa4f235386fa0 Mon Sep 17 00:00:00 2001 From: Rob B Date: Fri, 20 Oct 2023 09:06:47 -0400 Subject: [PATCH 18/21] feat: modref blacklist (#29) * feat: prevent certain mod references from being used * chore: switch to map[string]struct{} * chore: switch to map[string]bool to simplify code --- gql/resolver_mods.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gql/resolver_mods.go b/gql/resolver_mods.go index 5094f3c..46ffaf3 100644 --- a/gql/resolver_mods.go +++ b/gql/resolver_mods.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "io" + "strings" "time" "github.com/99designs/gqlgen/graphql" @@ -22,6 +23,15 @@ import ( "github.com/satisfactorymodding/smr-api/util/converter" ) +var DisallowedModReferences = map[string]bool{ + "satisfactory": true, + "factorygame": true, + "sml": true, + "satisfactorymodloader": true, + "examplemod": true, + "docmod": true, +} + func (r *mutationResolver) CreateMod(ctx context.Context, mod generated.NewMod) (*generated.Mod, error) { wrapper, newCtx := WrapMutationTrace(ctx, "createMod") defer wrapper.end() @@ -31,6 +41,10 @@ func (r *mutationResolver) CreateMod(ctx context.Context, mod generated.NewMod) return nil, errors.Wrap(err, "validation failed") } + if DisallowedModReferences[strings.ToLower(mod.ModReference)] { + return nil, errors.New("using this mod reference is not allowed") + } + if postgres.GetModByReference(newCtx, mod.ModReference) != nil { return nil, errors.New("mod with this mod reference already exists") } From 7807b9e775ca6b52436ca541740648ccfdeaec63 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Fri, 20 Oct 2023 23:02:59 +0300 Subject: [PATCH 19/21] feat: support mod references in rest endpoint --- db/postgres/version.go | 2 +- nodes/mod.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/postgres/version.go b/db/postgres/version.go index d70aee8..ea88cdf 100644 --- a/db/postgres/version.go +++ b/db/postgres/version.go @@ -94,7 +94,7 @@ func GetAllModVersionsWithDependencies(ctx context.Context, modID string) []Tiny } var versions []TinyVersion - DBCtx(ctx).Debug(). + DBCtx(ctx). Preload("Dependencies"). Preload("Targets"). Where("approved = ? AND denied = ?", true, false). diff --git a/nodes/mod.go b/nodes/mod.go index 4fdeeb3..ee3b53c 100644 --- a/nodes/mod.go +++ b/nodes/mod.go @@ -340,7 +340,7 @@ func downloadModVersionTarget(c echo.Context) error { func getAllModVersions(c echo.Context) (interface{}, *ErrorResponse) { modID := c.Param("modId") - mod := postgres.GetModByID(c.Request().Context(), modID) + mod := postgres.GetModByIDOrReference(c.Request().Context(), modID) if mod == nil { return nil, &ErrorModNotFound From b35adb72f9500e19f9eb07a5152ffff8cfe0dffb Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sat, 6 Jan 2024 09:22:23 +0200 Subject: [PATCH 20/21] fix: update tags only if they are specified --- gql/resolver_mods.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gql/resolver_mods.go b/gql/resolver_mods.go index 46ffaf3..58586c7 100644 --- a/gql/resolver_mods.go +++ b/gql/resolver_mods.go @@ -113,9 +113,11 @@ func (r *mutationResolver) UpdateMod(ctx context.Context, modID string, mod gene return nil, errors.Wrap(err, "validation failed") } - err := postgres.ResetModTags(newCtx, modID, mod.TagIDs) - if err != nil { - return nil, err + if mod.TagIDs != nil { + err := postgres.ResetModTags(newCtx, modID, mod.TagIDs) + if err != nil { + return nil, err + } } dbMod := postgres.GetModByIDNoCache(newCtx, modID) From 65bcbc9ceca744ad294ab9f44cacc3698517105d Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sat, 6 Jan 2024 09:52:32 +0200 Subject: [PATCH 21/21] feat: tag descriptions --- db/postgres/postgres_types.go | 3 ++- gql/gql_types.go | 5 +++-- gql/resolver_tags.go | 17 ++++++++++------- .../sql/000025_add_tag_description.down.sql | 2 ++ .../sql/000025_add_tag_description.up.sql | 2 ++ schemas/tags.graphql | 12 +++++++++--- 6 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 migrations/sql/000025_add_tag_description.down.sql create mode 100644 migrations/sql/000025_add_tag_description.up.sql diff --git a/db/postgres/postgres_types.go b/db/postgres/postgres_types.go index c09660e..4d12da1 100644 --- a/db/postgres/postgres_types.go +++ b/db/postgres/postgres_types.go @@ -169,7 +169,8 @@ type Announcement struct { type Tag struct { SMRModel - Name string `gorm:"type:varchar(24)"` + Name string `gorm:"type:varchar(24)"` + Description string `gorm:"type:varchar(512)"` Mods []Mod `gorm:"many2many:mod_tags"` } diff --git a/gql/gql_types.go b/gql/gql_types.go index 6d8d396..43dc719 100644 --- a/gql/gql_types.go +++ b/gql/gql_types.go @@ -199,8 +199,9 @@ func DBTagToGenerated(tag *postgres.Tag) *generated.Tag { return nil } return &generated.Tag{ - Name: tag.Name, - ID: tag.ID, + Name: tag.Name, + ID: tag.ID, + Description: tag.Description, } } diff --git a/gql/resolver_tags.go b/gql/resolver_tags.go index 8050dcf..08d618f 100644 --- a/gql/resolver_tags.go +++ b/gql/resolver_tags.go @@ -8,12 +8,13 @@ import ( "github.com/satisfactorymodding/smr-api/generated" ) -func (r *mutationResolver) CreateTag(ctx context.Context, tagName string) (*generated.Tag, error) { +func (r *mutationResolver) CreateTag(ctx context.Context, tagName string, description string) (*generated.Tag, error) { wrapper, newCtx := WrapMutationTrace(ctx, "createTag") defer wrapper.end() dbTag := &postgres.Tag{ - Name: tagName, + Name: tagName, + Description: description, } resultTag, err := postgres.CreateTag(newCtx, dbTag, true) @@ -23,15 +24,16 @@ func (r *mutationResolver) CreateTag(ctx context.Context, tagName string) (*gene return DBTagToGenerated(resultTag), nil } -func (r *mutationResolver) CreateMultipleTags(ctx context.Context, tagNames []string) ([]*generated.Tag, error) { +func (r *mutationResolver) CreateMultipleTags(ctx context.Context, tags []*generated.NewTag) ([]*generated.Tag, error) { wrapper, newCtx := WrapMutationTrace(ctx, "createMultipleTags") defer wrapper.end() - resultTags := make([]postgres.Tag, len(tagNames)) + resultTags := make([]postgres.Tag, len(tags)) - for i, tagName := range tagNames { + for i, tag := range tags { dbTag := &postgres.Tag{ - Name: tagName, + Name: tag.Name, + Description: tag.Description, } resultTag, err := postgres.CreateTag(newCtx, dbTag, false) @@ -60,7 +62,7 @@ func (r *mutationResolver) DeleteTag(ctx context.Context, id string) (bool, erro return true, nil } -func (r *mutationResolver) UpdateTag(ctx context.Context, id string, newName string) (*generated.Tag, error) { +func (r *mutationResolver) UpdateTag(ctx context.Context, id string, newName string, description string) (*generated.Tag, error) { wrapper, newCtx := WrapMutationTrace(ctx, "updateTag") defer wrapper.end() @@ -76,6 +78,7 @@ func (r *mutationResolver) UpdateTag(ctx context.Context, id string, newName str } SetStringINNOE(&newName, &dbTag.Name) + SetStringINNOE(&description, &dbTag.Description) postgres.Save(newCtx, &dbTag) diff --git a/migrations/sql/000025_add_tag_description.down.sql b/migrations/sql/000025_add_tag_description.down.sql new file mode 100644 index 0000000..91bd0f5 --- /dev/null +++ b/migrations/sql/000025_add_tag_description.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE tags + DROP COLUMN description; diff --git a/migrations/sql/000025_add_tag_description.up.sql b/migrations/sql/000025_add_tag_description.up.sql new file mode 100644 index 0000000..5cf24be --- /dev/null +++ b/migrations/sql/000025_add_tag_description.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE tags + ADD COLUMN IF NOT EXISTS description varchar(512); \ No newline at end of file diff --git a/schemas/tags.graphql b/schemas/tags.graphql index 6400b81..a2a31f3 100644 --- a/schemas/tags.graphql +++ b/schemas/tags.graphql @@ -4,6 +4,12 @@ scalar TagName type Tag { id: TagID! name: TagName! + description: String! +} + +input NewTag { + name: TagName! + description: String! } input TagFilter { @@ -22,8 +28,8 @@ extend type Query { ### Mutations extend type Mutation { - createTag(tagName: TagName!): Tag @isLoggedIn - createMultipleTags(tagNames: [TagName!]!): [Tag!]! @canManageTags @isLoggedIn - updateTag(tagID: TagID!, NewName: TagName!): Tag! @canManageTags @isLoggedIn + createTag(tagName: TagName!, description: String!): Tag @canManageTags @isLoggedIn + createMultipleTags(tagNames: [NewTag!]!): [Tag!]! @canManageTags @isLoggedIn + updateTag(tagID: TagID!, NewName: TagName!, description: String!): Tag! @canManageTags @isLoggedIn deleteTag(tagID: TagID!): Boolean! @canManageTags @isLoggedIn } \ No newline at end of file