Files
accredit/main.go
2025-10-18 20:04:03 +08:00

186 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"strings"
"time"
)
// User 用户数据结构
type User struct {
IP string `json:"ip"`
Username string `json:"username"`
ExpiryDate string `json:"expiryDate"`
Phone string `json:"phone"`
}
// UserData 用户数据容器
type UserData struct {
Users []User `json:"users"`
}
// APIResponse API响应结构
type APIResponse struct {
Expiry string `json:"expiry"`
ServerTime string `json:"serverTime"`
}
// getClientIP 获取客户端真实IP地址
func getClientIP(r *http.Request) string {
// 优先检查代理头
if xForwardedFor := r.Header.Get("X-Forwarded-For"); xForwardedFor != "" {
// X-Forwarded-For 可能包含多个IP取第一个
ips := strings.Split(xForwardedFor, ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
}
}
if xRealIP := r.Header.Get("X-Real-IP"); xRealIP != "" {
return xRealIP
}
if xClientIP := r.Header.Get("X-Client-IP"); xClientIP != "" {
return xClientIP
}
// 从RemoteAddr获取IP
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return r.RemoteAddr
}
// 处理IPv6回环地址
if ip == "::1" {
return "127.0.0.1"
}
// 移除IPv6前缀
if strings.HasPrefix(ip, "::ffff:") {
return strings.TrimPrefix(ip, "::ffff:")
}
return ip
}
// loadUserData 加载用户数据
func loadUserData() ([]User, error) {
data, err := os.ReadFile("users.json")
if err != nil {
return nil, err
}
var userData UserData
err = json.Unmarshal(data, &userData)
if err != nil {
return nil, err
}
return userData.Users, nil
}
// findUserByIP 根据IP查找用户
func findUserByIP(users []User, ip string) *User {
for _, user := range users {
if user.IP == ip {
return &user
}
}
return nil
}
// setCORSHeaders 设置CORS头
func setCORSHeaders(w http.ResponseWriter) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
}
// healthHandler 健康检查处理器
func healthHandler(w http.ResponseWriter, r *http.Request) {
setCORSHeaders(w)
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
response := map[string]interface{}{
"status": "ok",
"time": time.Now().Format(time.RFC3339),
}
json.NewEncoder(w).Encode(response)
}
// checkExpiryHandler 检查到期时间处理器
func checkExpiryHandler(w http.ResponseWriter, r *http.Request) {
setCORSHeaders(w)
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
// 获取客户端IP
clientIP := getClientIP(r)
log.Printf("收到来自 %s 的请求", clientIP)
// 获取当前服务器时间
serverTime := time.Now().Format(time.RFC3339)
// 加载用户数据
users, err := loadUserData()
if err != nil {
log.Printf("读取用户数据失败: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{})
return
}
// 查找用户
user := findUserByIP(users, clientIP)
if user == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{})
return
}
// 找到用户,返回到期时间
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(APIResponse{
Expiry: user.ExpiryDate,
ServerTime: serverTime,
})
}
func main() {
// 定义命令行参数
portFlag := flag.String("port", "3763", "服务器端口号")
flag.Parse()
// 设置路由
http.HandleFunc("/health", healthHandler)
http.HandleFunc("/api/check-expiry", checkExpiryHandler)
port := ":" + *portFlag
fmt.Printf("服务器运行在端口 %s\n", port)
fmt.Printf("健康检查: http://localhost%s/health\n", port)
fmt.Printf("API接口: http://localhost%s/api/check-expiry\n", port)
// 启动服务器
log.Fatal(http.ListenAndServe(port, nil))
}