feat(chat): IP-keyed daily rate limit (10/day), live reset countdown, web_demo Anthropic key
This commit is contained in:
+27
-7
@@ -1086,16 +1086,36 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
||||
if str_eq(msg, "") {
|
||||
return "{\"error\":\"message required\"}"
|
||||
}
|
||||
// Rate limit: 25 requests per uid per hour (stored in process state)
|
||||
// Rate limit: 10 chats per uid per day (UTC day, keyed by uid).
|
||||
// State key: "__rl_<uid>" → "<count>|<day_number>"
|
||||
// day_number = unix_timestamp / 86400 (integer UTC day)
|
||||
// Returns rate_limited JSON with reset_at (next midnight UTC) so
|
||||
// the frontend can show a real countdown.
|
||||
let rate_uid: String = json_get(body, "uid")
|
||||
if !str_eq(rate_uid, "") {
|
||||
let rate_key: String = "__rate__" + rate_uid
|
||||
let rate_val: String = state_get(rate_key)
|
||||
let rate_count: Int = if str_eq(rate_val, "") { 0 } else { str_to_int(rate_val) }
|
||||
if rate_count >= 25 {
|
||||
return "{\"response\":\"You've hit the rate limit. Come back in an hour.\"}"
|
||||
let now_ts: Int = unix_timestamp()
|
||||
let today_day: Int = now_ts / 86400
|
||||
let next_reset: Int = (today_day + 1) * 86400
|
||||
let rl_key: String = "__rl_" + rate_uid
|
||||
let rl_val: String = state_get(rl_key)
|
||||
let rl_count: Int = 0
|
||||
let rl_day: Int = 0
|
||||
if !str_eq(rl_val, "") {
|
||||
// format: "count|day"
|
||||
let parts: [String] = str_split(rl_val, "|")
|
||||
if native_list_len(parts) >= 2 {
|
||||
let rl_count = str_to_int(native_list_get(parts, 0))
|
||||
let rl_day = str_to_int(native_list_get(parts, 1))
|
||||
}
|
||||
}
|
||||
state_set(rate_key, int_to_str(rate_count + 1))
|
||||
// Reset count if it's a new day
|
||||
if rl_day != today_day {
|
||||
let rl_count = 0
|
||||
}
|
||||
if rl_count >= 10 {
|
||||
return "{\"rate_limited\":true,\"reset_at\":" + int_to_str(next_reset) + "}"
|
||||
}
|
||||
state_set(rl_key, int_to_str(rl_count + 1) + "|" + int_to_str(today_day))
|
||||
}
|
||||
// Turnstile: verify on first message only (tokens are single-use).
|
||||
// Per-message verification breaks chat flow. Forms get full verification.
|
||||
|
||||
Reference in New Issue
Block a user