package handlers import ( "strconv" "strings" "time" "server/internal/models" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) func normalizeSSID(ssid string) string { return strings.TrimSpace(ssid) } func ssidKey(ssid string) string { return strings.ToLower(strings.TrimSpace(ssid)) } func normalizeBSSID(bssid string) string { var hex []rune for _, c := range strings.ToLower(bssid) { if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') { hex = append(hex, c) } } if len(hex) != 12 { return strings.TrimSpace(bssid) } s := string(hex) return s[0:2] + ":" + s[2:4] + ":" + s[4:6] + ":" + s[6:8] + ":" + s[8:10] + ":" + s[10:12] } func bssidKey(bssid string) string { var hex []rune for _, c := range strings.ToLower(bssid) { if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') { hex = append(hex, c) } } return string(hex) } type wifiItemPayload struct { SSID string `json:"ssid"` BSSID string `json:"bssid"` } // POST /api/student/report-wifi — ghi nhận SSID + BSSID vào kho func ReportWifiHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { var req struct { StudentRkID int64 `json:"studentRkId"` SSID string `json:"ssid"` BSSID string `json:"bssid"` } if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid payload"}) } ssid := normalizeSSID(req.SSID) bssid := normalizeBSSID(req.BSSID) sKey := ssidKey(ssid) bKey := bssidKey(bssid) if sKey == "" || bKey == "" || len(bKey) != 12 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ssid and valid bssid are required"}) } now := time.Now() var row models.WifiPoolEntry err := db.Where("bssid_key = ?", bKey).First(&row).Error if err == gorm.ErrRecordNotFound { row = models.WifiPoolEntry{ SSID: ssid, SSIDKey: sKey, BSSID: bssid, BSSIDKey: bKey, HitCount: 1, LastSeenAt: now, LastStudentRkID: req.StudentRkID, } if err := db.Create(&row).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } } else if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } else { row.HitCount++ row.LastSeenAt = now row.SSID = ssid row.SSIDKey = sKey row.BSSID = bssid if req.StudentRkID > 0 { row.LastStudentRkID = req.StudentRkID } if err := db.Save(&row).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } } return c.JSON(fiber.Map{"ok": true, "data": row}) } } // GET /api/student/wifi-policy — danh sách điểm phát được phép (theo BSSID) func GetStudentWifiPolicyHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { var rows []models.AcceptedWifi if err := db.Where("bssid_key <> '' AND LENGTH(bssid_key) = 12").Order("ssid asc").Find(&rows).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } items := make([]wifiItemPayload, 0, len(rows)) for _, r := range rows { items = append(items, wifiItemPayload{SSID: r.SSID, BSSID: r.BSSID}) } return c.JSON(fiber.Map{ "enforce": len(items) > 0, "accepted": items, "acceptedSsids": nil, // legacy — client mới dùng accepted }) } } // GET /api/wifi-pool?q=&limit= func ListWifiPoolHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { q := strings.TrimSpace(c.Query("q", "")) limit := 50 if l, err := strconv.Atoi(c.Query("limit", "50")); err == nil && l > 0 && l <= 200 { limit = l } query := db.Model(&models.WifiPoolEntry{}).Where("bssid_key <> '' AND LENGTH(bssid_key) = 12") if q != "" { like := "%" + q + "%" query = query.Where("ssid LIKE ? OR ssid_key LIKE ? OR bssid LIKE ? OR bssid_key LIKE ?", like, like, like, like) } var rows []models.WifiPoolEntry if err := query.Order("hit_count DESC, last_seen_at DESC").Limit(limit).Find(&rows).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": rows, "q": q, "limit": limit}) } } // GET /api/network/accepted-wifis func ListAcceptedWifisHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { var rows []models.AcceptedWifi if err := db.Order("ssid asc, bssid asc").Find(&rows).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": rows}) } } // POST /api/network/accepted-wifis — thay toàn bộ danh sách (cần SSID + BSSID) func SaveAcceptedWifisHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { var req struct { Items []wifiItemPayload `json:"items"` } if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid payload"}) } if err := db.Where("1 = 1").Delete(&models.AcceptedWifi{}).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } now := time.Now() count := 0 seen := map[string]bool{} for _, item := range req.Items { ssid := normalizeSSID(item.SSID) bssid := normalizeBSSID(item.BSSID) sKey := ssidKey(ssid) bKey := bssidKey(bssid) if sKey == "" || bKey == "" || len(bKey) != 12 || seen[bKey] { continue } seen[bKey] = true if err := db.Create(&models.AcceptedWifi{ SSID: ssid, SSIDKey: sKey, BSSID: bssid, BSSIDKey: bKey, CreatedAt: now, }).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } count++ } return c.JSON(fiber.Map{"ok": true, "count": count}) } } // POST /api/network/accepted-wifis/add func AddAcceptedWifiHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { var req wifiItemPayload if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid payload"}) } ssid := normalizeSSID(req.SSID) bssid := normalizeBSSID(req.BSSID) sKey := ssidKey(ssid) bKey := bssidKey(bssid) if sKey == "" || bKey == "" || len(bKey) != 12 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ssid and valid bssid are required"}) } row := models.AcceptedWifi{ SSID: ssid, SSIDKey: sKey, BSSID: bssid, BSSIDKey: bKey, CreatedAt: time.Now(), } if err := db.Where("bssid_key = ?", bKey).FirstOrCreate(&row).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true, "data": row}) } } // DELETE /api/network/accepted-wifis/:id func DeleteAcceptedWifiHandler(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil || id == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid id"}) } if err := db.Delete(&models.AcceptedWifi{}, id).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true}) } }