diff --git a/api/cdb/cached_client.go b/api/cdb/cached_client.go index b49a80de..fab88221 100644 --- a/api/cdb/cached_client.go +++ b/api/cdb/cached_client.go @@ -13,6 +13,8 @@ type CachedCDBClient struct { search_cache *cache.Cache[string, []*Package] dependency_cache *cache.Cache[string, PackageDependency] detail_cache *cache.Cache[string, *PackageDetails] + updates PackageUpdates + updates_time time.Time } func NewCachedClient(client *CDBClient, ttl time.Duration) *CachedCDBClient { @@ -69,3 +71,15 @@ func (c *CachedCDBClient) GetDetails(author, name string) (*PackageDetails, erro } return res, nil } + +func (c *CachedCDBClient) GetUpdates() (PackageUpdates, error) { + last_update := time.Since(c.updates_time) + if last_update > c.ttl || c.updates == nil { + var err error + c.updates, err = c.client.GetUpdates() + if err != nil { + return nil, err + } + } + return c.updates, nil +} diff --git a/api/cdb/client.go b/api/cdb/client.go index 164e3dc7..74bedb2d 100644 --- a/api/cdb/client.go +++ b/api/cdb/client.go @@ -109,6 +109,12 @@ func (c *CDBClient) GetRelease(author, name string, id int) (*PackageRelease, er return pr, err } +func (c *CDBClient) GetUpdates() (PackageUpdates, error) { + pu := PackageUpdates{} + err := c.get("api/updates", &pu, nil) + return pu, err +} + func (c *CDBClient) GetScreenshots(author, name string) ([]*PackageScreenshot, error) { ps := []*PackageScreenshot{} err := c.get(fmt.Sprintf("api/packages/%s/%s/screenshots", author, name), &ps, nil) diff --git a/api/cdb/types.go b/api/cdb/types.go index 033cb711..9cbabe3e 100644 --- a/api/cdb/types.go +++ b/api/cdb/types.go @@ -133,6 +133,8 @@ type DependencyInfo struct { type PackageDependency map[string][]*DependencyInfo +type PackageUpdates map[string]int + type MinetestVersion struct { IsDev bool `json:"is_dev"` Name string `json:"name"` diff --git a/db/migrations/08_mod_latest_version.up.sql b/db/migrations/08_mod_latest_version.up.sql new file mode 100644 index 00000000..0e2e5500 --- /dev/null +++ b/db/migrations/08_mod_latest_version.up.sql @@ -0,0 +1 @@ +alter table mod add column latest_version varchar(64) not null default ''; diff --git a/modmanager/manager.go b/modmanager/manager.go index eb9c2da6..4f2db731 100644 --- a/modmanager/manager.go +++ b/modmanager/manager.go @@ -36,11 +36,6 @@ func (m *ModManager) Create(mod *types.Mod) error { return handler.Create(m.handlercontext, mod) } -func (m *ModManager) Status(mod *types.Mod) (*ModStatus, error) { - handler := m.handlers[mod.SourceType] - return handler.Status(m.handlercontext, mod) -} - func (m *ModManager) Update(mod *types.Mod, version string) error { handler := m.handlers[mod.SourceType] return handler.Update(m.handlercontext, mod, version) diff --git a/modmanager/manager_cdb_test.go b/modmanager/manager_cdb_test.go index 0dd934f1..622f5768 100644 --- a/modmanager/manager_cdb_test.go +++ b/modmanager/manager_cdb_test.go @@ -17,7 +17,7 @@ func TestLatestCDBRelease(t *testing.T) { Name: "blockexchange", ModType: types.ModTypeMod, SourceType: types.SourceTypeCDB, - Author: "buckaroobanzay", + Author: "BuckarooBanzay", } assert.NoError(t, mm.Create(mod)) assert.True(t, mod.Version != "") @@ -27,13 +27,14 @@ func TestLatestCDBRelease(t *testing.T) { assert.NotNil(t, mods) assert.Equal(t, 1, len(mods)) - status, err := mm.Status(mod) + err = mm.CheckUpdates() assert.NoError(t, err) - assert.NotNil(t, status) - assert.Equal(t, mod.Version, status.CurrentVersion) - assert.Equal(t, mod.Version, status.LatestVersion) - err = mm.Update(mod, mod.Version) + mod, err = app.Repos.ModRepo.GetByID(mod.ID) + assert.NoError(t, err) + assert.Equal(t, mod.Version, mod.LatestVersion) + + err = mm.Update(mod, mod.LatestVersion) assert.NoError(t, err) err = mm.Remove(mod) diff --git a/modmanager/manager_git_test.go b/modmanager/manager_git_test.go index 53eb436a..01ad3b37 100644 --- a/modmanager/manager_git_test.go +++ b/modmanager/manager_git_test.go @@ -65,20 +65,15 @@ func TestCheckoutHash(t *testing.T) { assert.Equal(t, 1, len(mods)) // check remote status - status, err := mm.Status(mod) + err = mm.CheckUpdates() assert.NoError(t, err) - assert.NotNil(t, status) - assert.Equal(t, "fe34e3f3cd3e066ba0be76f9df46c11e66411496", status.CurrentVersion) - assert.True(t, status.LatestVersion != "") - assert.True(t, status.LatestVersion != status.CurrentVersion) - // update - assert.NoError(t, mm.Update(mod, status.LatestVersion)) - status2, err := mm.Status(mod) + mod, err = app.Repos.ModRepo.GetByID(mod.ID) assert.NoError(t, err) - assert.NotNil(t, status2) - assert.Equal(t, status.LatestVersion, status2.CurrentVersion) - assert.Equal(t, status.LatestVersion, status2.LatestVersion) + assert.NotEqual(t, "fe34e3f3cd3e066ba0be76f9df46c11e66411496", mod.LatestVersion) + + // update + assert.NoError(t, mm.Update(mod, mod.LatestVersion)) // remove assert.NoError(t, mm.Remove(mod)) diff --git a/modmanager/source_cdb.go b/modmanager/source_cdb.go index 516a85a6..9295112a 100644 --- a/modmanager/source_cdb.go +++ b/modmanager/source_cdb.go @@ -9,9 +9,11 @@ import ( "path" "strconv" "strings" + "time" ) var cli = cdb.New() +var cached_cli = cdb.NewCachedClient(cli, time.Hour) type ContentDBModHandler struct{} @@ -69,7 +71,6 @@ func (h *ContentDBModHandler) installMod(ctx *HandlerContext, mod *types.Mod, re default: return fmt.Errorf("mod type not supported: %s", mod.ModType) } - fmt.Printf("Fullpath: '%s' entry: '%s'\n", fullpath, f.Name) // create basedir if it does not exist basedir := path.Dir(fullpath) @@ -150,20 +151,6 @@ func (h *ContentDBModHandler) Create(ctx *HandlerContext, mod *types.Mod) error return ctx.Repo.Create(mod) } -func (h *ContentDBModHandler) Status(ctx *HandlerContext, mod *types.Mod) (*ModStatus, error) { - release, err := h.getLatestRelease(ctx, mod) - if err != nil { - return nil, fmt.Errorf("could not fetch latest release: %v", err) - } - - s := &ModStatus{ - CurrentVersion: mod.Version, - LatestVersion: fmt.Sprintf("%d", release.ID), - } - - return s, nil -} - func (h *ContentDBModHandler) Update(ctx *HandlerContext, mod *types.Mod, version string) error { release_id, err := strconv.Atoi(version) @@ -196,3 +183,18 @@ func (h *ContentDBModHandler) Remove(ctx *HandlerContext, mod *types.Mod) error return ctx.Repo.Delete(mod.ID) } + +func (h *ContentDBModHandler) CheckUpdate(ctx *HandlerContext, mod *types.Mod) (bool, error) { + updates, err := cached_cli.GetUpdates() + if err != nil { + return false, fmt.Errorf("could not get updates: %v", err) + } + + v := updates[fmt.Sprintf("%s/%s", mod.Author, mod.Name)] + if v > 0 { + mod.LatestVersion = fmt.Sprintf("%d", v) + return true, nil + } + + return false, nil +} diff --git a/modmanager/source_git.go b/modmanager/source_git.go index 1e48c658..247d5822 100644 --- a/modmanager/source_git.go +++ b/modmanager/source_git.go @@ -70,40 +70,6 @@ func (h *GitModHandler) Create(ctx *HandlerContext, mod *types.Mod) error { return ctx.Repo.Create(mod) } -func (h *GitModHandler) Status(ctx *HandlerContext, mod *types.Mod) (*ModStatus, error) { - status := &ModStatus{} - - dir := getDir(ctx.WorldDir, mod) - - r, err := git.PlainOpen(dir) - if err != nil { - return status, err - } - - heah, err := r.Head() - if err != nil { - return status, err - } - status.CurrentVersion = heah.Hash().String() - - rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{ - Name: "origin", - URLs: []string{mod.URL}, - }) - - refs, err := rem.List(&git.ListOptions{}) - if err != nil { - return status, err - } - for _, ref := range refs { - if ref.Name() == plumbing.ReferenceName(mod.Branch) { - status.LatestVersion = ref.Hash().String() - } - } - - return status, nil -} - func (h *GitModHandler) Update(ctx *HandlerContext, mod *types.Mod, version string) error { dir := getDir(ctx.WorldDir, mod) @@ -122,9 +88,16 @@ func (h *GitModHandler) Update(ctx *HandlerContext, mod *types.Mod, version stri return err } - return w.Checkout(&git.CheckoutOptions{ + err = w.Checkout(&git.CheckoutOptions{ Hash: plumbing.NewHash(version), }) + if err != nil { + return err + } + + mod.Version = version + return ctx.Repo.Update(mod) + } func (h *GitModHandler) Remove(ctx *HandlerContext, mod *types.Mod) error { @@ -137,3 +110,22 @@ func (h *GitModHandler) Remove(ctx *HandlerContext, mod *types.Mod) error { return ctx.Repo.Delete(mod.ID) } + +func (h *GitModHandler) CheckUpdate(ctx *HandlerContext, mod *types.Mod) (bool, error) { + rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{ + Name: "origin", + URLs: []string{mod.URL}, + }) + + refs, err := rem.List(&git.ListOptions{}) + if err != nil { + return false, fmt.Errorf("git error: %v", err) + } + for _, ref := range refs { + if ref.Name() == plumbing.ReferenceName(mod.Branch) { + mod.LatestVersion = ref.Hash().String() + } + } + + return mod.LatestVersion != mod.Version, nil +} diff --git a/modmanager/types.go b/modmanager/types.go index 6675e5e4..d5afe2f2 100644 --- a/modmanager/types.go +++ b/modmanager/types.go @@ -17,7 +17,7 @@ type HandlerContext struct { type SourceTypeHandler interface { Create(ctx *HandlerContext, mod *types.Mod) error - Status(ctx *HandlerContext, mod *types.Mod) (*ModStatus, error) Update(ctx *HandlerContext, mod *types.Mod, version string) error Remove(ctx *HandlerContext, mod *types.Mod) error + CheckUpdate(ctx *HandlerContext, mod *types.Mod) (bool, error) } diff --git a/modmanager/updates.go b/modmanager/updates.go new file mode 100644 index 00000000..18742fcc --- /dev/null +++ b/modmanager/updates.go @@ -0,0 +1,28 @@ +package modmanager + +import ( + "fmt" +) + +func (m *ModManager) CheckUpdates() error { + mods, err := m.repo.GetAll() + if err != nil { + return fmt.Errorf("get all mods failed: %v", err) + } + + for _, mod := range mods { + h := m.handlers[mod.SourceType] + updated, err := h.CheckUpdate(m.handlercontext, mod) + if err != nil { + return fmt.Errorf("update check failed for mod '%s': %v", mod.Name, err) + } + if updated { + err = m.repo.Update(mod) + if err != nil { + return fmt.Errorf("failed to update mod-data for '%s': %v", mod.Name, err) + } + } + } + + return nil +} diff --git a/public/js/api/mods.js b/public/js/api/mods.js index f966662b..460a831d 100644 --- a/public/js/api/mods.js +++ b/public/js/api/mods.js @@ -9,4 +9,12 @@ export const create_mod = mod => protected_fetch("api/mods", { body: JSON.stringify(mod) }); +export const update_mod = (mod, version) => protected_fetch(`api/mods/${mod.id}/update/${version}`, { + method: "POST" +}); + +export const check_updates = () => protected_fetch("api/mods/checkupdates", { + method: "POST" +}); + export const remove_mod = id => fetch(`api/mods/${id}`, {method: "DELETE"}); diff --git a/public/js/components/pages/mods/Mods.js b/public/js/components/pages/mods/Mods.js index b4f14e7f..a104d2a2 100644 --- a/public/js/components/pages/mods/Mods.js +++ b/public/js/components/pages/mods/Mods.js @@ -1,4 +1,4 @@ -import { add, remove, get_all, is_busy, get_git_mod } from '../../../service/mods.js'; +import { add, remove, get_all, is_busy, get_git_mod, update_mod, check_updates } from '../../../service/mods.js'; import FeedbackButton from '../../FeedbackButton.js'; import DefaultLayout from '../../layouts/DefaultLayout.js'; import CDBPackageLink from '../../CDBPackageLink.js'; @@ -45,9 +45,11 @@ export default { branch: "refs/heads/master" }); }, + update_mod: update_mod, remove: remove, get_mods: get_all, - get_git_mod: get_git_mod + get_git_mod: get_git_mod, + check_updates: check_updates }, computed: { busy: is_busy @@ -71,6 +73,15 @@ export default { +
Source-Type | Source | Version | +Latest Version | Auto-update | Actions |
---|---|---|---|---|---|
{{mod.mod_type}} | @@ -156,6 +168,9 @@ export default {{{mod.version}} | ++ {{mod.latest_version}} + | @@ -168,14 +183,14 @@ export default { |
-
-
- Edit
-
-
+
+
+
|