Skip to content
Merged

Dev #15

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_darwin-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"path_provider_android","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.22\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"sqflite_android","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_android-2.4.2+2\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"bitsdojo_window_macos","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_macos-0.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_darwin-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"bitsdojo_window_linux","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_linux-0.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"bitsdojo_window_windows","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_windows-0.1.6\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[]},"dependencyGraph":[{"name":"bitsdojo_window","dependencies":["bitsdojo_window_windows","bitsdojo_window_macos","bitsdojo_window_linux"]},{"name":"bitsdojo_window_linux","dependencies":[]},{"name":"bitsdojo_window_macos","dependencies":[]},{"name":"bitsdojo_window_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"sqflite","dependencies":["sqflite_android","sqflite_darwin"]},{"name":"sqflite_android","dependencies":[]},{"name":"sqflite_darwin","dependencies":[]}],"date_created":"2026-03-07 10:36:50.253535","version":"3.38.5","swift_package_manager_enabled":{"ios":false,"macos":false}}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_darwin-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"path_provider_android","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.22\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"sqflite_android","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_android-2.4.2+2\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"bitsdojo_window_macos","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_macos-0.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_darwin-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"bitsdojo_window_linux","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_linux-0.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"bitsdojo_window_windows","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_windows-0.1.6\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[]},"dependencyGraph":[{"name":"bitsdojo_window","dependencies":["bitsdojo_window_windows","bitsdojo_window_macos","bitsdojo_window_linux"]},{"name":"bitsdojo_window_linux","dependencies":[]},{"name":"bitsdojo_window_macos","dependencies":[]},{"name":"bitsdojo_window_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"sqflite","dependencies":["sqflite_android","sqflite_darwin"]},{"name":"sqflite_android","dependencies":[]},{"name":"sqflite_darwin","dependencies":[]}],"date_created":"2026-03-08 13:00:51.851139","version":"3.38.5","swift_package_manager_enabled":{"ios":false,"macos":false}}
225 changes: 225 additions & 0 deletions lib/core/database/redis_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,231 @@ class RedisConnection {
await disconnect();
}
}

// ─── Data commands ─────────────────────────────────────────────────────

/// Raw command helper.
Future<dynamic> sendCommand(List<dynamic> args) async {
if (!isConnected || _command == null) {
throw StateError('Not connected to Redis');
}
return _command!.send_object(args);
}

/// SELECT database index.
Future<void> selectDatabase(int db) async {
await sendCommand(['SELECT', db]);
}

/// DBSIZE — number of keys in the currently selected database.
Future<int> dbSize() async {
final result = await sendCommand(['DBSIZE']);
return result is int ? result : int.tryParse(result.toString()) ?? 0;
}

/// CONFIG GET databases — max number of databases.
Future<int> getMaxDatabases() async {
try {
final result = await sendCommand(['CONFIG', 'GET', 'databases']);
if (result is List && result.length >= 2) {
return int.tryParse(result[1].toString()) ?? 16;
}
} catch (_) {
// Some Redis instances don't allow CONFIG; fall back.
}
return 16;
}

/// SCAN cursor [MATCH pattern] [COUNT count].
/// Returns (nextCursor, keys).
Future<(int, List<String>)> scan({
int cursor = 0,
String? match,
int count = 100,
}) async {
final args = <dynamic>['SCAN', cursor];
if (match != null && match.isNotEmpty) {
args.addAll(['MATCH', match]);
}
args.addAll(['COUNT', count]);
final result = await sendCommand(args);
if (result is List && result.length == 2) {
final nextCursor = int.tryParse(result[0].toString()) ?? 0;
final keys = (result[1] as List?)
?.map((e) => e.toString())
.toList() ??
[];
return (nextCursor, keys);
}
return (0, <String>[]);
}

/// TYPE key.
Future<String> keyType(String key) async {
final result = await sendCommand(['TYPE', key]);
return result?.toString() ?? 'none';
}

/// TTL key (returns -1 if no expiry, -2 if missing).
Future<int> ttl(String key) async {
final result = await sendCommand(['TTL', key]);
return result is int ? result : int.tryParse(result.toString()) ?? -1;
}

/// GET (string).
Future<String?> get(String key) async {
final result = await sendCommand(['GET', key]);
return result?.toString();
}

/// SET key value [EX seconds].
Future<void> set(String key, String value, {int? ttlSeconds}) async {
if (ttlSeconds != null && ttlSeconds > 0) {
await sendCommand(['SET', key, value, 'EX', ttlSeconds]);
} else {
await sendCommand(['SET', key, value]);
}
}

/// HGETALL key. Returns a `Map<String, String>`.
Future<Map<String, String>> hgetall(String key) async {
final result = await sendCommand(['HGETALL', key]);
final map = <String, String>{};
if (result is List) {
for (var i = 0; i + 1 < result.length; i += 2) {
map[result[i].toString()] = result[i + 1].toString();
}
}
return map;
}

/// HSET key field value.
Future<void> hset(String key, String field, String value) async {
await sendCommand(['HSET', key, field, value]);
}

/// HDEL key field.
Future<void> hdel(String key, String field) async {
await sendCommand(['HDEL', key, field]);
}

/// LRANGE key start stop.
Future<List<String>> lrange(String key, int start, int stop) async {
final result = await sendCommand(['LRANGE', key, start, stop]);
if (result is List) {
return result.map((e) => e.toString()).toList();
}
return [];
}

/// LLEN key.
Future<int> llen(String key) async {
final result = await sendCommand(['LLEN', key]);
return result is int ? result : int.tryParse(result.toString()) ?? 0;
}

/// RPUSH key value.
Future<void> rpush(String key, String value) async {
await sendCommand(['RPUSH', key, value]);
}

/// SMEMBERS key.
Future<List<String>> smembers(String key) async {
final result = await sendCommand(['SMEMBERS', key]);
if (result is List) {
return result.map((e) => e.toString()).toList();
}
return [];
}

/// SCARD key.
Future<int> scard(String key) async {
final result = await sendCommand(['SCARD', key]);
return result is int ? result : int.tryParse(result.toString()) ?? 0;
}

/// SADD key member.
Future<void> sadd(String key, String member) async {
await sendCommand(['SADD', key, member]);
}

/// SREM key member.
Future<void> srem(String key, String member) async {
await sendCommand(['SREM', key, member]);
}

/// ZRANGE key start stop WITHSCORES → list of (member, score).
Future<List<(String, double)>> zrangeWithScores(
String key, int start, int stop) async {
final result =
await sendCommand(['ZRANGE', key, start, stop, 'WITHSCORES']);
final list = <(String, double)>[];
if (result is List) {
for (var i = 0; i + 1 < result.length; i += 2) {
final member = result[i].toString();
final score = double.tryParse(result[i + 1].toString()) ?? 0;
list.add((member, score));
}
}
return list;
}

/// ZCARD key.
Future<int> zcard(String key) async {
final result = await sendCommand(['ZCARD', key]);
return result is int ? result : int.tryParse(result.toString()) ?? 0;
}

/// ZADD key score member.
Future<void> zadd(String key, double score, String member) async {
await sendCommand(['ZADD', key, score, member]);
}

/// ZREM key member.
Future<void> zrem(String key, String member) async {
await sendCommand(['ZREM', key, member]);
}

/// DEL key.
Future<int> del(String key) async {
final result = await sendCommand(['DEL', key]);
return result is int ? result : int.tryParse(result.toString()) ?? 0;
}

/// RENAME old new.
Future<void> rename(String oldKey, String newKey) async {
await sendCommand(['RENAME', oldKey, newKey]);
}

/// EXPIRE key seconds.
Future<void> expire(String key, int seconds) async {
await sendCommand(['EXPIRE', key, seconds]);
}

/// PERSIST key (remove TTL).
Future<void> persist(String key) async {
await sendCommand(['PERSIST', key]);
}

/// STRLEN / LLEN / SCARD / ZCARD / HLEN — get size for any type.
Future<int> keySize(String key, String type) async {
switch (type) {
case 'string':
final r = await sendCommand(['STRLEN', key]);
return r is int ? r : int.tryParse(r.toString()) ?? 0;
case 'list':
return llen(key);
case 'set':
return scard(key);
case 'zset':
return zcard(key);
case 'hash':
final r = await sendCommand(['HLEN', key]);
return r is int ? r : int.tryParse(r.toString()) ?? 0;
default:
return 0;
}
}
}

class RedisConnectionException implements Exception {
Expand Down
Loading