feat(redis): add bulk operations and command runner

This commit is contained in:
t8y2 committed 2026-05-12 13:38:43 +08:00
1 parent f2e8b49df7
commit 368fa5a102
17 files changed
+673 -20

No files matched your search

+4
View File
@@ -114,6 +114,8 @@ export const redisZadd = forward("redisZadd");
export const redisZrem = forward("redisZrem");
export const redisSetTtl = forward("redisSetTtl");
export const redisDeleteKeys = forward("redisDeleteKeys");
export const redisFlushDb = forward("redisFlushDb");
export const redisExecuteCommand = forward("redisExecuteCommand");
export const redisLoadMore = forward("redisLoadMore");
// MongoDB
@@ -152,6 +154,8 @@ export type {
RedisKeyInfo,
RedisValue,
RedisScanResult,
RedisCommandSafety,
RedisCommandResult,
MongoDocumentResult,
HistoryEntry,
SqlFileStatus,
+13
View File
@@ -26,6 +26,7 @@ import type {
UpdateInfo,
RedisValue,
RedisScanResult,
RedisCommandResult,
MongoDocumentResult,
HistoryEntry,
SqlFileRequest,
@@ -584,6 +585,18 @@ export async function redisDeleteKeys(connectionId: string, db: number, keyRaws:
return post("/api/redis/delete-keys", { connectionId, db, keyRaws });
}
export async function redisFlushDb(connectionId: string, db: number): Promise<void> {
return post("/api/redis/flush-db", { connectionId, db });
}
export async function redisExecuteCommand(
connectionId: string,
db: number,
command: string,
): Promise<RedisCommandResult> {
return post("/api/redis/execute-command", { connectionId, db, command });
}
export async function redisLoadMore(
connectionId: string,
db: number,
+82
View File
@@ -0,0 +1,82 @@
export type RedisCommandSafety = "allowed" | "confirm" | "blocked";
const BLOCKED_COMMANDS = new Set([
"KEYS",
"FLUSHALL",
"SHUTDOWN",
"CONFIG",
"SAVE",
"BGSAVE",
"SLAVEOF",
"REPLICAOF",
"MIGRATE",
"MODULE",
"SCRIPT",
"EVAL",
"EVALSHA",
]);
const CONFIRM_COMMANDS = new Set([
"DEL",
"UNLINK",
"EXPIRE",
"EXPIREAT",
"PEXPIRE",
"PEXPIREAT",
"PERSIST",
"RENAME",
"RENAMENX",
"SET",
"SETEX",
"PSETEX",
"SETNX",
"MSET",
"MSETNX",
"HSET",
"HDEL",
"LPUSH",
"RPUSH",
"LPOP",
"RPOP",
"LSET",
"LREM",
"SADD",
"SREM",
"ZADD",
"ZREM",
"XADD",
"XDEL",
"FLUSHDB",
]);
export function firstRedisCommandToken(command: string): string {
const trimmed = command.trimStart();
if (!trimmed) return "";
const quote = trimmed[0] === '"' || trimmed[0] === "'" ? trimmed[0] : "";
let token = "";
let escaping = false;
for (let i = quote ? 1 : 0; i < trimmed.length; i++) {
const ch = trimmed[i];
if (escaping) {
token += ch;
escaping = false;
continue;
}
if (ch === "\\") {
escaping = true;
continue;
}
if (quote && ch === quote) break;
if (!quote && /\s/.test(ch)) break;
token += ch;
}
return token.toUpperCase();
}
export function classifyRedisCommandSafety(command: string): RedisCommandSafety {
const token = firstRedisCommandToken(command);
if (BLOCKED_COMMANDS.has(token)) return "blocked";
if (CONFIRM_COMMANDS.has(token)) return "confirm";
return "allowed";
}
+17
View File
@@ -129,6 +129,23 @@ export function collectExpandedGroupIds(nodes: RedisKeyTreeNode[]): Set<string>
return ids;
}
export function collectRedisGroupKeyRaws(group: RedisKeyTreeGroupNode): string[] {
const keyRaws: string[] = [];
const visit = (nodes: RedisKeyTreeNode[]) => {
for (const node of nodes) {
if (node.kind === "leaf") {
keyRaws.push(node.keyRaw);
} else {
visit(node.children);
}
}
};
visit(group.children);
return keyRaws;
}
export function flattenVisibleRedisKeyTree(
nodes: RedisKeyTreeNode[],
expandedGroupIds: ReadonlySet<string>,
+20
View File
@@ -365,6 +365,14 @@ export interface RedisScanResult {
keys: RedisKeyInfo[];
}
export type RedisCommandSafety = "allowed" | "confirm" | "blocked";
export interface RedisCommandResult {
command: string;
safety: RedisCommandSafety;
value: any;
}
export async function redisListDatabases(connectionId: string): Promise<number[]> {
return invoke("redis_list_databases", { connectionId });
}
@@ -449,6 +457,18 @@ export async function redisDeleteKeys(connectionId: string, db: number, keyRaws:
return invoke("redis_delete_keys", { connectionId, db, keyRaws });
}
export async function redisFlushDb(connectionId: string, db: number): Promise<void> {
return invoke("redis_flush_db", { connectionId, db });
}
export async function redisExecuteCommand(
connectionId: string,
db: number,
command: string,
): Promise<RedisCommandResult> {
return invoke("redis_execute_command", { connectionId, db, command });
}
export async function redisLoadMore(
connectionId: string,
db: number,