mirror of https://github.com/jetkvm/kvm.git
Compare commits
5 Commits
9832be29ef
...
0cc84f0c54
| Author | SHA1 | Date |
|---|---|---|
|
|
0cc84f0c54 | |
|
|
3fab951d43 | |
|
|
740d9b61a0 | |
|
|
7f2dcc84b4 | |
|
|
6e1b84f39b |
|
|
@ -311,13 +311,27 @@ func (s *State) checkUpdateStatus(
|
|||
appUpdateStatus.available = false
|
||||
}
|
||||
|
||||
// Handle custom target versions
|
||||
if slices.Contains(params.Components, "app") && params.AppTargetVersion != "" {
|
||||
appUpdateStatus.available = appVersionRemote.String() != appUpdateStatus.localVersion
|
||||
components := params.Components
|
||||
// skip check if no components are specified
|
||||
if len(components) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if slices.Contains(params.Components, "system") && params.SystemTargetVersion != "" {
|
||||
systemUpdateStatus.available = systemVersionRemote.String() != systemUpdateStatus.localVersion
|
||||
// TODO: simplify this
|
||||
if slices.Contains(components, "app") {
|
||||
if params.AppTargetVersion != "" {
|
||||
appUpdateStatus.available = appVersionRemote.String() != appVersionLocal.String()
|
||||
}
|
||||
} else {
|
||||
appUpdateStatus.available = false
|
||||
}
|
||||
|
||||
if slices.Contains(components, "system") {
|
||||
if params.SystemTargetVersion != "" {
|
||||
systemUpdateStatus.available = systemVersionRemote.String() != systemVersionLocal.String()
|
||||
}
|
||||
} else {
|
||||
systemUpdateStatus.available = false
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package ota
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func pseudoGetLocalVersion() (systemVersion *semver.Version, appVersion *semver.Version, err error) {
|
||||
systemVersion = semver.MustParse("0.2.5")
|
||||
appVersion = semver.MustParse("0.4.7")
|
||||
return systemVersion, appVersion, nil
|
||||
}
|
||||
|
||||
func newOtaState() *State {
|
||||
logger := zerolog.New(os.Stdout).Level(zerolog.TraceLevel)
|
||||
otaState := NewState(Options{
|
||||
SkipConfirmSystem: true,
|
||||
Logger: &logger,
|
||||
ReleaseAPIEndpoint: "https://api.jetkvm.com/releases",
|
||||
GetHTTPClient: func() *http.Client {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
return client
|
||||
},
|
||||
GetLocalVersion: pseudoGetLocalVersion,
|
||||
HwReboot: func(force bool, postRebootAction *PostRebootAction, delay time.Duration) error { return nil },
|
||||
ResetConfig: func() error { return nil },
|
||||
OnStateUpdate: func(state *RPCState) {},
|
||||
OnProgressUpdate: func(progress float32) {},
|
||||
})
|
||||
return otaState
|
||||
}
|
||||
|
||||
func TestCheckUpdateComponents(t *testing.T) {
|
||||
otaState := newOtaState()
|
||||
updateParams := UpdateParams{
|
||||
DeviceID: "test",
|
||||
IncludePreRelease: false,
|
||||
SystemTargetVersion: "0.2.2",
|
||||
Components: []string{"system"},
|
||||
}
|
||||
info, err := otaState.GetUpdateStatus(context.Background(), updateParams)
|
||||
t.Logf("update status: %+v", info)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to check update: %v", err)
|
||||
}
|
||||
assert.True(t, info.SystemUpdateAvailable)
|
||||
assert.False(t, info.AppUpdateAvailable)
|
||||
}
|
||||
|
|
@ -203,6 +203,7 @@ type Options struct {
|
|||
HwReboot HwRebootFunc
|
||||
ReleaseAPIEndpoint string
|
||||
ResetConfig ResetConfigFunc
|
||||
SkipConfirmSystem bool
|
||||
}
|
||||
|
||||
// NewState creates a new OTA state
|
||||
|
|
@ -221,7 +222,9 @@ func NewState(opts Options) *State {
|
|||
releaseAPIEndpoint: opts.ReleaseAPIEndpoint,
|
||||
resetConfig: opts.ResetConfig,
|
||||
}
|
||||
go s.confirmCurrentSystem()
|
||||
if !opts.SkipConfirmSystem {
|
||||
go s.confirmCurrentSystem()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
|||
4
ota.go
4
ota.go
|
|
@ -91,7 +91,7 @@ func getUpdateStatus(includePreRelease bool) (*ota.UpdateStatus, error) {
|
|||
updateStatus.Error = err.Error()
|
||||
}
|
||||
|
||||
logger.Info().Interface("updateStatus", updateStatus).Msg("Update status")
|
||||
otaLogger.Info().Interface("updateStatus", updateStatus).Msg("Update status")
|
||||
|
||||
return updateStatus, nil
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@ func rpcTryUpdateComponents(params updateParams, includePreRelease bool, resetCo
|
|||
go func() {
|
||||
err := otaState.TryUpdate(context.Background(), updateParams)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to try update")
|
||||
otaLogger.Warn().Err(err).Msg("failed to try update")
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -603,6 +603,9 @@ export interface UpdateState {
|
|||
|
||||
updateErrorMessage: string | null;
|
||||
setUpdateErrorMessage: (errorMessage: string) => void;
|
||||
|
||||
shouldReload: boolean;
|
||||
setShouldReload: (reloadRequired: boolean) => void;
|
||||
}
|
||||
|
||||
export const useUpdateStore = create<UpdateState>(set => ({
|
||||
|
|
@ -640,6 +643,9 @@ export const useUpdateStore = create<UpdateState>(set => ({
|
|||
updateErrorMessage: null,
|
||||
setUpdateErrorMessage: (errorMessage: string) =>
|
||||
set({ updateErrorMessage: errorMessage }),
|
||||
|
||||
shouldReload: false,
|
||||
setShouldReload: (reloadRequired: boolean) => set({ shouldReload: reloadRequired }),
|
||||
}));
|
||||
|
||||
export type UsbConfigModalViews = "updateUsbConfig" | "updateUsbConfigSuccess";
|
||||
|
|
@ -850,12 +856,12 @@ export interface MacrosState {
|
|||
loadMacros: () => Promise<void>;
|
||||
saveMacros: (macros: KeySequence[]) => Promise<void>;
|
||||
sendFn:
|
||||
| ((
|
||||
method: string,
|
||||
params: unknown,
|
||||
callback?: ((resp: JsonRpcResponse) => void) | undefined,
|
||||
) => void)
|
||||
| null;
|
||||
| ((
|
||||
method: string,
|
||||
params: unknown,
|
||||
callback?: ((resp: JsonRpcResponse) => void) | undefined,
|
||||
) => void)
|
||||
| null;
|
||||
setSendFn: (
|
||||
sendFn: (
|
||||
method: string,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export default function SettingsGeneralUpdateRoute() {
|
|||
const [searchParams] = useSearchParams();
|
||||
const { updateSuccess } = location.state || {};
|
||||
|
||||
const { setModalView, otaState } = useUpdateStore();
|
||||
const { setModalView, otaState, shouldReload, setShouldReload } = useUpdateStore();
|
||||
const { send } = useJsonRpc();
|
||||
|
||||
const customAppVersion = useMemo(() => searchParams.get("custom_app_version") || undefined, [searchParams]);
|
||||
|
|
@ -28,15 +28,19 @@ export default function SettingsGeneralUpdateRoute() {
|
|||
|
||||
const onClose = useCallback(async () => {
|
||||
navigate(".."); // back to the devices.$id.settings page
|
||||
// Add 1s delay between navigation and calling reload() to prevent reload from interrupting the navigation.
|
||||
await sleep(1000);
|
||||
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
|
||||
}, [navigate]);
|
||||
|
||||
if (shouldReload) {
|
||||
setShouldReload(false);
|
||||
await sleep(1000); // Add 1s delay between navigation and calling reload() to prevent reload from interrupting the navigation.
|
||||
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
|
||||
}
|
||||
}, [navigate, setShouldReload, shouldReload]);
|
||||
|
||||
const onConfirmUpdate = useCallback(() => {
|
||||
setShouldReload(true);
|
||||
send("tryUpdate", {});
|
||||
setModalView("updating");
|
||||
}, [send, setModalView]);
|
||||
}, [send, setModalView, setShouldReload]);
|
||||
|
||||
const onConfirmCustomUpdate = useCallback((appTargetVersion?: string, systemTargetVersion?: string) => {
|
||||
const components = [];
|
||||
|
|
@ -97,10 +101,9 @@ export function Dialog({
|
|||
const { modalView, setModalView, otaState } = useUpdateStore();
|
||||
const forceCustomUpdate = customSystemVersion !== undefined || customAppVersion !== undefined;
|
||||
const onConfirmCustomUpdate = useCallback(() => {
|
||||
console.debug("onConfirmCustomUpdate", customAppVersion, customSystemVersion, versionInfo);
|
||||
onConfirmCustomUpdateCallback(
|
||||
customAppVersion !== undefined ? customAppVersion : versionInfo?.remote?.appVersion,
|
||||
customSystemVersion !== undefined ? customSystemVersion : versionInfo?.remote?.systemVersion,
|
||||
customAppVersion !== undefined ? versionInfo?.remote?.appVersion : undefined,
|
||||
customSystemVersion !== undefined ? versionInfo?.remote?.systemVersion : undefined,
|
||||
);
|
||||
}, [onConfirmCustomUpdateCallback, customAppVersion, customSystemVersion, versionInfo]);
|
||||
|
||||
|
|
|
|||
4
web.go
4
web.go
|
|
@ -814,7 +814,7 @@ func handleSendWOLMagicPacket(c *gin.Context) {
|
|||
inputMacAddr := c.Param("mac-addr")
|
||||
macAddr, err := net.ParseMAC(inputMacAddr)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Str("sendWol", inputMacAddr).Msg("Invalid mac address provided")
|
||||
logger.Warn().Err(err).Str("inputMacAddr", inputMacAddr).Msg("Invalid MAC address provided")
|
||||
c.String(http.StatusBadRequest, "Invalid mac address provided")
|
||||
return
|
||||
}
|
||||
|
|
@ -822,7 +822,7 @@ func handleSendWOLMagicPacket(c *gin.Context) {
|
|||
macAddrString := macAddr.String()
|
||||
err = rpcSendWOLMagicPacket(macAddrString)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Str("sendWOL", macAddrString).Msg("Failed to send WOL magic packet")
|
||||
logger.Warn().Err(err).Str("macAddrString", macAddrString).Msg("Failed to send WOL magic packet")
|
||||
c.String(http.StatusInternalServerError, "Failed to send WOL to %s: %v", macAddrString, err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue