feat: add internal USB recovery and initialize picoclaw token

This commit is contained in:
wenjie
2026-04-16 17:00:16 +08:00
parent aa0546ff37
commit cefe0f9ab2
6 changed files with 62 additions and 11 deletions

View File

@@ -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 {

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -41,3 +41,9 @@ func server(r *gin.Engine) {
downloadRouter(r)
extensionsRouter(r)
}
func LoopbackHTTPAllowedPaths() []string {
paths := PicoclawLoopbackHTTPAllowedPaths()
paths = append(paths, HIDLoopbackHTTPAllowedPaths()...)
return paths
}

View File

@@ -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 {

View File

@@ -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
<div className="px-3">
<Slider
defaultValue={8}
defaultValue={DEFAULT_POWER_LONG_DURATION_SECONDS}
min={1}
max={30}
tooltip={{ placement: 'bottom' }}