Add a warning when the version does not match the latest release (#442)
Some checks failed
Docker publish rootless / build-rootless (push) Waiting to run
Docker publish / build (push) Waiting to run
Update Currencies / update-currencies (push) Waiting to run
Docker publish ARM / build (push) Has been cancelled
Docker publish rootless ARM / build-rootless (push) Has been cancelled
Dockerhub publish / build (push) Has been cancelled

This commit is contained in:
Tonya
2025-01-04 19:22:25 +00:00
committed by GitHub
parent a4e94ddf7a
commit 9cf244c933
16 changed files with 200 additions and 12 deletions

View File

@@ -23,8 +23,9 @@ func NewTask(name string, interval time.Duration, fn func(context.Context)) *Bac
}
func (tsk *BackgroundTask) Start(ctx context.Context) error {
timer := time.NewTimer(tsk.Interval)
tsk.Fn(ctx)
timer := time.NewTimer(tsk.Interval)
for {
select {
case <-ctx.Done():

View File

@@ -89,6 +89,7 @@ type (
Title string `json:"title"`
Message string `json:"message"`
Build Build `json:"build"`
Latest services.Latest `json:"latest"`
Demo bool `json:"demo"`
AllowRegistration bool `json:"allowRegistration"`
}
@@ -123,6 +124,7 @@ func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.Hand
Title: "Homebox",
Message: "Track, Manage, and Organize your Things",
Build: build,
Latest: ctrl.svc.BackgroundService.GetLatestVersion(),
Demo: ctrl.isDemo,
AllowRegistration: ctrl.allowRegistration,
})

View File

@@ -254,6 +254,18 @@ func run(cfg *config.Config) error {
}
}))
if cfg.Options.GithubReleaseCheck {
runner.AddPlugin(NewTask("get-latest-github-release", time.Hour, func(ctx context.Context) {
log.Debug().Msg("running get latest github release")
err := app.services.BackgroundService.GetLatestGithubRelease(context.Background())
if err != nil {
log.Error().
Err(err).
Msg("failed to get latest github release")
}
}))
}
if cfg.Debug.Enabled {
runner.AddFunc("debug", func(ctx context.Context) error {
debugserver := http.Server{

View File

@@ -2950,6 +2950,17 @@ const docTemplate = `{
}
}
},
"services.Latest": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"services.UserRegistration": {
"type": "object",
"properties": {
@@ -2982,6 +2993,9 @@ const docTemplate = `{
"health": {
"type": "boolean"
},
"latest": {
"$ref": "#/definitions/services.Latest"
},
"message": {
"type": "string"
},

View File

@@ -2943,6 +2943,17 @@
}
}
},
"services.Latest": {
"type": "object",
"properties": {
"date": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"services.UserRegistration": {
"type": "object",
"properties": {
@@ -2975,6 +2986,9 @@
"health": {
"type": "boolean"
},
"latest": {
"$ref": "#/definitions/services.Latest"
},
"message": {
"type": "string"
},

View File

@@ -654,6 +654,13 @@ definitions:
value:
type: number
type: object
services.Latest:
properties:
date:
type: string
version:
type: string
type: object
services.UserRegistration:
properties:
email:
@@ -675,6 +682,8 @@ definitions:
type: boolean
health:
type: boolean
latest:
$ref: '#/definitions/services.Latest'
message:
type: string
title:

View File

@@ -61,7 +61,7 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
repo: repos,
autoIncrementAssetID: options.autoIncrementAssetID,
},
BackgroundService: &BackgroundService{repos},
BackgroundService: &BackgroundService{repos, Latest{}},
Currencies: currencies.NewCurrencyService(options.currencies),
}
}

View File

@@ -2,6 +2,9 @@ package services
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
@@ -11,8 +14,13 @@ import (
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
)
type Latest struct {
Version string `json:"version"`
Date string `json:"date"`
}
type BackgroundService struct {
repos *repo.AllRepos
latest Latest
}
func (svc *BackgroundService) SendNotifiersToday(ctx context.Context) error {
@@ -79,3 +87,52 @@ func (svc *BackgroundService) SendNotifiersToday(ctx context.Context) error {
return nil
}
func (svc *BackgroundService) GetLatestGithubRelease(ctx context.Context) error {
url := "https://api.github.com/repos/sysadminsmedia/homebox/releases/latest"
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return fmt.Errorf("failed to create latest version request: %w", err)
}
req.Header.Set("User-Agent", "Homebox-Version-Checker")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make latest version request: %w", err)
}
defer func() {
err := resp.Body.Close()
if err != nil {
log.Printf("error closing latest version response body: %v", err)
}
}()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("latest version unexpected status code: %d", resp.StatusCode)
}
// ignoring fields that are not relevant
type Release struct {
ReleaseVersion string `json:"tag_name"`
PublishedAt time.Time `json:"published_at"`
}
var release Release
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
return fmt.Errorf("failed to decode latest version response: %w", err)
}
svc.latest = Latest{
Version: release.ReleaseVersion,
Date: release.PublishedAt.String(),
}
return nil
}
func (svc *BackgroundService) GetLatestVersion() (Latest) {
return svc.latest
}

View File

@@ -32,6 +32,7 @@ type Options struct {
AllowRegistration bool `yaml:"disable_registration" conf:"default:true"`
AutoIncrementAssetID bool `yaml:"auto_increment_asset_id" conf:"default:true"`
CurrencyConfig string `yaml:"currencies"`
GithubReleaseCheck bool `yaml:"check_github_release" conf:"default:true"`
}
type DebugConf struct {

View File

@@ -25,7 +25,7 @@
| HBOX_MAILER_FROM | | email from address to use |
| HBOX_SWAGGER_HOST | 7745 | swagger host to use, if not set swagger will be disabled |
| HBOX_SWAGGER_SCHEMA | `http` | swagger schema to use, can be one of: `http`, `https` |
| HBOX_OPTIONS_CHECK_GITHUB_RELEASE | true | check for new github releases |
::: tip "CLI Arguments"
If you're deploying without docker you can use command line arguments to configure the application. Run `homebox --help` for more information.
@@ -55,6 +55,7 @@ OPTIONS
--options-allow-registration/$HBOX_OPTIONS_ALLOW_REGISTRATION <bool> (default: true)
--options-auto-increment-asset-id/$HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID <bool> (default: true)
--options-currency-config/$HBOX_OPTIONS_CURRENCY_CONFIG <string>
--options-check-github-release/$HBOX_OPTIONS_CHECK_GITHUB_RELEASE <bool> (default: true)
--help/-h display this help message
```
:::

View File

@@ -0,0 +1,41 @@
<template>
<BaseModal v-model="modal">
<template #title>🎉 {{ $t("components.app.outdated.new_version_available") }} 🎉</template>
<div class="p-4">
<p>{{ $t("components.app.outdated.current_version") }}: {{ current }}</p>
<p>{{ $t("components.app.outdated.latest_version") }}: {{ latest }}</p>
<p>
<a href="https://github.com/sysadminsmedia/homebox/releases" target="_blank" rel="noopener" class="link">
{{ $t("components.app.outdated.new_version_available_link") }}
</a>
</p>
</div>
<button class="btn btn-warning" @click="hide">
{{ $t("components.app.outdated.dismiss") }}
</button>
</BaseModal>
</template>
<script setup lang="ts">
const props = defineProps({
modelValue: {
type: Boolean,
required: true,
},
current: {
type: String,
required: true,
},
latest: {
type: String,
required: true,
},
});
const modal = useVModel(props, "modelValue");
const hide = () => {
modal.value = false;
localStorage.setItem("latestVersion", props.latest);
};
</script>

View File

@@ -6,6 +6,7 @@
up the tree
-->
<ModalConfirm />
<AppOutdatedModal v-model="modals.outdated" :current="current ?? ''" :latest="latest ?? ''" />
<ItemCreateModal v-model="modals.item" />
<LabelCreateModal v-model="modals.label" />
<LocationCreateModal v-model="modals.location" />
@@ -100,10 +101,9 @@
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { lt } from "semver";
import { useLabelStore } from "~~/stores/labels";
import { useLocationStore } from "~~/stores/locations";
import MdiMenu from "~icons/mdi/menu";
import MdiPlus from "~icons/mdi/plus";
import MdiHome from "~icons/mdi/home";
import MdiFileTree from "~icons/mdi/file-tree";
@@ -111,6 +111,8 @@
import MdiAccount from "~icons/mdi/account";
import MdiCog from "~icons/mdi/cog";
import MdiWrench from "~icons/mdi/wrench";
import MdiMenu from "~icons/mdi/menu";
import MdiPlus from "~icons/mdi/plus";
const { t } = useI18n();
const username = computed(() => authCtx.user?.name || "User");
@@ -124,6 +126,15 @@
return data;
});
const latest = computed(() => status.value?.latest.version);
const current = computed(() => status.value?.build.version);
const isDev = computed(() => import.meta.dev || !current.value?.includes("."));
const isOutdated = computed(() => current.value && latest.value && lt(current.value, latest.value));
const hasHiddenLatest = computed(() => localStorage.getItem("latestVersion") === latest.value);
const displayOutdatedWarning = computed(() => !isDev && !hasHiddenLatest.value && isOutdated.value);
// Preload currency format
useFormatCurrency();
const modals = reactive({
@@ -131,6 +142,14 @@
location: false,
label: false,
import: false,
outdated: displayOutdatedWarning.value,
});
watch(displayOutdatedWarning, () => {
console.log("displayOutdatedWarning", displayOutdatedWarning.value);
if (displayOutdatedWarning.value) {
modals.outdated = true;
}
});
const dropdown = [

View File

@@ -392,6 +392,11 @@ export interface ValueOverTimeEntry {
value: number;
}
export interface Latest {
date: Date | string;
version: string;
}
export interface UserRegistration {
email: string;
name: string;
@@ -404,6 +409,7 @@ export interface APISummary {
build: Build;
demo: boolean;
health: boolean;
latest: Latest;
message: string;
title: string;
versions: string[];

View File

@@ -6,6 +6,13 @@
"description": "Import a CSV file containing your items, labels, and locations. See documentation for more information on the \nrequired format.",
"title": "Import CSV File",
"upload": "Upload"
},
"outdated": {
"current_version": "Current Version",
"latest_version": "Latest Version",
"new_version_available": "New Version Available",
"new_version_available_link": "Click here to view the release notes",
"dismiss": "Dismiss"
}
},
"global": {

View File

@@ -61,6 +61,7 @@
"markdown-it": "^14.1.0",
"pinia": "^2.2.8",
"postcss": "^8.4.49",
"semver": "^7.6.3",
"tailwindcss": "^3.4.15",
"vue": "3.4.8",
"vue-router": "^4.5.0"

View File

@@ -68,6 +68,9 @@ importers:
postcss:
specifier: ^8.4.49
version: 8.4.49
semver:
specifier: ^7.6.3
version: 7.6.3
tailwindcss:
specifier: ^3.4.15
version: 3.4.15