feat(redis): add bulk operations and command runner
This commit is contained in:
1 parent
f2e8b49df7
commit
368fa5a102
17 files changed
+673
-20
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user