8#include <ascii-chat/network/rate_limit/sqlite.h>
9#include <ascii-chat/log/logging.h>
10#include <ascii-chat/util/time.h>
21static asciichat_error_t sqlite_check(
void *backend_data,
const char *ip_address, rate_event_type_t event_type,
22 const rate_limit_config_t *config,
bool *allowed) {
30 uint64_t window_start_ns = now_ns - ((uint64_t)limit->window_secs * NS_PER_SEC_INT);
33 const char *sql =
"SELECT COUNT(*) FROM rate_events "
34 "WHERE ip_address = ? AND event_type = ? AND timestamp >= ?";
36 sqlite3_stmt *stmt = NULL;
37 int rc = sqlite3_prepare_v2(backend->
db, sql, -1, &stmt, NULL);
38 if (rc != SQLITE_OK) {
39 return SET_ERRNO(ERROR_CONFIG,
"Failed to prepare rate limit query: %s", sqlite3_errmsg(backend->
db));
42 sqlite3_bind_text(stmt, 1, ip_address, -1, SQLITE_STATIC);
44 sqlite3_bind_int64(stmt, 3, (sqlite3_int64)window_start_ns);
46 rc = sqlite3_step(stmt);
47 if (rc != SQLITE_ROW) {
48 sqlite3_finalize(stmt);
49 return SET_ERRNO(ERROR_CONFIG,
"Failed to execute rate limit query: %s", sqlite3_errmsg(backend->
db));
52 uint32_t event_count = (uint32_t)sqlite3_column_int(stmt, 0);
53 sqlite3_finalize(stmt);
56 *allowed = (event_count < limit->max_events);
59 log_warn(
"Rate limit exceeded for %s (event: %s, count: %u/%u)", ip_address,
66static asciichat_error_t sqlite_record(
void *backend_data,
const char *ip_address, rate_event_type_t event_type) {
73 const char *sql =
"INSERT INTO rate_events (ip_address, event_type, timestamp) VALUES (?, ?, ?)";
75 sqlite3_stmt *stmt = NULL;
76 int rc = sqlite3_prepare_v2(backend->
db, sql, -1, &stmt, NULL);
77 if (rc != SQLITE_OK) {
78 return SET_ERRNO(ERROR_CONFIG,
"Failed to prepare rate limit insert: %s", sqlite3_errmsg(backend->
db));
81 sqlite3_bind_text(stmt, 1, ip_address, -1, SQLITE_STATIC);
83 sqlite3_bind_int64(stmt, 3, (sqlite3_int64)now_ns);
85 rc = sqlite3_step(stmt);
86 sqlite3_finalize(stmt);
88 if (rc != SQLITE_DONE) {
89 return SET_ERRNO(ERROR_CONFIG,
"Failed to record rate event: %s", sqlite3_errmsg(backend->
db));
96static asciichat_error_t sqlite_cleanup(
void *backend_data, uint32_t max_age_secs) {
100 if (max_age_secs == 0) {
101 max_age_secs = SEC_PER_HOUR;
106 uint64_t cutoff_ns = now_ns - ((uint64_t)max_age_secs * NS_PER_SEC_INT);
109 const char *sql =
"DELETE FROM rate_events WHERE timestamp < ?";
111 sqlite3_stmt *stmt = NULL;
112 int rc = sqlite3_prepare_v2(backend->
db, sql, -1, &stmt, NULL);
113 if (rc != SQLITE_OK) {
114 return SET_ERRNO(ERROR_CONFIG,
"Failed to prepare rate limit cleanup: %s", sqlite3_errmsg(backend->
db));
117 sqlite3_bind_int64(stmt, 1, (sqlite3_int64)cutoff_ns);
119 rc = sqlite3_step(stmt);
120 int changes = sqlite3_changes(backend->
db);
121 sqlite3_finalize(stmt);
123 if (rc != SQLITE_DONE) {
124 return SET_ERRNO(ERROR_CONFIG,
"Failed to cleanup rate events: %s", sqlite3_errmsg(backend->
db));
128 log_debug(
"Cleaned up %d old rate events", changes);
134static void sqlite_destroy(
void *backend_data) {
150 log_error(
"Failed to allocate SQLite backend");
154 memset(backend, 0,
sizeof(*backend));
158 log_debug(
"SQLite rate limiter backend allocated (database will be set externally)");
175 .check = sqlite_check,
176 .record = sqlite_record,
177 .cleanup = sqlite_cleanup,
178 .destroy = sqlite_destroy,
const char * rate_limiter_event_type_string(rate_event_type_t event_type)
Get event type string for logging.
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
void sqlite_backend_set_db(void *backend_data, sqlite3 *db)
Set SQLite database handle for backend.
void * sqlite_backend_create(const char *db_path)
const rate_limiter_backend_ops_t sqlite_backend_ops
sqlite3 * db
SQLite database handle.
uint64_t time_get_realtime_ns(void)