mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-11-09 17:44:43 -06:00
* feat(storage): browse a medium and exchange files with the target machine The image menu and the download page are replaced by one storage page: it lists the media the emulated USB disk can carry, mounts and ejects them, and browses the files inside a medium over that same gadget, so the two machines exchange files without going through the terminal. Mediated media: * Add the media API (list, state, mount, eject, delete, download, files) and a file browser on top of it, and keep the pre-2.6 image endpoints working as a compatibility layer over the same code. * Add diskfs: an image is mounted through a loop device so the kernel's exFAT and FAT drivers read it, and the data partition is browsed in place, because the emulated disk already hands that very directory to the target machine. * Keep the one-writer policy the read-only mounts introduced: an image is served read-only in every mode, and the whole data partition is remounted read-only here while the target machine is its writer, through the same SetDataWritable the virtual disk switch uses. The file manager refuses those writes itself as well, so a request does not depend on a mount state. * Name the mounted medium and the USB device after the image file, cut to the SCSI inquiry product field and the USB product string, and restore the defaults when the medium is released. * Close the HID devices around a gadget re-enumeration and reopen them after, since mounting and a forced eject drop them. * Report transfers through one job slot with progress, queueing behind a running transfer, and a free space preflight before an upload starts. Image uploads keep the volume descriptor and partition table check, so an unusable image is rejected while it streams. The shared folder view: * Refresh it on a timer while the target machine owns the partition. The target's block level writes land in this device's page cache, which the mounted exFAT shares, so the file data here is already current - but the dentry and inode caches are not, and a file the target grew keeps the size it was first read with. Dropping those caches and pushing a storage event makes the web UI follow the target machine within a second of its own writeback. The partition's write counter cannot drive this: it only moves once this side writes its dirty pages back, which is tens of seconds later or never. * Send the refresh notifications from their own goroutine, so a client that stopped reading cannot hold up the refresh loop. The web UI: * Add the storage page: the medium list with mount and eject, the file browser with search and sorting, uploads, downloads and deletes, and transfer progress. * Gate writing on the target machine owning the disk - image uploads and file uploads alike - instead of letting the server answer with an error the page has to surface, and drop the medium rows' download and delete buttons there too. * Give the shared folder, .iso and .img their own glyphs, and pick the file icon from the extension, so a listing can be read at a glance. * Name the partition the way the storage page does in the desktop menu as well, instead of showing the API's untranslated string. * Read the mounted medium in Settings - Device from the media state, now that the storage API it used is gone. * Tell an ISO 9660 image apart from a filesystem that cannot be read at all: the kernel of this device carries no iso9660 support (CONFIG_ISO9660_FS), so the contents of an ISO cannot be listed while the image itself is served to the target machine like any other medium. Localization: * Translate the new strings for every locale, keep the Settings - Device strings the read-only mounts added, and drop the keys the retired image menu and download page left behind. Chores: * Remove the desktop image menu, the download page and the API they used. * fix(storage): harden media file transfers Serialize medium deletion with gadget attachment and restore /data write access after failed mount transitions. Use available filesystem space for upload limits, refine virtual-device and shared-file handling, and update media path and installation test coverage. Pin an image while it is streamed out, so a writable mount cannot start between the check and the last byte; sweep stale upload temporaries from the whole data partition at startup, which the file manager itself cannot delete; and preflight URL downloads against the response size, which only the upload path did. Show dot files in the file browser again: only the two transfer temporary prefixes stay hidden, which is what the dot filter was for. Delete folders recursively after the web UI confirms, refusing when an attached or mounted medium lives inside the directory; accept every .iso and .img upload by name instead of sniffing volume descriptors; and show image and file transfers in one progress display at the top of the storage page. * fix(storage): make cache drops work and close delete and gadget races - drop_caches only accepts 1..4 on 5.10: write 4 once to silence the log, then 2/3. Writing 6/7 failed with EINVAL, so the shared folder never saw what the target machine wrote. - Resolve a delete path to its on-disk spelling (exFAT is case-insensitive) before checking it against the attached and loop-mounted media, refuse deleting a loop-mounted image or a folder holding an upload target, and fail closed when the disk state cannot be read. - GetMediaState no longer fails when the virtual disk is off and lun.0 does not exist; browsing works with the virtual disk off. - An explicit enable for the virtual disk compares against the disk being configured, so it leaves a served image alone; the settings toggle keeps the #937 semantics. - Restore /data write access after a failed gadget script or network switch. - eject clears the LUN file before resetting ro/cdrom, so a refused eject does not change the type of the medium still being served. - Mount loop images with MS_NOSYMFOLLOW and open files with O_NOFOLLOW. - Accept UDF images as optical media. - Only refuse deleting transfer temporaries while a transfer runs; sweep stale ones in the background, by exact name, and not on a read-only /data; hide them from listings. - Cap URL downloads without Content-Length at the free space and verify the received length. - Encode Content-Disposition filename* strictly per RFC 5987. - Restore image_test.go and add tests for the above. * fix(web): keep the file browser and transfers consistent on the storage page - Tag a listing with the medium and path it belongs to, so entries of the previous directory are never shown or acted on under a new path. - Poll the desktop storage menu only while it is open. - Show a dedicated message for a checksum mismatch. - Bring back the USB / SCP / TF card hints behind a help button. - Pick up a transfer already running when the page opens; make uploads cancellable; track concurrent uploads separately; let a failed job be dismissed; show a success state and guard the progress display by task. - Handle request errors that were left unhandled. - Dedicated confirmation for deleting a folder recursively. - Treat UDF images as optical, send credentials on the image download pre-check, translate the remaining Hungarian string. --------- Co-authored-by: Guoguo <i@qwq.trade>
2.4 KiB
2.4 KiB
NanoKVM Frontend
This is NanoKVM web project. For more documentation, please refer to the Wiki.
Structure
src
├── api // backend api
├── assets // static resources
├── components // public components
├── i18n // language resources
├── jotai // Global jotai variables
├── lib // util libs
├── pages // web pages
│ ├── auth // login and password
│ ├── desktop // remote desktop
│ ├── storage // virtual media and the files inside them
│ └── terminal // web terminal
├── router.tsx // routers
└── types // types
Local Development
Requirements: Node.js >= 22 and pnpm >= 11 (the versions CI uses; enforced via the engines field in package.json).
Development requires SSH. You can enable it in the Web Settings:
Settings > SSH.
Due to CORS restrictions, authentication needs to be disabled during local development.
To develop authentication features, you need to build the project and test in NanoKVM.
- Log in to NanoKVM via SSH:
ssh root@your-nanokvm-ip(default password is root). - Open the configuration file
/etc/kvm/server.yaml/and addauthentication: disable. ⚠️CAUTION: This option disables all authentication and should NOT be enabled in production environment! - Restart the service:
/etc/init.d/S95nanokvm restart. - Edit the
.env.developmentfile and changeVITE_SERVER_IPto your NanoKVM IP address. - Run
pnpm devto start the server and visit http://localhost:3001/ in browser.
It's recommended to disable browser caching to avoid access issues during development:
- Open the browser Developer Tools;
- Go to the
Networktab; - Check
Disable cacheoption; - Refresh the page.
Deployment
Build:
cd web
pnpm install
pnpm build
- After the compilation is complete, the
distfolder will be generated. - Rename the folder to
web. - Upload
webto/kvmapp/server/in NanoKVM. - Restart the service by executing
/etc/init.d/S95nanokvm restartin NanoKVM.
Tips:
- File uploads requires SSH. You can enable it in the Web Settings:
Settings > SSH. - Browser may have old version cache. If you can't open the page, try a force refresh or clear the cache.