fix: use out/ directory for electron-vite dev mode compatibility
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
out/
|
out/
|
||||||
dist/
|
dist/
|
||||||
electron-dist/
|
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
|
|||||||
@@ -0,0 +1,591 @@
|
|||||||
|
import { app, screen, safeStorage, ipcMain, BrowserWindow, nativeTheme, dialog, nativeImage } from "electron";
|
||||||
|
import { join } from "path";
|
||||||
|
import { readFileSync, writeFile } from "fs";
|
||||||
|
import Store from "electron-store";
|
||||||
|
import Redis from "ioredis";
|
||||||
|
import __cjs_mod__ from "node:module";
|
||||||
|
const __filename = import.meta.filename;
|
||||||
|
const __dirname = import.meta.dirname;
|
||||||
|
const require2 = __cjs_mod__.createRequire(import.meta.url);
|
||||||
|
const STATE_FILE = join(app.getPath("userData"), "jrdm-win-state.json");
|
||||||
|
const MIN_WIDTH = 900;
|
||||||
|
const MIN_HEIGHT = 600;
|
||||||
|
const DEFAULT_WIDTH = 1200;
|
||||||
|
const DEFAULT_HEIGHT = 800;
|
||||||
|
function parseJson(str) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(str);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getLastState() {
|
||||||
|
try {
|
||||||
|
const raw = readFileSync(STATE_FILE, "utf-8");
|
||||||
|
const state = parseJson(raw);
|
||||||
|
if (state) {
|
||||||
|
const display = screen.getPrimaryDisplay().workArea;
|
||||||
|
if (state.x != null && (state.x < 0 || state.x > display.width - 100)) {
|
||||||
|
state.x = null;
|
||||||
|
state.y = null;
|
||||||
|
}
|
||||||
|
if (state.y != null && (state.y < 0 || state.y > display.height - 100)) {
|
||||||
|
state.x = null;
|
||||||
|
state.y = null;
|
||||||
|
}
|
||||||
|
state.width = Math.max(state.width || DEFAULT_WIDTH, MIN_WIDTH);
|
||||||
|
state.height = Math.max(state.height || DEFAULT_HEIGHT, MIN_HEIGHT);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
return { x: null, y: null, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, maximized: false };
|
||||||
|
}
|
||||||
|
function saveState(win) {
|
||||||
|
const bounds = win.getBounds();
|
||||||
|
const state = { ...bounds, maximized: win.isMaximized() };
|
||||||
|
writeFile(STATE_FILE, JSON.stringify(state), "utf-8", () => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function restoreAndTrack(win) {
|
||||||
|
const lastState = getLastState();
|
||||||
|
if (lastState.x != null && lastState.y != null) {
|
||||||
|
win.setBounds({ x: lastState.x, y: lastState.y, width: lastState.width, height: lastState.height });
|
||||||
|
}
|
||||||
|
if (lastState.maximized) {
|
||||||
|
win.maximize();
|
||||||
|
}
|
||||||
|
win.on("close", () => saveState(win));
|
||||||
|
}
|
||||||
|
const store = new Store({
|
||||||
|
name: "jrdm-config",
|
||||||
|
defaults: {
|
||||||
|
connections: [],
|
||||||
|
settings: {
|
||||||
|
theme: "system",
|
||||||
|
locale: "en",
|
||||||
|
fontSize: 13,
|
||||||
|
scanCount: 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function encrypt(text) {
|
||||||
|
if (!safeStorage.isEncryptionAvailable()) {
|
||||||
|
return Buffer.from(text).toString("base64");
|
||||||
|
}
|
||||||
|
return safeStorage.encryptString(text).toString("base64");
|
||||||
|
}
|
||||||
|
function decrypt(encoded) {
|
||||||
|
if (!safeStorage.isEncryptionAvailable()) {
|
||||||
|
return Buffer.from(encoded, "base64").toString("utf-8");
|
||||||
|
}
|
||||||
|
return safeStorage.decryptString(Buffer.from(encoded, "base64"));
|
||||||
|
}
|
||||||
|
const clients = /* @__PURE__ */ new Map();
|
||||||
|
function buildRedisOptions(opts) {
|
||||||
|
const options = {
|
||||||
|
host: opts.host,
|
||||||
|
port: opts.port,
|
||||||
|
username: opts.username || void 0,
|
||||||
|
password: opts.password || void 0,
|
||||||
|
stringNumbers: true,
|
||||||
|
enableReadyCheck: true,
|
||||||
|
family: 0,
|
||||||
|
connectTimeout: 1e4,
|
||||||
|
retryStrategy(times) {
|
||||||
|
if (times > 3) return null;
|
||||||
|
return Math.min(times * 200, 1e3);
|
||||||
|
},
|
||||||
|
maxRetriesPerRequest: 3
|
||||||
|
};
|
||||||
|
if (opts.tls) {
|
||||||
|
options.tls = {
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
checkServerIdentity: () => void 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
function getClient(id) {
|
||||||
|
return clients.get(id);
|
||||||
|
}
|
||||||
|
function isClientConnected(id) {
|
||||||
|
const client = clients.get(id);
|
||||||
|
return client?.status === "ready";
|
||||||
|
}
|
||||||
|
async function connect(id, opts) {
|
||||||
|
if (clients.has(id)) {
|
||||||
|
await disconnect(id);
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const redis = new Redis(buildRedisOptions(opts));
|
||||||
|
redis.once("ready", () => {
|
||||||
|
clients.set(id, redis);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
redis.once("error", (err) => {
|
||||||
|
redis.disconnect();
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!clients.has(id)) {
|
||||||
|
redis.disconnect();
|
||||||
|
reject(new Error("连接超时,请检查主机和端口"));
|
||||||
|
}
|
||||||
|
}, 12e3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function disconnect(id) {
|
||||||
|
const client = clients.get(id);
|
||||||
|
if (client) {
|
||||||
|
client.disconnect();
|
||||||
|
clients.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function getKeyType(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.type(key);
|
||||||
|
}
|
||||||
|
async function getKeyTTL(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.ttl(key);
|
||||||
|
}
|
||||||
|
async function deleteKey(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.del(key);
|
||||||
|
}
|
||||||
|
async function setKeyTTL(id, key, ttl) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
if (ttl < 0) {
|
||||||
|
await client.persist(key);
|
||||||
|
} else {
|
||||||
|
await client.expire(key, ttl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function renameKey(id, oldKey, newKey) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.rename(oldKey, newKey);
|
||||||
|
}
|
||||||
|
async function existsKey(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return await client.exists(key) === 1;
|
||||||
|
}
|
||||||
|
async function batchDelete(id, keys) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
if (keys.length === 0) return 0;
|
||||||
|
const pipeline = client.pipeline();
|
||||||
|
for (const key of keys) {
|
||||||
|
pipeline.del(key);
|
||||||
|
}
|
||||||
|
const results = await pipeline.exec();
|
||||||
|
return results?.filter(([err]) => !err).length ?? 0;
|
||||||
|
}
|
||||||
|
async function memoryUsage(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.memory("USAGE", key);
|
||||||
|
}
|
||||||
|
async function scanKeys(id, cursor, pattern, count) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
const [nextCursor, keys] = await client.scan(cursor, "MATCH", pattern, "COUNT", count);
|
||||||
|
return { cursor: nextCursor, keys };
|
||||||
|
}
|
||||||
|
async function getStringValue(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.get(key);
|
||||||
|
}
|
||||||
|
async function setStringValue(id, key, value) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.set(key, value);
|
||||||
|
}
|
||||||
|
async function getHashFields(id, key, cursor, count) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
const [nextCursor, raw] = await client.hscan(key, cursor, "COUNT", count);
|
||||||
|
const fields = [];
|
||||||
|
for (let i = 0; i < raw.length; i += 2) {
|
||||||
|
fields.push([raw[i], raw[i + 1]]);
|
||||||
|
}
|
||||||
|
return { cursor: nextCursor, fields };
|
||||||
|
}
|
||||||
|
async function setHashField(id, key, field, value) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.hset(key, field, value);
|
||||||
|
}
|
||||||
|
async function deleteHashField(id, key, field) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.hdel(key, field);
|
||||||
|
}
|
||||||
|
async function listRange(id, key, start, stop) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.lrange(key, start, stop);
|
||||||
|
}
|
||||||
|
async function listLength(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.llen(key);
|
||||||
|
}
|
||||||
|
async function listPush(id, key, ...values) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.rpush(key, ...values);
|
||||||
|
}
|
||||||
|
async function listSet(id, key, index, value) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.lset(key, index, value);
|
||||||
|
}
|
||||||
|
async function listRemove(id, key, count, value) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.lrem(key, count, value);
|
||||||
|
}
|
||||||
|
async function setMembers(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.smembers(key);
|
||||||
|
}
|
||||||
|
async function setSize(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.scard(key);
|
||||||
|
}
|
||||||
|
async function setAdd(id, key, ...members) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.sadd(key, ...members);
|
||||||
|
}
|
||||||
|
async function setRemove(id, key, ...members) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.srem(key, ...members);
|
||||||
|
}
|
||||||
|
async function zsetRange(id, key, start, stop, withScores) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
if (withScores) return client.zrange(key, start, stop, "WITHSCORES");
|
||||||
|
return client.zrange(key, start, stop);
|
||||||
|
}
|
||||||
|
async function zsetSize(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.zcard(key);
|
||||||
|
}
|
||||||
|
async function zsetAdd(id, key, score, member) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.zadd(key, score, member);
|
||||||
|
}
|
||||||
|
async function zsetRemove(id, key, ...members) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.zrem(key, ...members);
|
||||||
|
}
|
||||||
|
async function streamInfo(id, key) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.xinfo("STREAM", key);
|
||||||
|
}
|
||||||
|
async function streamRange(id, key, start, end, count) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.xrange(key, start, end, "COUNT", count);
|
||||||
|
}
|
||||||
|
async function streamAdd(id, key, streamId, fieldValues) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.xadd(key, streamId, ...fieldValues);
|
||||||
|
}
|
||||||
|
async function streamTrim(id, key, maxLen) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.xtrim(key, "MAXLEN", maxLen);
|
||||||
|
}
|
||||||
|
async function streamDel(id, key, ...ids) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.xdel(key, ...ids);
|
||||||
|
}
|
||||||
|
async function execute(id, command, args) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.call(command, ...args);
|
||||||
|
}
|
||||||
|
async function getServerInfo(id) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.info();
|
||||||
|
}
|
||||||
|
async function selectDb(id, db) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
await client.select(db);
|
||||||
|
}
|
||||||
|
async function dbSize(id) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.dbsize();
|
||||||
|
}
|
||||||
|
async function ping(id) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.ping();
|
||||||
|
}
|
||||||
|
async function slowLogGet(id, count) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.slowlog("GET", count);
|
||||||
|
}
|
||||||
|
async function slowLogLen(id) {
|
||||||
|
const client = getClient(id);
|
||||||
|
if (!client) throw new Error(`Client ${id} not found`);
|
||||||
|
return client.slowlog("LEN");
|
||||||
|
}
|
||||||
|
function registerIpcHandlers() {
|
||||||
|
ipcMain.on("window:minimize", () => BrowserWindow.getFocusedWindow()?.minimize());
|
||||||
|
ipcMain.on("window:maximize", () => {
|
||||||
|
const win = BrowserWindow.getFocusedWindow();
|
||||||
|
if (win) win.isMaximized() ? win.unmaximize() : win.maximize();
|
||||||
|
});
|
||||||
|
ipcMain.on("window:close", () => BrowserWindow.getFocusedWindow()?.close());
|
||||||
|
ipcMain.handle("theme:get", () => nativeTheme.shouldUseDarkColors);
|
||||||
|
ipcMain.on("theme:set", (_e, theme) => {
|
||||||
|
nativeTheme.themeSource = theme;
|
||||||
|
});
|
||||||
|
ipcMain.handle("dialog:openFile", async (_e, options) => {
|
||||||
|
const win = BrowserWindow.getFocusedWindow();
|
||||||
|
if (!win) return null;
|
||||||
|
const result = await dialog.showOpenDialog(win, options);
|
||||||
|
if (result.canceled || result.filePaths.length === 0) return null;
|
||||||
|
const fs = await import("fs");
|
||||||
|
try {
|
||||||
|
return { path: result.filePaths[0], content: fs.readFileSync(result.filePaths[0], "utf-8") };
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ipcMain.handle("credential:encrypt", (_e, text) => encrypt(text));
|
||||||
|
ipcMain.handle("credential:decrypt", (_e, encoded) => decrypt(encoded));
|
||||||
|
ipcMain.handle("storage:getConnections", () => store.get("connections", []));
|
||||||
|
ipcMain.handle("storage:saveConnection", (_e, conn) => {
|
||||||
|
const connections = store.get("connections", []);
|
||||||
|
const idx = connections.findIndex((c) => c.id === conn.id);
|
||||||
|
if (idx >= 0) connections[idx] = conn;
|
||||||
|
else connections.push(conn);
|
||||||
|
store.set("connections", connections);
|
||||||
|
return connections;
|
||||||
|
});
|
||||||
|
ipcMain.handle("storage:deleteConnection", (_e, id) => {
|
||||||
|
const connections = store.get("connections", []).filter((c) => c.id !== id);
|
||||||
|
store.set("connections", connections);
|
||||||
|
return connections;
|
||||||
|
});
|
||||||
|
ipcMain.handle("storage:reorderConnections", (_e, ids) => {
|
||||||
|
const connections = store.get("connections", []);
|
||||||
|
const reordered = ids.map((id, i) => {
|
||||||
|
const conn = connections.find((c) => c.id === id);
|
||||||
|
conn.order = i;
|
||||||
|
return conn;
|
||||||
|
});
|
||||||
|
store.set("connections", reordered);
|
||||||
|
return reordered;
|
||||||
|
});
|
||||||
|
ipcMain.handle("storage:getSettings", () => store.get("settings"));
|
||||||
|
ipcMain.handle("storage:saveSettings", (_e, settings) => {
|
||||||
|
store.set("settings", settings);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:connect", async (_e, id, opts) => {
|
||||||
|
try {
|
||||||
|
await connect(id, opts);
|
||||||
|
return { success: true };
|
||||||
|
} catch (err) {
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:disconnect", async (_e, id) => {
|
||||||
|
await disconnect(id);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:ping", async (_e, id) => {
|
||||||
|
return ping(id);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:isConnected", (_e, id) => {
|
||||||
|
return isClientConnected(id);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:execute", async (_e, id, command, args) => {
|
||||||
|
return execute(id, command, args);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:getInfo", async (_e, id) => {
|
||||||
|
return getServerInfo(id);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:selectDb", async (_e, id, db) => {
|
||||||
|
await selectDb(id, db);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:dbSize", async (_e, id) => {
|
||||||
|
return dbSize(id);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:slowLogGet", async (_e, id, count) => {
|
||||||
|
return slowLogGet(id, count);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:slowLogLen", async (_e, id) => {
|
||||||
|
return slowLogLen(id);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:scanKeys", async (_e, id, cursor, pattern, count) => {
|
||||||
|
return scanKeys(id, cursor, pattern, count);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:getKeyType", async (_e, id, key) => {
|
||||||
|
return getKeyType(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:getKeyTTL", async (_e, id, key) => {
|
||||||
|
return getKeyTTL(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:setTTL", async (_e, id, key, ttl) => {
|
||||||
|
await setKeyTTL(id, key, ttl);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:deleteKey", async (_e, id, key) => {
|
||||||
|
await deleteKey(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:renameKey", async (_e, id, oldKey, newKey) => {
|
||||||
|
await renameKey(id, oldKey, newKey);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:existsKey", async (_e, id, key) => {
|
||||||
|
return existsKey(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:batchDelete", async (_e, id, keys) => {
|
||||||
|
return batchDelete(id, keys);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:memoryUsage", async (_e, id, key) => {
|
||||||
|
return memoryUsage(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:getString", async (_e, id, key) => {
|
||||||
|
return getStringValue(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:setString", async (_e, id, key, value) => {
|
||||||
|
await setStringValue(id, key, value);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:hashScan", async (_e, id, key, cursor, count) => {
|
||||||
|
return getHashFields(id, key, cursor, count);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:hashSet", async (_e, id, key, field, value) => {
|
||||||
|
await setHashField(id, key, field, value);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:hashDel", async (_e, id, key, field) => {
|
||||||
|
await deleteHashField(id, key, field);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:listRange", async (_e, id, key, start, stop) => {
|
||||||
|
return listRange(id, key, start, stop);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:listLength", async (_e, id, key) => {
|
||||||
|
return listLength(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:listPush", async (_e, id, key, values) => {
|
||||||
|
return listPush(id, key, ...values);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:listSet", async (_e, id, key, index, value) => {
|
||||||
|
await listSet(id, key, index, value);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:listRemove", async (_e, id, key, count, value) => {
|
||||||
|
return listRemove(id, key, count, value);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:setMembers", async (_e, id, key) => {
|
||||||
|
return setMembers(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:setSize", async (_e, id, key) => {
|
||||||
|
return setSize(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:setAdd", async (_e, id, key, members) => {
|
||||||
|
return setAdd(id, key, ...members);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:setRemove", async (_e, id, key, members) => {
|
||||||
|
return setRemove(id, key, ...members);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:zsetRange", async (_e, id, key, start, stop, withScores) => {
|
||||||
|
return zsetRange(id, key, start, stop, withScores);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:zsetSize", async (_e, id, key) => {
|
||||||
|
return zsetSize(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:zsetAdd", async (_e, id, key, score, member) => {
|
||||||
|
return zsetAdd(id, key, score, member);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:zsetRemove", async (_e, id, key, members) => {
|
||||||
|
return zsetRemove(id, key, ...members);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:streamInfo", async (_e, id, key) => {
|
||||||
|
return streamInfo(id, key);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:streamRange", async (_e, id, key, start, end, count) => {
|
||||||
|
return streamRange(id, key, start, end, count);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:streamAdd", async (_e, id, key, streamId, fieldValues) => {
|
||||||
|
return streamAdd(id, key, streamId, fieldValues);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:streamTrim", async (_e, id, key, maxLen) => {
|
||||||
|
return streamTrim(id, key, maxLen);
|
||||||
|
});
|
||||||
|
ipcMain.handle("redis:streamDel", async (_e, id, key, ids) => {
|
||||||
|
return streamDel(id, key, ...ids);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mainWindow = null;
|
||||||
|
function getIconPath() {
|
||||||
|
return join(__dirname, "../../build/icons/icon_256.png");
|
||||||
|
}
|
||||||
|
function createWindow() {
|
||||||
|
const iconPath = getIconPath();
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
minWidth: 900,
|
||||||
|
minHeight: 600,
|
||||||
|
title: "JRedisDesktop",
|
||||||
|
icon: nativeImage.createFromPath(iconPath),
|
||||||
|
backgroundColor: nativeTheme.shouldUseDarkColors ? "#0f0f1a" : "#f8fafc",
|
||||||
|
webPreferences: {
|
||||||
|
preload: join(__dirname, "../preload/index.mjs"),
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: false
|
||||||
|
},
|
||||||
|
frame: false,
|
||||||
|
titleBarStyle: "hidden"
|
||||||
|
});
|
||||||
|
if (process.platform === "linux" && mainWindow) {
|
||||||
|
mainWindow.setIcon(nativeImage.createFromPath(iconPath));
|
||||||
|
}
|
||||||
|
restoreAndTrack(mainWindow);
|
||||||
|
if (process.env.ELECTRON_RENDERER_URL) {
|
||||||
|
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL);
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
} else {
|
||||||
|
mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
|
||||||
|
}
|
||||||
|
mainWindow.on("closed", () => {
|
||||||
|
mainWindow = null;
|
||||||
|
});
|
||||||
|
nativeTheme.on("updated", () => {
|
||||||
|
mainWindow?.webContents.send("theme:os-updated", nativeTheme.shouldUseDarkColors);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
registerIpcHandlers();
|
||||||
|
createWindow();
|
||||||
|
});
|
||||||
|
app.on("window-all-closed", () => {
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||||
|
});
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { contextBridge, ipcRenderer } from "electron";
|
||||||
|
const electronAPI = {
|
||||||
|
window: {
|
||||||
|
minimize: () => ipcRenderer.send("window:minimize"),
|
||||||
|
maximize: () => ipcRenderer.send("window:maximize"),
|
||||||
|
close: () => ipcRenderer.send("window:close")
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
get: () => ipcRenderer.invoke("theme:get"),
|
||||||
|
set: (theme) => ipcRenderer.send("theme:set", theme),
|
||||||
|
onOsUpdated: (callback) => {
|
||||||
|
ipcRenderer.on("theme:os-updated", (_e, isDark) => callback(isDark));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
openFile: (options) => ipcRenderer.invoke("dialog:openFile", options)
|
||||||
|
},
|
||||||
|
credential: {
|
||||||
|
encrypt: (text) => ipcRenderer.invoke("credential:encrypt", text),
|
||||||
|
decrypt: (encoded) => ipcRenderer.invoke("credential:decrypt", encoded)
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
getConnections: () => ipcRenderer.invoke("storage:getConnections"),
|
||||||
|
saveConnection: (conn) => ipcRenderer.invoke("storage:saveConnection", conn),
|
||||||
|
deleteConnection: (id) => ipcRenderer.invoke("storage:deleteConnection", id),
|
||||||
|
reorderConnections: (ids) => ipcRenderer.invoke("storage:reorderConnections", ids),
|
||||||
|
getSettings: () => ipcRenderer.invoke("storage:getSettings"),
|
||||||
|
saveSettings: (settings) => ipcRenderer.invoke("storage:saveSettings", settings)
|
||||||
|
},
|
||||||
|
redis: {
|
||||||
|
connect: (id, opts) => ipcRenderer.invoke("redis:connect", id, opts),
|
||||||
|
disconnect: (id) => ipcRenderer.invoke("redis:disconnect", id),
|
||||||
|
execute: (id, command, args) => ipcRenderer.invoke("redis:execute", id, command, args),
|
||||||
|
getInfo: (id) => ipcRenderer.invoke("redis:getInfo", id),
|
||||||
|
scanKeys: (id, cursor, pattern, count) => ipcRenderer.invoke("redis:scanKeys", id, cursor, pattern, count),
|
||||||
|
getKeyType: (id, key) => ipcRenderer.invoke("redis:getKeyType", id, key),
|
||||||
|
getKeyTTL: (id, key) => ipcRenderer.invoke("redis:getKeyTTL", id, key),
|
||||||
|
getString: (id, key) => ipcRenderer.invoke("redis:getString", id, key),
|
||||||
|
setString: (id, key, value) => ipcRenderer.invoke("redis:setString", id, key, value),
|
||||||
|
deleteKey: (id, key) => ipcRenderer.invoke("redis:deleteKey", id, key),
|
||||||
|
hashScan: (id, key, cursor, count) => ipcRenderer.invoke("redis:hashScan", id, key, cursor, count),
|
||||||
|
hashSet: (id, key, field, value) => ipcRenderer.invoke("redis:hashSet", id, key, field, value),
|
||||||
|
hashDel: (id, key, field) => ipcRenderer.invoke("redis:hashDel", id, key, field),
|
||||||
|
setTTL: (id, key, ttl) => ipcRenderer.invoke("redis:setTTL", id, key, ttl),
|
||||||
|
selectDb: (id, db) => ipcRenderer.invoke("redis:selectDb", id, db),
|
||||||
|
dbSize: (id) => ipcRenderer.invoke("redis:dbSize", id),
|
||||||
|
// List
|
||||||
|
listRange: (id, key, start, stop) => ipcRenderer.invoke("redis:listRange", id, key, start, stop),
|
||||||
|
listLength: (id, key) => ipcRenderer.invoke("redis:listLength", id, key),
|
||||||
|
listPush: (id, key, values) => ipcRenderer.invoke("redis:listPush", id, key, values),
|
||||||
|
listSet: (id, key, index, value) => ipcRenderer.invoke("redis:listSet", id, key, index, value),
|
||||||
|
listRemove: (id, key, count, value) => ipcRenderer.invoke("redis:listRemove", id, key, count, value),
|
||||||
|
// Set
|
||||||
|
setMembers: (id, key) => ipcRenderer.invoke("redis:setMembers", id, key),
|
||||||
|
setSize: (id, key) => ipcRenderer.invoke("redis:setSize", id, key),
|
||||||
|
setAdd: (id, key, members) => ipcRenderer.invoke("redis:setAdd", id, key, members),
|
||||||
|
setRemove: (id, key, members) => ipcRenderer.invoke("redis:setRemove", id, key, members),
|
||||||
|
// Sorted set
|
||||||
|
zsetRange: (id, key, start, stop, withScores) => ipcRenderer.invoke("redis:zsetRange", id, key, start, stop, withScores),
|
||||||
|
zsetSize: (id, key) => ipcRenderer.invoke("redis:zsetSize", id, key),
|
||||||
|
zsetAdd: (id, key, score, member) => ipcRenderer.invoke("redis:zsetAdd", id, key, score, member),
|
||||||
|
zsetRemove: (id, key, members) => ipcRenderer.invoke("redis:zsetRemove", id, key, members),
|
||||||
|
// Slow log
|
||||||
|
slowLogGet: (id, count) => ipcRenderer.invoke("redis:slowLogGet", id, count),
|
||||||
|
slowLogLen: (id) => ipcRenderer.invoke("redis:slowLogLen", id),
|
||||||
|
// Memory
|
||||||
|
memoryUsage: (id, key) => ipcRenderer.invoke("redis:memoryUsage", id, key),
|
||||||
|
batchDelete: (id, keys) => ipcRenderer.invoke("redis:batchDelete", id, keys),
|
||||||
|
// Stream
|
||||||
|
streamInfo: (id, key) => ipcRenderer.invoke("redis:streamInfo", id, key),
|
||||||
|
streamRange: (id, key, start, end, count) => ipcRenderer.invoke("redis:streamRange", id, key, start, end, count),
|
||||||
|
streamAdd: (id, key, streamId, fieldValues) => ipcRenderer.invoke("redis:streamAdd", id, key, streamId, fieldValues),
|
||||||
|
streamTrim: (id, key, maxLen) => ipcRenderer.invoke("redis:streamTrim", id, key, maxLen),
|
||||||
|
streamDel: (id, key, ids) => ipcRenderer.invoke("redis:streamDel", id, key, ids),
|
||||||
|
// Key ops
|
||||||
|
renameKey: (id, oldKey, newKey) => ipcRenderer.invoke("redis:renameKey", id, oldKey, newKey),
|
||||||
|
existsKey: (id, key) => ipcRenderer.invoke("redis:existsKey", id, key),
|
||||||
|
ping: (id) => ipcRenderer.invoke("redis:ping", id),
|
||||||
|
isConnected: (id) => ipcRenderer.invoke("redis:isConnected", id)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
contextBridge.exposeInMainWorld("electronAPI", electronAPI);
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>JRedisDesktop</title>
|
||||||
|
<script type="module" crossorigin src="./assets/index-DZjCwPfE.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="./assets/index-Czrh6LTB.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,7 +6,7 @@ export default defineConfig({
|
|||||||
main: {
|
main: {
|
||||||
plugins: [externalizeDepsPlugin()],
|
plugins: [externalizeDepsPlugin()],
|
||||||
build: {
|
build: {
|
||||||
outDir: 'electron-dist/main',
|
outDir: 'out/main',
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
input: {
|
||||||
index: resolve(__dirname, 'electron/main/index.ts')
|
index: resolve(__dirname, 'electron/main/index.ts')
|
||||||
@@ -17,7 +17,7 @@ export default defineConfig({
|
|||||||
preload: {
|
preload: {
|
||||||
plugins: [externalizeDepsPlugin()],
|
plugins: [externalizeDepsPlugin()],
|
||||||
build: {
|
build: {
|
||||||
outDir: 'electron-dist/preload',
|
outDir: 'out/preload',
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
input: {
|
||||||
index: resolve(__dirname, 'electron/preload/index.ts')
|
index: resolve(__dirname, 'electron/preload/index.ts')
|
||||||
@@ -28,7 +28,7 @@ export default defineConfig({
|
|||||||
renderer: {
|
renderer: {
|
||||||
root: 'src',
|
root: 'src',
|
||||||
build: {
|
build: {
|
||||||
outDir: resolve(__dirname, 'electron-dist/renderer'),
|
outDir: resolve(__dirname, 'out/renderer'),
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
input: {
|
||||||
index: resolve(__dirname, 'src/index.html')
|
index: resolve(__dirname, 'src/index.html')
|
||||||
|
|||||||
Reference in New Issue
Block a user