rebase
This commit is contained in:
740
device.go
Executable file
740
device.go
Executable file
@@ -0,0 +1,740 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Device 设备信息结构
|
||||
type Device struct {
|
||||
MAC string `json:"mac"`
|
||||
Username string `json:"username"`
|
||||
DeviceName string `json:"device_name"`
|
||||
Token string `json:"token"`
|
||||
BindTime time.Time `json:"bind_time"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// BindingCode 绑定码结构
|
||||
type BindingCode struct {
|
||||
Code string `json:"code"`
|
||||
Username string `json:"username"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
Used bool `json:"used"`
|
||||
}
|
||||
|
||||
// DeviceManager 设备管理器
|
||||
type DeviceManager struct {
|
||||
devices map[string]*Device // MAC -> Device
|
||||
bindingCodes map[string]*BindingCode // Code -> BindingCode
|
||||
tokens map[string]string // Token -> MAC
|
||||
mu sync.RWMutex
|
||||
filePath string
|
||||
}
|
||||
|
||||
var deviceManager *DeviceManager
|
||||
var deviceManagerOnce sync.Once
|
||||
|
||||
// GetDeviceManager 获取设备管理器单例
|
||||
func GetDeviceManager() *DeviceManager {
|
||||
deviceManagerOnce.Do(func() {
|
||||
deviceManager = &DeviceManager{
|
||||
devices: make(map[string]*Device),
|
||||
bindingCodes: make(map[string]*BindingCode),
|
||||
tokens: make(map[string]string),
|
||||
filePath: "./devices.json",
|
||||
}
|
||||
deviceManager.LoadFromFile()
|
||||
})
|
||||
return deviceManager
|
||||
}
|
||||
|
||||
// GenerateBindingCode 生成6位数字绑定码
|
||||
func (dm *DeviceManager) GenerateBindingCode(username string) (string, error) {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
|
||||
// 生成6位随机数字
|
||||
var code string
|
||||
for i := 0; i < 6; i++ {
|
||||
n, err := rand.Int(rand.Reader, big.NewInt(10))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
code += fmt.Sprintf("%d", n.Int64())
|
||||
}
|
||||
|
||||
// 检查是否已存在(小概率)
|
||||
if _, exists := dm.bindingCodes[code]; exists {
|
||||
// 递归重新生成
|
||||
return dm.GenerateBindingCode(username)
|
||||
}
|
||||
|
||||
// 创建绑定码,5分钟有效
|
||||
bindingCode := &BindingCode{
|
||||
Code: code,
|
||||
Username: username,
|
||||
ExpiresAt: time.Now().Add(5 * time.Minute),
|
||||
Used: false,
|
||||
}
|
||||
|
||||
dm.bindingCodes[code] = bindingCode
|
||||
fmt.Printf("[Device] Generated binding code %s for user %s\n", code, username)
|
||||
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// generateDeviceToken 生成设备Token
|
||||
func generateDeviceToken() string {
|
||||
b := make([]byte, 32)
|
||||
rand.Read(b)
|
||||
return fmt.Sprintf("%x", b)
|
||||
}
|
||||
|
||||
// BindDevice 绑定设备
|
||||
func (dm *DeviceManager) BindDevice(mac, bindingCode, deviceName string) (*Device, error) {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
|
||||
// 验证绑定码
|
||||
code, exists := dm.bindingCodes[bindingCode]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("绑定码不存在")
|
||||
}
|
||||
|
||||
if code.Used {
|
||||
return nil, fmt.Errorf("绑定码已使用")
|
||||
}
|
||||
|
||||
if time.Now().After(code.ExpiresAt) {
|
||||
return nil, fmt.Errorf("绑定码已过期")
|
||||
}
|
||||
|
||||
// 检查设备是否已绑定
|
||||
if existingDevice, exists := dm.devices[mac]; exists {
|
||||
return nil, fmt.Errorf("设备已绑定到用户 %s", existingDevice.Username)
|
||||
}
|
||||
|
||||
// 生成Token
|
||||
token := generateDeviceToken()
|
||||
|
||||
// 创建设备
|
||||
device := &Device{
|
||||
MAC: mac,
|
||||
Username: code.Username,
|
||||
DeviceName: deviceName,
|
||||
Token: token,
|
||||
BindTime: time.Now(),
|
||||
LastSeen: time.Now(),
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
// 保存设备信息
|
||||
dm.devices[mac] = device
|
||||
dm.tokens[token] = mac
|
||||
|
||||
// 标记绑定码已使用
|
||||
code.Used = true
|
||||
|
||||
// 保存到文件
|
||||
dm.SaveToFile()
|
||||
|
||||
fmt.Printf("[Device] Device %s bound to user %s\n", mac, code.Username)
|
||||
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// VerifyToken 验证设备Token
|
||||
func (dm *DeviceManager) VerifyToken(token string) (*Device, error) {
|
||||
dm.mu.RLock()
|
||||
defer dm.mu.RUnlock()
|
||||
|
||||
mac, exists := dm.tokens[token]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("无效的Token")
|
||||
}
|
||||
|
||||
device, exists := dm.devices[mac]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("设备不存在")
|
||||
}
|
||||
|
||||
if !device.IsActive {
|
||||
return nil, fmt.Errorf("设备已停用")
|
||||
}
|
||||
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// GetDeviceByMAC 根据MAC地址获取设备
|
||||
func (dm *DeviceManager) GetDeviceByMAC(mac string) (*Device, error) {
|
||||
dm.mu.RLock()
|
||||
defer dm.mu.RUnlock()
|
||||
|
||||
device, exists := dm.devices[mac]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("设备未绑定")
|
||||
}
|
||||
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// UpdateLastSeen 更新设备最后在线时间
|
||||
func (dm *DeviceManager) UpdateLastSeen(mac string) {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
|
||||
if device, exists := dm.devices[mac]; exists {
|
||||
device.LastSeen = time.Now()
|
||||
dm.SaveToFile()
|
||||
}
|
||||
}
|
||||
|
||||
// DirectBindDevice 直接绑定设备(不需要绑定码)
|
||||
func (dm *DeviceManager) DirectBindDevice(mac, username, deviceName string) (*Device, error) {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
|
||||
// 检查设备是否已绑定
|
||||
if existingDevice, exists := dm.devices[mac]; exists {
|
||||
return nil, fmt.Errorf("设备已绑定到用户 %s", existingDevice.Username)
|
||||
}
|
||||
|
||||
// 生成Token
|
||||
token := generateDeviceToken()
|
||||
|
||||
// 创建设备
|
||||
device := &Device{
|
||||
MAC: mac,
|
||||
Username: username,
|
||||
DeviceName: deviceName,
|
||||
Token: token,
|
||||
BindTime: time.Now(),
|
||||
LastSeen: time.Now(),
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
dm.devices[mac] = device
|
||||
dm.tokens[token] = mac
|
||||
|
||||
dm.SaveToFile()
|
||||
|
||||
fmt.Printf("[Device] Device %s directly bound to user %s\n", mac, username)
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// UnbindDevice 解绑设备
|
||||
func (dm *DeviceManager) UnbindDevice(mac string) error {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
|
||||
device, exists := dm.devices[mac]
|
||||
if !exists {
|
||||
return fmt.Errorf("设备不存在")
|
||||
}
|
||||
|
||||
// 删除Token映射
|
||||
delete(dm.tokens, device.Token)
|
||||
// 删除设备
|
||||
delete(dm.devices, mac)
|
||||
|
||||
dm.SaveToFile()
|
||||
|
||||
fmt.Printf("[Device] Device %s unbound\n", mac)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDevice 获取单个设备信息
|
||||
func (dm *DeviceManager) GetDevice(mac string) (*Device, error) {
|
||||
dm.mu.RLock()
|
||||
defer dm.mu.RUnlock()
|
||||
|
||||
device, exists := dm.devices[mac]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("设备不存在")
|
||||
}
|
||||
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// GetUserDevices 获取用户的所有设备
|
||||
func (dm *DeviceManager) GetUserDevices(username string) []*Device {
|
||||
dm.mu.RLock()
|
||||
defer dm.mu.RUnlock()
|
||||
|
||||
var devices []*Device
|
||||
for _, device := range dm.devices {
|
||||
if device.Username == username {
|
||||
devices = append(devices, device)
|
||||
}
|
||||
}
|
||||
|
||||
return devices
|
||||
}
|
||||
|
||||
// SaveToFile 保存设备信息到文件
|
||||
func (dm *DeviceManager) SaveToFile() error {
|
||||
data := struct {
|
||||
Devices map[string]*Device `json:"devices"`
|
||||
}{
|
||||
Devices: dm.devices,
|
||||
}
|
||||
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
fmt.Println("[Error] Failed to marshal devices:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(dm.filePath, jsonData, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("[Error] Failed to write devices.json:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadFromFile 从文件加载设备信息
|
||||
func (dm *DeviceManager) LoadFromFile() error {
|
||||
file, err := os.Open(dm.filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println("[Info] devices.json not found, creating new file")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var fileData struct {
|
||||
Devices map[string]*Device `json:"devices"`
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &fileData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dm.devices = fileData.Devices
|
||||
if dm.devices == nil {
|
||||
dm.devices = make(map[string]*Device)
|
||||
}
|
||||
|
||||
// 重建Token索引
|
||||
for mac, device := range dm.devices {
|
||||
dm.tokens[device.Token] = mac
|
||||
}
|
||||
|
||||
fmt.Printf("[Info] Loaded %d devices from file\n", len(dm.devices))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTP处理器
|
||||
|
||||
// GenerateBindingCodeHandler 生成绑定码
|
||||
func GenerateBindingCodeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前登录用户
|
||||
username := GetCurrentUser(r) // 需要从session获取
|
||||
if username == "" {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
code, err := dm.GenerateBindingCode(username)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to generate binding code", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"code": code,
|
||||
"expires_in": 300, // 5分钟
|
||||
})
|
||||
}
|
||||
|
||||
// BindDeviceHandler ESP32设备绑定接口
|
||||
func BindDeviceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("[API] ESP32 device bind request received from", r.RemoteAddr)
|
||||
|
||||
// 解析请求
|
||||
var req struct {
|
||||
MAC string `json:"mac"`
|
||||
BindingCode string `json:"binding_code"`
|
||||
DeviceName string `json:"device_name"`
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.MAC == "" || req.BindingCode == "" {
|
||||
http.Error(w, "Missing required fields", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有提供设备名称,使用默认名称
|
||||
if req.DeviceName == "" {
|
||||
req.DeviceName = "ESP32音乐播放器"
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
device, err := dm.BindDevice(req.MAC, req.BindingCode, req.DeviceName)
|
||||
if err != nil {
|
||||
fmt.Printf("[Error] Device bind failed: %v\n", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "设备绑定成功",
|
||||
"token": device.Token,
|
||||
"username": device.Username,
|
||||
})
|
||||
}
|
||||
|
||||
// VerifyDeviceHandler 验证设备Token
|
||||
func VerifyDeviceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
token := r.Header.Get("X-Device-Token")
|
||||
if token == "" {
|
||||
token = r.URL.Query().Get("token")
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
http.Error(w, "Missing token", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
device, err := dm.VerifyToken(token)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新最后在线时间
|
||||
dm.UpdateLastSeen(device.MAC)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"device": map[string]interface{}{
|
||||
"mac": device.MAC,
|
||||
"username": device.Username,
|
||||
"device_name": device.DeviceName,
|
||||
"bind_time": device.BindTime,
|
||||
"last_seen": device.LastSeen,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetCurrentUser 从请求中获取当前登录用户
|
||||
func GetCurrentUser(r *http.Request) string {
|
||||
userStore := GetUserStore()
|
||||
|
||||
// 1. 尝试从 Authorization Header 获取 (Bearer Token)
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader != "" {
|
||||
token := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
// 直接从UserStore验证Token
|
||||
user, err := userStore.GetUserByToken(token)
|
||||
if err == nil && user != nil {
|
||||
return user.Username
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 尝试从 Cookie 获取 session_token
|
||||
cookie, err := r.Cookie("session_token")
|
||||
if err == nil {
|
||||
username := userStore.GetUsernameByToken(cookie.Value)
|
||||
if username != "" {
|
||||
return username
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 尝试从 X-Device-Token Header 获取 (用于ESP32设备)
|
||||
deviceToken := r.Header.Get("X-Device-Token")
|
||||
if deviceToken != "" {
|
||||
dm := GetDeviceManager()
|
||||
device, err := dm.VerifyToken(deviceToken)
|
||||
if err == nil && device != nil {
|
||||
// 更新最后在线时间
|
||||
dm.UpdateLastSeen(device.MAC)
|
||||
return device.Username
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// DirectBindDeviceHandler Web端直接绑定设备(不需要绑定码)
|
||||
func DirectBindDeviceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户
|
||||
username := GetCurrentUser(r)
|
||||
if username == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "未登录",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 解析请求
|
||||
var req struct {
|
||||
MAC string `json:"mac"`
|
||||
DeviceName string `json:"device_name"`
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "请求格式错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.MAC == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "MAC地址不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有提供设备名称,使用默认名称
|
||||
if req.DeviceName == "" {
|
||||
req.DeviceName = "ESP32音乐播放器"
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
device, err := dm.DirectBindDevice(req.MAC, username, req.DeviceName)
|
||||
if err != nil {
|
||||
fmt.Printf("[Error] Direct bind failed: %v\n", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "设备绑定成功",
|
||||
"device": map[string]interface{}{
|
||||
"mac": device.MAC,
|
||||
"device_name": device.DeviceName,
|
||||
"bind_time": device.BindTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ListUserDevicesHandler 列出用户的所有设备
|
||||
func ListUserDevicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户
|
||||
username := GetCurrentUser(r)
|
||||
if username == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "未登录",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
devices := dm.GetUserDevices(username)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"devices": devices,
|
||||
})
|
||||
}
|
||||
|
||||
// UnbindDeviceHandler 解绑设备
|
||||
func UnbindDeviceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户
|
||||
username := GetCurrentUser(r)
|
||||
if username == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "未登录",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 解析请求
|
||||
var req struct {
|
||||
MAC string `json:"mac"`
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "请求格式错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.MAC == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "MAC地址不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
|
||||
// 检查设备是否属于当前用户
|
||||
device, err := dm.GetDevice(req.MAC)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "设备不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if device.Username != username {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "无权解绑此设备",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 解绑设备
|
||||
err = dm.UnbindDevice(req.MAC)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "设备已解绑",
|
||||
})
|
||||
}
|
||||
|
||||
// SyncDeviceHandler ESP32用MAC地址同步Token(用于网页端绑定后自动获取token)
|
||||
func SyncDeviceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
MAC string `json:"mac"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.MAC == "" {
|
||||
http.Error(w, "Missing MAC address", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
dm := GetDeviceManager()
|
||||
device, err := dm.GetDevice(req.MAC)
|
||||
|
||||
if err != nil || device == nil {
|
||||
// 设备未绑定
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "设备未绑定",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新最后在线时间
|
||||
dm.UpdateLastSeen(device.MAC)
|
||||
|
||||
// 返回 token 和用户名
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"token": device.Token,
|
||||
"username": device.Username,
|
||||
"message": "同步成功",
|
||||
})
|
||||
|
||||
fmt.Printf("[Info] Device %s synced token for user: %s\n", device.MAC, device.Username)
|
||||
}
|
||||
Reference in New Issue
Block a user