diff --git a/server/config/picoclaw_internal.go b/server/config/picoclaw_internal.go index 446dc1e..b9fb663 100644 --- a/server/config/picoclaw_internal.go +++ b/server/config/picoclaw_internal.go @@ -25,9 +25,13 @@ func GetPicoclawInternalToken() (string, error) { return picoclawInternalToken.value, nil } - if token, err := readPicoclawInternalToken(); err == nil && token != "" { - picoclawInternalToken.value = token - return token, nil + if token, err := readPicoclawInternalToken(); err == nil { + if token != "" { + picoclawInternalToken.value = token + return token, nil + } + } else if !os.IsNotExist(err) { + return "", err } token := generateRandomSecretKey() @@ -42,6 +46,11 @@ func GetPicoclawInternalToken() (string, error) { return token, nil } +func EnsurePicoclawInternalToken() error { + _, err := GetPicoclawInternalToken() + return err +} + func readPicoclawInternalToken() (string, error) { data, err := os.ReadFile(picoclawInternalTokenFile) if err != nil { diff --git a/server/main.go b/server/main.go index baefcfb..f4f3200 100644 --- a/server/main.go +++ b/server/main.go @@ -28,6 +28,10 @@ func main() { } func initialize() { + if err := config.EnsurePicoclawInternalToken(); err != nil { + log.Fatalf("failed to initialize picoclaw internal token: %v", err) + } + logger.Init() // init screen parameters @@ -82,7 +86,7 @@ func run() { httpAddr, httpsAddr, r, - router.PicoclawLoopbackHTTPAllowedPaths()..., + router.LoopbackHTTPAllowedPaths()..., ); err != nil { panic("start http server failed") } diff --git a/server/router/hid.go b/server/router/hid.go index 67de45f..665a57d 100644 --- a/server/router/hid.go +++ b/server/router/hid.go @@ -7,9 +7,16 @@ import ( "NanoKVM-Server/service/hid" ) +const internalUSBRecoverPath = "/api/internal/usb/recover" + +func HIDLoopbackHTTPAllowedPaths() []string { + return []string{internalUSBRecoverPath} +} + func hidRouter(r *gin.Engine) { service := hid.NewService() api := r.Group("/api").Use(middleware.CheckToken()) + localAPI := r.Group("/api/internal").Use(middleware.CheckLoopbackInternalToken()) api.POST("/hid/paste", service.Paste) // paste @@ -23,4 +30,6 @@ func hidRouter(r *gin.Engine) { api.GET("/hid/mode", service.GetHidMode) // get hid mode api.POST("/hid/mode", service.SetHidMode) // set hid mode api.POST("/hid/reset", service.ResetHid) // reset hid + + localAPI.POST("/usb/recover", service.RecoverUSB) } diff --git a/server/router/router.go b/server/router/router.go index c51fdc6..c731e0c 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -41,3 +41,9 @@ func server(r *gin.Engine) { downloadRouter(r) extensionsRouter(r) } + +func LoopbackHTTPAllowedPaths() []string { + paths := PicoclawLoopbackHTTPAllowedPaths() + paths = append(paths, HIDLoopbackHTTPAllowedPaths()...) + return paths +} diff --git a/server/service/hid/status.go b/server/service/hid/status.go index 08c06a1..186ab3b 100644 --- a/server/service/hid/status.go +++ b/server/service/hid/status.go @@ -91,6 +91,30 @@ func (s *Service) SetHidMode(c *gin.Context) { func (s *Service) ResetHid(c *gin.Context) { var rsp proto.Response + if err := ResetUSBPHY(); err != nil { + log.Errorf("failed to reset hid: %v", err) + rsp.ErrRsp(c, -1, "failed to reset hid") + return + } + + rsp.OkRsp(c) + log.Debugf("reset hid success") +} + +func (s *Service) RecoverUSB(c *gin.Context) { + var rsp proto.Response + + if err := ResetUSBPHY(); err != nil { + log.Errorf("failed to recover usb: %v", err) + rsp.ErrRsp(c, -1, "failed to recover usb") + return + } + + rsp.OkRsp(c) + log.Debugf("recover usb success") +} + +func ResetUSBPHY() error { h := GetHid() h.Lock() h.CloseNoLock() @@ -102,13 +126,10 @@ func (s *Service) ResetHid(c *gin.Context) { command := fmt.Sprintf("%s restart_phy", USBDevScript) err := exec.Command("sh", "-c", command).Run() if err != nil { - log.Errorf("failed to reset hid: %v", err) - rsp.ErrRsp(c, -1, "failed to reset hid") - return + return err } - rsp.OkRsp(c) - log.Debugf("reset hid success") + return nil } func copyModeFile(srcScript string) error { diff --git a/web/src/pages/desktop/menu/power/power-long.tsx b/web/src/pages/desktop/menu/power/power-long.tsx index 4266f3f..4c5a830 100644 --- a/web/src/pages/desktop/menu/power/power-long.tsx +++ b/web/src/pages/desktop/menu/power/power-long.tsx @@ -5,6 +5,8 @@ import { useTranslation } from 'react-i18next'; import * as api from '@/api/vm.ts'; +const DEFAULT_POWER_LONG_DURATION_SECONDS = 12; + type PowerLongProps = { showConfirm: boolean; isLoading: boolean; @@ -14,7 +16,7 @@ type PowerLongProps = { export const PowerLong = ({ showConfirm, isLoading, setIsLoading }: PowerLongProps) => { const { t } = useTranslation(); - const [duration, setDuration] = useState(8); + const [duration, setDuration] = useState(DEFAULT_POWER_LONG_DURATION_SECONDS); function power() { if (isLoading) return; @@ -55,7 +57,7 @@ export const PowerLong = ({ showConfirm, isLoading, setIsLoading }: PowerLongPro