214 lines
6.3 KiB
Go
214 lines
6.3 KiB
Go
package handlers
|
||
|
||
import (
|
||
"bytes"
|
||
"database/sql"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
"licensing-cotton/internal/config"
|
||
"licensing-cotton/internal/database"
|
||
"licensing-cotton/internal/models"
|
||
"net/http"
|
||
"time"
|
||
)
|
||
|
||
// CreateRequest 记录新的 renew 申请 (如果设备未在申请列表中)
|
||
func CreateRequest(deviceID string) error {
|
||
var existingStatus string
|
||
|
||
// 查询该设备是否已经有 `pending_requests` 记录
|
||
err := database.DB.QueryRow(`SELECT status FROM pending_requests WHERE device_id = ? ORDER BY request_time DESC LIMIT 1`, deviceID).
|
||
Scan(&existingStatus)
|
||
|
||
if err == nil {
|
||
if existingStatus == "pending" {
|
||
// 如果已有 `pending` 申请,则不允许重复提交
|
||
return errors.New("该设备已有未处理的续期申请,等待管理员审批")
|
||
}
|
||
// 如果 `status` 是 `approved` 或 `rejected`,则允许提交新申请
|
||
} else if err != sql.ErrNoRows {
|
||
// 查询时发生错误
|
||
return fmt.Errorf("查询续期申请失败: %w", err)
|
||
}
|
||
|
||
// 插入新的申请(设为 `pending`)
|
||
_, err = database.DB.Exec(
|
||
`INSERT INTO pending_requests (device_id, request_time, status) VALUES (?, ?, ?)`,
|
||
deviceID, time.Now(), "pending",
|
||
)
|
||
if err != nil {
|
||
return fmt.Errorf("创建续期申请失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func HandleListPendingRequests(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// 管理员权限检查
|
||
if !isAdminRequest(w, r) {
|
||
return
|
||
}
|
||
|
||
// **查询所有未审批的请求**
|
||
rows, err := database.DB.Query(`
|
||
SELECT id, device_id, request_time, status, approved_by, approved_at, expiration
|
||
FROM pending_requests WHERE status='pending'
|
||
`)
|
||
if err != nil {
|
||
http.Error(w, "查询待审批请求失败: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
defer rows.Close()
|
||
|
||
pendingRequests := []models.PendingRequest{}
|
||
for rows.Next() {
|
||
var req models.PendingRequest
|
||
var approvedBy sql.NullString
|
||
var approvedAt sql.NullTime
|
||
var expiration sql.NullTime
|
||
|
||
// **扫描所有字段**
|
||
if err := rows.Scan(&req.ID, &req.DeviceID, &req.RequestTime, &req.Status, &approvedBy, &approvedAt, &expiration); err != nil {
|
||
http.Error(w, "解析查询结果失败: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// **处理 `NULL` 值**
|
||
if approvedBy.Valid {
|
||
req.ApprovedBy = &approvedBy.String
|
||
}
|
||
if approvedAt.Valid {
|
||
req.ApprovedAt = &approvedAt.Time
|
||
}
|
||
if expiration.Valid {
|
||
req.Expiration = &expiration.Time
|
||
}
|
||
|
||
pendingRequests = append(pendingRequests, req)
|
||
}
|
||
|
||
// **返回 JSON**
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(pendingRequests)
|
||
}
|
||
|
||
func HandleDeviceRequest(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
if !isAdminRequest(w, r) {
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
DeviceID string `json:"device_id"`
|
||
Expiration string `json:"expiration"`
|
||
ApprovedBy string `json:"approved_by"`
|
||
Action string `json:"action"` // "approve" 或 "reject"
|
||
}
|
||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
http.Error(w, "解析请求失败: "+err.Error(), http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
switch req.Action {
|
||
case "approve":
|
||
handleApprove(w, struct{ DeviceID, Expiration, ApprovedBy, Action string }(req))
|
||
case "reject":
|
||
handleReject(w, struct{ DeviceID, Expiration, ApprovedBy, Action string }(req))
|
||
default:
|
||
http.Error(w, "无效的操作", http.StatusBadRequest)
|
||
}
|
||
}
|
||
|
||
func handleApprove(w http.ResponseWriter, req struct{ DeviceID, Expiration, ApprovedBy, Action string }) {
|
||
expirationTime, err := time.Parse(time.RFC3339, req.Expiration)
|
||
if err != nil {
|
||
http.Error(w, "无效的 expiration 格式", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
var exists bool
|
||
err = database.DB.QueryRow(`SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = ?)`, req.DeviceID).Scan(&exists)
|
||
if err != nil {
|
||
http.Error(w, "数据库查询失败: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
if !exists {
|
||
_, err = database.DB.Exec(`INSERT INTO devices (device_id, expiration) VALUES (?, ?)`, req.DeviceID, expirationTime)
|
||
} else {
|
||
_, err = database.DB.Exec(`UPDATE devices SET expiration = ? WHERE device_id = ?`, expirationTime, req.DeviceID)
|
||
}
|
||
|
||
if err != nil {
|
||
http.Error(w, "处理设备记录失败: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
_, err = database.DB.Exec(`UPDATE pending_requests SET status='approved', approved_by=?, approved_at=? WHERE device_id=?`, req.ApprovedBy, time.Now(), req.DeviceID)
|
||
if err != nil {
|
||
http.Error(w, "更新请求状态失败: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"message": fmt.Sprintf("设备 %s 已审批,授权至 %s", req.DeviceID, req.Expiration),
|
||
})
|
||
}
|
||
|
||
func handleReject(w http.ResponseWriter, req struct{ DeviceID, Expiration, ApprovedBy, Action string }) {
|
||
currentTime := time.Now()
|
||
_, err := database.DB.Exec(`UPDATE pending_requests SET status='rejected', approved_by=?, approved_at=? WHERE device_id=?`, req.ApprovedBy, currentTime, req.DeviceID)
|
||
if err != nil {
|
||
http.Error(w, "拒绝请求失败: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"message": fmt.Sprintf("设备 %s 的请求已被拒绝", req.DeviceID),
|
||
})
|
||
}
|
||
|
||
func SetAutoRenewAllDevices(w http.ResponseWriter, r *http.Request) {
|
||
bodyBytes, err := io.ReadAll(r.Body)
|
||
if err != nil {
|
||
http.Error(w, "Failed to read request body: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 重置 r.Body 供后续使用
|
||
|
||
if !isAdminRequest(w, r) {
|
||
return
|
||
}
|
||
|
||
// 解析请求体中的 "enabled" 字段
|
||
var req struct {
|
||
Enabled string `json:"enabled"`
|
||
}
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
http.Error(w, "Failed to read request body: "+err.Error(), http.StatusInternalServerError)
|
||
//http.Error(w, `{"error":"解析请求失败","code":400}`, http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// 设置全局变量
|
||
config.AutoRenewAllDevices = req.Enabled == "true"
|
||
|
||
// 返回成功响应
|
||
msg := fmt.Sprintf(`{"message":"AutoRenewAllDevices set to %v","code":200}`, req.Enabled)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusOK)
|
||
w.Write([]byte(msg))
|
||
}
|