Spaces:
Paused
Paused
fix undefined TrackUsage and cleanup keys.go
Browse files- internal/handler/anthropic.go +23 -21
- internal/handler/chat.go +23 -21
- internal/handler/index.go +8 -8
- internal/handler/keys.go +28 -61
internal/handler/anthropic.go
CHANGED
|
@@ -19,28 +19,30 @@ import (
|
|
| 19 |
|
| 20 |
// HandleMessages handles Anthropic Messages API requests (/v1/messages)
|
| 21 |
func HandleMessages(w http.ResponseWriter, r *http.Request) {
|
| 22 |
-
apiKey := r.Header.Get("x-api-key")
|
| 23 |
-
if apiKey == "" {
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
| 34 |
defer func() { TrackUsage(apiKey, 150) }()
|
| 35 |
-
if token == "free" || strings.HasPrefix(token, "RWPX-") {
|
| 36 |
-
anonymousToken, err := auth.GetAnonymousToken()
|
| 37 |
-
if err != nil {
|
| 38 |
-
logger.LogError("Failed to get anonymous token: %v", err)
|
| 39 |
-
writeAnthropicError(w, http.StatusInternalServerError, "api_error", "Failed to get anonymous token")
|
| 40 |
-
return
|
| 41 |
-
}
|
| 42 |
-
token = anonymousToken
|
| 43 |
-
}
|
| 44 |
var req model.AnthropicRequest
|
| 45 |
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
| 46 |
writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", "Invalid request body")
|
|
|
|
| 19 |
|
| 20 |
// HandleMessages handles Anthropic Messages API requests (/v1/messages)
|
| 21 |
func HandleMessages(w http.ResponseWriter, r *http.Request) {
|
| 22 |
+
apiKey := r.Header.Get("x-api-key")
|
| 23 |
+
if apiKey == "" {
|
| 24 |
+
apiKey = strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
| 25 |
+
}
|
| 26 |
+
if apiKey == "" {
|
| 27 |
+
writeAnthropicError(w, http.StatusUnauthorized, "authentication_error", "Missing API key")
|
| 28 |
+
return
|
| 29 |
+
}
|
| 30 |
+
ok, reason := CheckAndTrack(apiKey, 0)
|
| 31 |
+
if !ok {
|
| 32 |
+
writeAnthropicError(w, http.StatusTooManyRequests, "rate_limit_error", reason)
|
| 33 |
+
return
|
| 34 |
+
}
|
| 35 |
+
token := apiKey
|
| 36 |
defer func() { TrackUsage(apiKey, 150) }()
|
| 37 |
+
if token == "free" || strings.HasPrefix(token, "RWPX-") {
|
| 38 |
+
anonymousToken, err := auth.GetAnonymousToken()
|
| 39 |
+
if err != nil {
|
| 40 |
+
logger.LogError("Failed to get anonymous token: %v", err)
|
| 41 |
+
writeAnthropicError(w, http.StatusInternalServerError, "api_error", "Failed to get anonymous token")
|
| 42 |
+
return
|
| 43 |
+
}
|
| 44 |
+
token = anonymousToken
|
| 45 |
+
}
|
| 46 |
var req model.AnthropicRequest
|
| 47 |
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
| 48 |
writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", "Invalid request body")
|
internal/handler/chat.go
CHANGED
|
@@ -19,29 +19,31 @@ import (
|
|
| 19 |
)
|
| 20 |
|
| 21 |
func HandleChatCompletions(w http.ResponseWriter, r *http.Request) {
|
| 22 |
-
apiKey := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
| 23 |
-
if apiKey == "" {
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
| 34 |
// Estimación simple de tokens (4 chars = 1 token)
|
| 35 |
defer func() { TrackUsage(apiKey, 100) }()
|
| 36 |
-
if token == "free" || strings.HasPrefix(token, "RWPX-") {
|
| 37 |
-
anonymousToken, err := auth.GetAnonymousToken()
|
| 38 |
-
if err != nil {
|
| 39 |
-
logger.LogError("Failed to get anonymous token: %v", err)
|
| 40 |
-
http.Error(w, "Failed to get anonymous token", http.StatusInternalServerError)
|
| 41 |
-
return
|
| 42 |
-
}
|
| 43 |
-
token = anonymousToken
|
| 44 |
-
}
|
| 45 |
var req model.ChatRequest
|
| 46 |
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
| 47 |
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
|
|
| 19 |
)
|
| 20 |
|
| 21 |
func HandleChatCompletions(w http.ResponseWriter, r *http.Request) {
|
| 22 |
+
apiKey := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
| 23 |
+
if apiKey == "" {
|
| 24 |
+
apiKey = r.Header.Get("x-api-key")
|
| 25 |
+
}
|
| 26 |
+
if apiKey == "" {
|
| 27 |
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
| 28 |
+
return
|
| 29 |
+
}
|
| 30 |
+
ok, reason := CheckAndTrack(apiKey, 0)
|
| 31 |
+
if !ok {
|
| 32 |
+
http.Error(w, reason, http.StatusTooManyRequests)
|
| 33 |
+
return
|
| 34 |
+
}
|
| 35 |
+
token := apiKey
|
| 36 |
// Estimación simple de tokens (4 chars = 1 token)
|
| 37 |
defer func() { TrackUsage(apiKey, 100) }()
|
| 38 |
+
if token == "free" || strings.HasPrefix(token, "RWPX-") {
|
| 39 |
+
anonymousToken, err := auth.GetAnonymousToken()
|
| 40 |
+
if err != nil {
|
| 41 |
+
logger.LogError("Failed to get anonymous token: %v", err)
|
| 42 |
+
http.Error(w, "Failed to get anonymous token", http.StatusInternalServerError)
|
| 43 |
+
return
|
| 44 |
+
}
|
| 45 |
+
token = anonymousToken
|
| 46 |
+
}
|
| 47 |
var req model.ChatRequest
|
| 48 |
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
| 49 |
http.Error(w, "Invalid request", http.StatusBadRequest)
|
internal/handler/index.go
CHANGED
|
@@ -1,18 +1,18 @@
|
|
| 1 |
package handler
|
| 2 |
|
| 3 |
import (
|
| 4 |
-
_ "embed"
|
| 5 |
-
"net/http"
|
| 6 |
)
|
| 7 |
|
| 8 |
//go:embed index.html
|
| 9 |
var indexHTML []byte
|
| 10 |
|
| 11 |
func HandleIndex(w http.ResponseWriter, r *http.Request) {
|
| 12 |
-
if r.URL.Path != "/" {
|
| 13 |
-
http.NotFound(w, r)
|
| 14 |
-
return
|
| 15 |
-
}
|
| 16 |
-
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
| 17 |
-
w.Write(indexHTML)
|
| 18 |
}
|
|
|
|
| 1 |
package handler
|
| 2 |
|
| 3 |
import (
|
| 4 |
+
_ "embed"
|
| 5 |
+
"net/http"
|
| 6 |
)
|
| 7 |
|
| 8 |
//go:embed index.html
|
| 9 |
var indexHTML []byte
|
| 10 |
|
| 11 |
func HandleIndex(w http.ResponseWriter, r *http.Request) {
|
| 12 |
+
if r.URL.Path != "/" {
|
| 13 |
+
http.NotFound(w, r)
|
| 14 |
+
return
|
| 15 |
+
}
|
| 16 |
+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
| 17 |
+
w.Write(indexHTML)
|
| 18 |
}
|
internal/handler/keys.go
CHANGED
|
@@ -10,28 +10,20 @@ import (
|
|
| 10 |
)
|
| 11 |
|
| 12 |
type KeyData struct {
|
| 13 |
-
Name
|
| 14 |
-
Key
|
| 15 |
-
Requests
|
| 16 |
-
Tokens
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
Created time.Time `json:"created"`
|
| 20 |
-
LastUsed time.Time `json:"last_used"`
|
| 21 |
-
IsAdmin bool `json:"is_admin"`
|
| 22 |
}
|
| 23 |
-
|
| 24 |
type KeyStore struct {
|
| 25 |
-
mu
|
| 26 |
-
Keys
|
| 27 |
-
FreeReqs int `json:"free_reqs"`
|
| 28 |
-
FreeReset time.Time `json:"free_reset"`
|
| 29 |
}
|
| 30 |
|
| 31 |
var store = &KeyStore{Keys: make(map[string]*KeyData)}
|
| 32 |
var storePath = "/data/keys.json"
|
| 33 |
-
const freeDailyLimit = 340
|
| 34 |
-
const keyDailyLimit = 9999
|
| 35 |
|
| 36 |
func init() {
|
| 37 |
os.MkdirAll("/data", 0755)
|
|
@@ -43,44 +35,34 @@ func init() {
|
|
| 43 |
store.Keys = make(map[string]*KeyData)
|
| 44 |
}
|
| 45 |
}
|
| 46 |
-
|
| 47 |
func saveStore() {
|
| 48 |
data, _ := json.MarshalIndent(store, "", " ")
|
| 49 |
os.WriteFile(storePath, data, 0644)
|
| 50 |
}
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
*count = 0
|
| 55 |
-
*t = time.Now()
|
| 56 |
}
|
| 57 |
-
}
|
| 58 |
-
|
| 59 |
-
func CheckAndTrack(key string, tokens int) (bool, string) {
|
| 60 |
store.mu.Lock()
|
| 61 |
defer store.mu.Unlock()
|
| 62 |
-
if
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
}
|
| 67 |
-
store.FreeReqs++
|
| 68 |
saveStore()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
return true, ""
|
| 70 |
}
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
| 74 |
}
|
| 75 |
-
|
| 76 |
-
kd.Requests++
|
| 77 |
-
kd.Tokens += tokens
|
| 78 |
-
kd.DailyReqs++
|
| 79 |
-
kd.LastUsed = time.Now()
|
| 80 |
-
saveStore()
|
| 81 |
-
return true, ""
|
| 82 |
}
|
| 83 |
-
|
| 84 |
func GenerateKey(name string) string {
|
| 85 |
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
| 86 |
b := make([]byte, 8)
|
|
@@ -89,36 +71,21 @@ func GenerateKey(name string) string {
|
|
| 89 |
}
|
| 90 |
key := "RWPX-" + string(b)
|
| 91 |
store.mu.Lock()
|
| 92 |
-
store.Keys[key] = &KeyData{
|
| 93 |
-
Name: name,
|
| 94 |
-
Key: key,
|
| 95 |
-
Created: time.Now(),
|
| 96 |
-
LastReset: time.Now(),
|
| 97 |
-
}
|
| 98 |
saveStore()
|
| 99 |
store.mu.Unlock()
|
| 100 |
return key
|
| 101 |
}
|
| 102 |
-
|
| 103 |
func HandleGenKey(w http.ResponseWriter, r *http.Request) {
|
| 104 |
-
if r.Method != "POST" {
|
| 105 |
-
http.Error(w, "Method not allowed", 405)
|
| 106 |
-
return
|
| 107 |
-
}
|
| 108 |
name := r.FormValue("name")
|
| 109 |
if name == "" {
|
| 110 |
name = "usuario"
|
| 111 |
}
|
| 112 |
key := GenerateKey(name)
|
| 113 |
-
|
| 114 |
-
resp := map[string]string{"key": key, "name": name}
|
| 115 |
-
json.NewEncoder(w).Encode(resp)
|
| 116 |
}
|
| 117 |
-
|
| 118 |
func HandleStats(w http.ResponseWriter, r *http.Request) {
|
| 119 |
store.mu.Lock()
|
| 120 |
defer store.mu.Unlock()
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
w.Write(data)
|
| 124 |
-
}
|
|
|
|
| 10 |
)
|
| 11 |
|
| 12 |
type KeyData struct {
|
| 13 |
+
Name string `json:"name"`
|
| 14 |
+
Key string `json:"key"`
|
| 15 |
+
Requests int `json:"requests"`
|
| 16 |
+
Tokens int `json:"tokens"`
|
| 17 |
+
Created time.Time `json:"created"`
|
| 18 |
+
LastUsed time.Time `json:"last_used"`
|
|
|
|
|
|
|
|
|
|
| 19 |
}
|
|
|
|
| 20 |
type KeyStore struct {
|
| 21 |
+
mu sync.Mutex
|
| 22 |
+
Keys map[string]*KeyData `json:"keys"`
|
|
|
|
|
|
|
| 23 |
}
|
| 24 |
|
| 25 |
var store = &KeyStore{Keys: make(map[string]*KeyData)}
|
| 26 |
var storePath = "/data/keys.json"
|
|
|
|
|
|
|
| 27 |
|
| 28 |
func init() {
|
| 29 |
os.MkdirAll("/data", 0755)
|
|
|
|
| 35 |
store.Keys = make(map[string]*KeyData)
|
| 36 |
}
|
| 37 |
}
|
|
|
|
| 38 |
func saveStore() {
|
| 39 |
data, _ := json.MarshalIndent(store, "", " ")
|
| 40 |
os.WriteFile(storePath, data, 0644)
|
| 41 |
}
|
| 42 |
+
func TrackUsage(key string, tokens int) {
|
| 43 |
+
if key == "free" {
|
| 44 |
+
return
|
|
|
|
|
|
|
| 45 |
}
|
|
|
|
|
|
|
|
|
|
| 46 |
store.mu.Lock()
|
| 47 |
defer store.mu.Unlock()
|
| 48 |
+
if kd, ok := store.Keys[key]; ok {
|
| 49 |
+
kd.Requests++
|
| 50 |
+
kd.Tokens += tokens
|
| 51 |
+
kd.LastUsed = time.Now()
|
|
|
|
|
|
|
| 52 |
saveStore()
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
func CheckAndTrack(key string, tokens int) (bool, string) {
|
| 56 |
+
if key == "free" {
|
| 57 |
return true, ""
|
| 58 |
}
|
| 59 |
+
store.mu.Lock()
|
| 60 |
+
defer store.mu.Unlock()
|
| 61 |
+
if _, ok := store.Keys[key]; ok {
|
| 62 |
+
return true, ""
|
| 63 |
}
|
| 64 |
+
return false, "Key invalida"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
}
|
|
|
|
| 66 |
func GenerateKey(name string) string {
|
| 67 |
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
| 68 |
b := make([]byte, 8)
|
|
|
|
| 71 |
}
|
| 72 |
key := "RWPX-" + string(b)
|
| 73 |
store.mu.Lock()
|
| 74 |
+
store.Keys[key] = &KeyData{Name: name, Key: key, Created: time.Now()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
saveStore()
|
| 76 |
store.mu.Unlock()
|
| 77 |
return key
|
| 78 |
}
|
|
|
|
| 79 |
func HandleGenKey(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
name := r.FormValue("name")
|
| 81 |
if name == "" {
|
| 82 |
name = "usuario"
|
| 83 |
}
|
| 84 |
key := GenerateKey(name)
|
| 85 |
+
json.NewEncoder(w).Encode(map[string]string{"key": key, "name": name})
|
|
|
|
|
|
|
| 86 |
}
|
|
|
|
| 87 |
func HandleStats(w http.ResponseWriter, r *http.Request) {
|
| 88 |
store.mu.Lock()
|
| 89 |
defer store.mu.Unlock()
|
| 90 |
+
json.NewEncoder(w).Encode(store)
|
| 91 |
+
}
|
|
|
|
|
|