mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-09-11 00:22:56 -05:00
@@ -12,6 +12,11 @@ type DeleteMacReq struct {
|
||||
Mac string `form:"mac" validate:"required"`
|
||||
}
|
||||
|
||||
type SetMacNameReq struct {
|
||||
Mac string `form:"mac" validate:"required"`
|
||||
Name string `form:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type TailscaleState string
|
||||
|
||||
const (
|
||||
|
||||
@@ -14,9 +14,9 @@ func networkRouter(r *gin.Engine) {
|
||||
|
||||
api := r.Group("/api").Use(middleware.CheckToken())
|
||||
|
||||
api.POST("/network/wol", service.WakeOnLAN) // wake on lan
|
||||
api.GET("/network/wol/mac", service.GetMac) // get mac list
|
||||
api.DELETE("/network/wol/mac", service.DeleteMac) // delete mac
|
||||
|
||||
api.GET("/network/wifi", service.GetWifi) // get Wi-Fi information
|
||||
api.POST("/network/wol", service.WakeOnLAN) // wake on lan
|
||||
api.GET("/network/wol/mac", service.GetMac) // get mac list
|
||||
api.DELETE("/network/wol/mac", service.DeleteMac) // delete mac
|
||||
api.POST("/network/wol/mac/setName", service.SetMacName) // set mac name
|
||||
api.GET("/network/wifi", service.GetWifi) // get Wi-Fi information
|
||||
}
|
||||
|
||||
@@ -65,6 +65,54 @@ func (s *Service) GetMac(c *gin.Context) {
|
||||
rsp.OkRspWithData(c, data)
|
||||
}
|
||||
|
||||
func (s *Service) SetMacName(c *gin.Context) {
|
||||
var req proto.SetMacNameReq // Mac:string Name:string
|
||||
var rsp proto.Response
|
||||
|
||||
if err := proto.ParseFormRequest(c, &req); err != nil {
|
||||
rsp.ErrRsp(c, -1, "invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(WolMacFile)
|
||||
if err != nil {
|
||||
log.Errorf("failed to open %s: %s", WolMacFile, err)
|
||||
rsp.ErrRsp(c, -2, "read failed")
|
||||
return
|
||||
}
|
||||
|
||||
macs := strings.Split(string(content), "\n")
|
||||
var newLines []string
|
||||
macFound := false
|
||||
|
||||
for _, line := range macs {
|
||||
parts := strings.Split(line, " ")
|
||||
if req.Mac != parts[0] {
|
||||
newLines = append(newLines, line)
|
||||
continue
|
||||
}
|
||||
newLines = append(newLines, parts[0]+" "+req.Name)
|
||||
macFound = true
|
||||
}
|
||||
|
||||
if !macFound {
|
||||
log.Errorf("failed to found mac %s: %s", req.Mac, err)
|
||||
rsp.ErrRsp(c, -3, "write failed")
|
||||
return
|
||||
}
|
||||
|
||||
data := strings.Join(newLines, "\n")
|
||||
err = os.WriteFile(WolMacFile, []byte(data), 0o644)
|
||||
if err != nil {
|
||||
log.Errorf("failed to write %s: %s", WolMacFile, err)
|
||||
rsp.ErrRsp(c, -3, "write failed")
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("set wol mac name: %s %s", req.Mac, req.Name)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteMac(c *gin.Context) {
|
||||
var req proto.DeleteMacReq
|
||||
var rsp proto.Response
|
||||
@@ -85,7 +133,8 @@ func (s *Service) DeleteMac(c *gin.Context) {
|
||||
var newMacs []string
|
||||
|
||||
for _, mac := range macs {
|
||||
if req.Mac != mac {
|
||||
parts := strings.Split(mac, " ")
|
||||
if req.Mac != parts[0] {
|
||||
newMacs = append(newMacs, mac)
|
||||
}
|
||||
}
|
||||
@@ -164,7 +213,8 @@ func isMacExist(mac string) bool {
|
||||
|
||||
macs := strings.Split(string(content), "\n")
|
||||
for _, item := range macs {
|
||||
if mac == item {
|
||||
parts := strings.Split(item, " ")
|
||||
if mac == parts[0] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ export function deleteWolMac(mac: string) {
|
||||
});
|
||||
}
|
||||
|
||||
// set Mac name
|
||||
export function setWolMacName(mac: string,name: string) {
|
||||
return http.post('/api/network/wol/mac/setName', { mac, name });
|
||||
}
|
||||
|
||||
// get wifi information
|
||||
export function getWiFi() {
|
||||
return http.get('/api/network/wifi');
|
||||
|
||||
@@ -3,13 +3,21 @@ import { Button, Divider, Input, List } from 'antd';
|
||||
import type { InputRef } from 'antd';
|
||||
import clsx from 'clsx';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { NetworkIcon, SendIcon, Trash2Icon } from 'lucide-react';
|
||||
import { Eye, EyeClosed, NetworkIcon, Pencil, SendIcon, Trash2Icon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { deleteWolMac, getWolMacs, wol } from '@/api/network.ts';
|
||||
import { deleteWolMac, getWolMacs, setWolMacName, wol } from '@/api/network.ts';
|
||||
import { isKeyboardEnableAtom } from '@/jotai/keyboard.ts';
|
||||
import { MenuItem } from '@/components/menu-item.tsx';
|
||||
|
||||
interface MacItem {
|
||||
name: string;
|
||||
mac: string;
|
||||
isShow: boolean;
|
||||
isName: boolean;
|
||||
isEdit: boolean;
|
||||
}
|
||||
|
||||
export const Wol = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -19,20 +27,18 @@ export const Wol = () => {
|
||||
const [status, setStatus] = useState('');
|
||||
const [log, setLog] = useState('');
|
||||
|
||||
const [macList, setMacList] = useState<string[]>([]);
|
||||
const [macList, setMacList] = useState<MacItem[]>([]);
|
||||
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
|
||||
function handleOpenChange(open: boolean) {
|
||||
if (open) {
|
||||
getMacs();
|
||||
|
||||
setIsKeyboardEnable(false);
|
||||
} else {
|
||||
setInput('');
|
||||
setStatus('');
|
||||
setLog('');
|
||||
|
||||
setIsKeyboardEnable(true);
|
||||
}
|
||||
}
|
||||
@@ -48,14 +54,53 @@ export const Wol = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
setMacList(rsp.data.macs.filter((mac: string) => mac !== ''));
|
||||
const isEdit = false;
|
||||
const macList = rsp.data.macs
|
||||
.filter((item: string) => item.trim() !== '')
|
||||
.map((item: string) => {
|
||||
const parts = item.split(" ");
|
||||
const isName = parts.length > 1;
|
||||
const name = isName ? parts[1] : '';
|
||||
const mac = parts[0];
|
||||
const isShow = !isName;
|
||||
return { name, mac, isShow, isName, isEdit};
|
||||
});
|
||||
|
||||
setMacList([]);
|
||||
setMacList(macList);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleShow(mac: string) {
|
||||
setMacList(
|
||||
macList.map((item) =>
|
||||
item.mac === mac ? { ...item, isShow: !item.isShow } : item
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function editMac(mac: string,isEdit: boolean) {
|
||||
setMacList(
|
||||
macList.map((item) =>
|
||||
item.mac === mac ? { ...item, isEdit: !isEdit } : item
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function setMacName(e: React.KeyboardEvent<HTMLInputElement>, mac: string) {
|
||||
const value: string = e.currentTarget.value;
|
||||
const rsp = await setWolMacName(mac,value);
|
||||
if (rsp.code !== 0) {
|
||||
console.log(rsp.msg);
|
||||
return;
|
||||
}
|
||||
getMacs();
|
||||
}
|
||||
|
||||
function deleteMac(mac: string) {
|
||||
deleteWolMac(mac).then((rsp) => {
|
||||
if (rsp.code === 0) {
|
||||
setMacList(macList.filter((item) => item !== mac));
|
||||
getMacs();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -79,10 +124,12 @@ export const Wol = () => {
|
||||
|
||||
setStatus('success');
|
||||
setLog(t('wol.sent'));
|
||||
getMacs();
|
||||
setInput('');
|
||||
})
|
||||
.catch(() => {
|
||||
setStatus('failed');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const content = (
|
||||
@@ -95,7 +142,7 @@ export const Wol = () => {
|
||||
|
||||
<div className="pb-1 text-neutral-500">{t('wol.input')}</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<Input ref={inputRef} value={input} onChange={handleChange} />
|
||||
<Input ref={inputRef} value={input} onChange={handleChange} onPressEnter={() => wake()}/>
|
||||
<Button type="primary" onClick={() => wake()}>
|
||||
{t('wol.ok')}
|
||||
</Button>
|
||||
@@ -118,19 +165,34 @@ export const Wol = () => {
|
||||
itemLayout="horizontal"
|
||||
locale={{ emptyText: ' ' }}
|
||||
dataSource={macList}
|
||||
renderItem={(mac) => (
|
||||
renderItem={(item) => (
|
||||
<List.Item className="flex w-full items-center justify-between">
|
||||
<div className="h-[24px] max-w-[200px]">{mac}</div>
|
||||
<div className="h-[24px] max-w-[200px]">
|
||||
{item.isEdit ? <Input onFocus={() => setIsKeyboardEnable(false)} onBlur={() => setIsKeyboardEnable(true)} defaultValue={item.name} onPressEnter={(e) => setMacName(e, item.mac)} />:(item.isShow ? item.mac : item.name)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-1">
|
||||
{item.isName && <div
|
||||
className="flex h-[24px] w-[24px] cursor-pointer items-center justify-center rounded text-500 hover:bg-neutral-700/80"
|
||||
onClick={() => toggleShow(item.mac)}
|
||||
>
|
||||
{item.isShow ? <EyeClosed size={16} /> : <Eye size={16} />}
|
||||
</div>}
|
||||
<div
|
||||
className="flex h-[24px] w-[24px] cursor-pointer items-center justify-center rounded text-500 hover:bg-neutral-700"
|
||||
onClick={() => editMac(item.mac,item.isEdit)}
|
||||
>
|
||||
<Pencil size={16} />
|
||||
</div>
|
||||
<div
|
||||
className="flex h-[24px] w-[24px] cursor-pointer items-center justify-center rounded text-green-500 hover:bg-neutral-700/80"
|
||||
onClick={() => wake(mac)}
|
||||
onClick={() => wake(item.mac)}
|
||||
>
|
||||
<SendIcon size={16} />
|
||||
</div>
|
||||
<div
|
||||
className="flex h-[24px] w-[24px] cursor-pointer items-center justify-center rounded text-red-500 hover:bg-neutral-700"
|
||||
onClick={() => deleteMac(mac)}
|
||||
onClick={() => deleteMac(item.mac)}
|
||||
>
|
||||
<Trash2Icon size={16} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user