2023-11-25 03:39:35 +00:00
|
|
|
#include "config.h"
|
2023-11-22 08:39:24 +00:00
|
|
|
#include "hiredis_wrapper.h"
|
|
|
|
|
2023-11-25 03:39:35 +00:00
|
|
|
std::optional<Redis> redis;
|
|
|
|
|
|
|
|
|
2023-11-22 08:39:24 +00:00
|
|
|
Redis::Redis(const std::string& address, int port) {
|
|
|
|
this->_context = redisConnect(address.c_str(), port);
|
|
|
|
if (!this->_context) {
|
|
|
|
throw std::bad_alloc();
|
|
|
|
}
|
|
|
|
if (this->_context->err) {
|
|
|
|
RedisException e(this->_context->errstr);
|
|
|
|
redisFree(this->_context);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Redis::Redis(const std::string& unix) {
|
|
|
|
this->_context = redisConnectUnix(unix.c_str());
|
|
|
|
if (!this->_context) {
|
|
|
|
throw std::bad_alloc();
|
|
|
|
}
|
|
|
|
if (this->_context->err) {
|
|
|
|
RedisException e(this->_context->errstr);
|
|
|
|
redisFree(this->_context);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Redis::~Redis() {
|
|
|
|
redisFree(this->_context);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Redis::auth(const std::string& username, const std::string& password) {
|
|
|
|
RedisReply reply = this->command("AUTH %s %s", username.c_str(), password.c_str());
|
|
|
|
if (reply->type == REDIS_REPLY_STATUS) {
|
|
|
|
// good :D
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("AUTH gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Redis::auth(const std::string& password) {
|
|
|
|
RedisReply reply = this->command("AUTH %s", password.c_str());
|
|
|
|
if (reply->type == REDIS_REPLY_STATUS) {
|
|
|
|
// good :D
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("AUTH gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
time_t Redis::ttl(const std::string& key) {
|
|
|
|
RedisReply reply = this->command("TTL %s", key.c_str());
|
|
|
|
if (reply->type == REDIS_REPLY_INTEGER) {
|
|
|
|
return reply->integer;
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("TTL gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Redis::expire(const std::string& key, time_t expiry) {
|
|
|
|
RedisReply reply = this->command("EXPIRE %s %d", key.c_str(), expiry);
|
|
|
|
|
|
|
|
if (reply->type == REDIS_REPLY_INTEGER) {
|
|
|
|
return reply->integer == 1;
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("EXPIRE gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Redis::expire_nx(const std::string& key, time_t expiry) {
|
|
|
|
if (this->_fake_expire_nx) {
|
|
|
|
time_t current_expiry = this->ttl(key);
|
|
|
|
if (current_expiry < 0) {
|
|
|
|
return this->expire(key, expiry);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RedisReply reply(nullptr, freeReplyObject);
|
|
|
|
try{
|
|
|
|
reply = this->command("EXPIRE %s %d NX", key.c_str(), expiry);
|
|
|
|
} catch (const RedisException& e) {
|
|
|
|
if (e.error == "ERR wrong number of arguments for 'expire' command") {
|
|
|
|
this->_fake_expire_nx = true;
|
|
|
|
return this->expire_nx(key, expiry);
|
|
|
|
}
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply->type == REDIS_REPLY_INTEGER) {
|
|
|
|
return reply->integer == 1;
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("EXPIRE NX gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> Redis::get(const std::string& key) {
|
|
|
|
RedisReply reply = this->command("GET %s", key.c_str());
|
|
|
|
if (reply->type == REDIS_REPLY_STRING) {
|
|
|
|
return std::string(reply->str, reply->len);
|
|
|
|
} else if (reply->type == REDIS_REPLY_NIL) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("GET gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Redis::set(const std::string& key, const std::string& value, time_t expiry) {
|
|
|
|
RedisReply reply = this->command("SET %s %s EX %d", key.c_str(), value.c_str(), expiry);
|
|
|
|
if (reply->type == REDIS_REPLY_STATUS) {
|
|
|
|
// good :D
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("SET gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> Redis::hget(const std::string& key, const std::string& field) {
|
|
|
|
RedisReply reply = this->command("HGET %s %s", key.c_str(), field.c_str());
|
|
|
|
if (reply->type == REDIS_REPLY_STRING) {
|
|
|
|
return std::string(reply->str, reply->len);
|
|
|
|
} else if (reply->type == REDIS_REPLY_NIL) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("HGET gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Redis::hset(const std::string& key, const std::string& field, const std::string& value) {
|
|
|
|
RedisReply reply = this->command("HSET %s %s %s", key.c_str(), field.c_str(), value.c_str());
|
|
|
|
if (reply->type == REDIS_REPLY_INTEGER) {
|
|
|
|
// good :D
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("SET gave an unexpected return type");
|
|
|
|
}
|
|
|
|
}
|
2023-11-25 03:39:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
void init_redis() {
|
|
|
|
if (!config.redis_config) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const IPConnection* ip = std::get_if<IPConnection>(&config.redis_config->connection_method)) {
|
|
|
|
redis.emplace(ip->address, ip->port);
|
|
|
|
} else if (const UnixConnection* unix = std::get_if<UnixConnection>(&config.redis_config->connection_method)) {
|
|
|
|
redis.emplace(unix->unix);
|
|
|
|
} else {
|
|
|
|
__builtin_unreachable();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config.redis_config->username && config.redis_config->password) {
|
|
|
|
redis->auth(*config.redis_config->username, *config.redis_config->password);
|
|
|
|
} else if (config.redis_config->password) {
|
|
|
|
redis->auth(*config.redis_config->password);
|
|
|
|
}
|
|
|
|
}
|