Files
PeachDeployments/lib/Peach/see/redis/ViewRedis.dart
alvocool 38881d1fe1 Add app source and tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:15:30 +03:00

178 lines
5.0 KiB
Dart

import '../../about/external/data/Redis.dart';
import '../../about/internal/application/ServerProfile.dart';
import '../parent/ParentViewModel.dart';
import 'ConnectRedis.dart';
class ViewRedis extends ParentViewModel {
ConnectRedis connection;
ViewRedis(super.context, this.connection);
/// Pings Redis, then reads the database list, the current DB's size and the
/// keys matching [pattern]. On a NOAUTH bounce with no password entered, it
/// reads `requirepass` out of redis.conf once and retries — the common case
/// where the user has SSH access but hasn't been told the Redis password.
void scan(
ServerProfile server, {
required String host,
required int port,
required String password,
required int database,
required String pattern,
required String typeFilter,
bool triedDetect = false,
}) async {
if (!await canReach(
server,
() => scan(
server,
host: host,
port: port,
password: password,
database: database,
pattern: pattern,
typeFilter: typeFilter,
triedDetect: triedDetect,
),
)) {
return;
}
try {
final (ok, detail) = await getDataManager().redisPing(
server,
host: host,
port: port,
pass: password,
db: database,
);
if (!ok) {
if (!triedDetect &&
detail.toUpperCase().contains('NOAUTH') &&
password.trim().isEmpty) {
final found = await getDataManager().redisConfigPassword(server);
if (found.isNotEmpty) {
connection.onPasswordDetected(found);
return;
}
}
connection.onScanFailed(_hint(detail, host, port));
return;
}
final effectivePattern = pattern.trim().isEmpty ? '*' : pattern.trim();
final (databaseCount, databaseCounts) = await getDataManager()
.redisDatabases(server, host: host, port: port, pass: password);
final total = await getDataManager().redisDbSize(
server,
host: host,
port: port,
pass: password,
db: database,
);
final keys = <String>[];
final keyTypes = <String, String>{};
if (typeFilter == 'all') {
keys.addAll(
await getDataManager().redisKeys(
server,
pattern: effectivePattern,
host: host,
port: port,
pass: password,
db: database,
),
);
} else {
final typed = await getDataManager().redisKeysTyped(
server,
pattern: effectivePattern,
filterType: typeFilter,
host: host,
port: port,
pass: password,
db: database,
);
for (final (key, type) in typed) {
keys.add(key);
keyTypes[key] = type;
}
}
connection.onScanned(
RedisScan(
databaseCount: databaseCount,
databaseCounts: databaseCounts,
total: total,
keys: keys,
keyTypes: keyTypes,
),
);
} catch (error) {
connection.onScanFailed(error.toString());
}
}
/// Reads `requirepass` from the server's redis.conf.
void detectPassword(ServerProfile server) async {
if (!await canReach(server, () => detectPassword(server))) return;
try {
final found = await getDataManager().redisConfigPassword(server);
if (found.isEmpty) {
connection.onPasswordMissing();
} else {
connection.onPasswordDetected(found);
}
} catch (error) {
handleError(error, () => detectPassword(server), () {}, "Retry");
}
}
/// Reads one key. A failure is surfaced as an `error`-typed entry rather
/// than an overlay — the value panel is the natural place to show it.
void loadValue(
ServerProfile server,
String key, {
required String host,
required int port,
required String password,
required int database,
}) async {
try {
final entry = await getDataManager().redisGet(
server,
key,
host: host,
port: port,
pass: password,
db: database,
);
connection.onValueLoaded(entry);
} catch (error) {
connection.onValueLoaded(
RedisEntry(key: key, type: 'error', ttl: '', value: '$error'),
);
}
}
/// Turns a raw redis-cli complaint into something the user can act on.
String _hint(String detail, String host, int port) {
final message = detail.isEmpty ? 'Redis did not respond.' : detail;
if (message.toUpperCase().contains('NOAUTH')) {
return 'Redis requires a password. Enter it below (or tap Detect to read '
'it from redis.conf), then Scan.';
}
if (message.toLowerCase().contains('wrongpass')) {
return 'Wrong Redis password. Fix it and Scan again.';
}
if (message.toLowerCase().contains('connection refused')) {
return 'Nothing is listening on $host:$port. Check the host/port '
'(Redis in Docker often maps a different port).';
}
return message;
}
}