p3x-redis-ui-server 2026.4.310 → 2026.4.312
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/README.md +2 -2
- package/artifacts/cluster.md +1 -1
- package/dist/lib/redis-command-hints.mjs +1 -0
- package/dist/lib/redis-command-meta.mjs +1 -0
- package/dist/service/decompress.mjs +1 -0
- package/dist/service/http/index.mjs +1 -1
- package/dist/service/socket.io/request/ai-redis-query.mjs +1 -1
- package/dist/service/socket.io/request/connection-connect.mjs +1 -1
- package/dist/service/socket.io/request/key-get.mjs +1 -1
- package/dist/service/socket.io/request/memory-analysis.mjs +1 -0
- package/dist/service/socket.io/request/set-monitor.mjs +1 -1
- package/dist/service/socket.io/request/set-subscription.mjs +1 -1
- package/dist/service/socket.io/shared.mjs +1 -1
- package/package.json +5 -1
package/AGENTS.md
CHANGED
|
@@ -96,7 +96,7 @@ All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](ht
|
|
|
96
96
|
---
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
[**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.
|
|
99
|
+
[**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.312
|
|
100
100
|
|
|
101
101
|
[](https://www.npmjs.com/package/p3x-redis-ui-server) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [](https://www.patrikx3.com/en/front/contact) [](https://www.facebook.com/corifeus.software)
|
|
102
102
|
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ https://corifeus.com/redis-ui
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
|
-
# 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface v2026.4.
|
|
15
|
+
# 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface v2026.4.312
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
@@ -196,7 +196,7 @@ All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](ht
|
|
|
196
196
|
---
|
|
197
197
|
|
|
198
198
|
|
|
199
|
-
[**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.
|
|
199
|
+
[**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.312
|
|
200
200
|
|
|
201
201
|
[](https://www.npmjs.com/package/p3x-redis-ui-server) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [](https://www.patrikx3.com/en/front/contact) [](https://www.facebook.com/corifeus.software)
|
|
202
202
|
|
package/artifacts/cluster.md
CHANGED
|
@@ -55,7 +55,7 @@ All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](ht
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
[**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.
|
|
58
|
+
[**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.312
|
|
59
59
|
|
|
60
60
|
[](https://www.npmjs.com/package/p3x-redis-ui-server) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [](https://www.patrikx3.com/en/front/contact) [](https://www.facebook.com/corifeus.software)
|
|
61
61
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default{SET:"key value [EX seconds | PX ms | EXAT unix-sec | PXAT unix-ms | KEEPTTL] [NX | XX] [GET]",GET:"key",MSET:"key value [key value ...]",MGET:"key [key ...]",SETNX:"key value",SETEX:"key seconds value",PSETEX:"key milliseconds value",GETSET:"key value",GETDEL:"key",GETEX:"key [EX seconds | PX ms | EXAT unix-sec | PXAT unix-ms | PERSIST]",GETRANGE:"key start end",SETRANGE:"key offset value",APPEND:"key value",STRLEN:"key",INCR:"key",INCRBY:"key increment",INCRBYFLOAT:"key increment",DECR:"key",DECRBY:"key decrement",MSETNX:"key value [key value ...]",LCS:"key1 key2 [LEN] [IDX] [MINMATCHLEN min] [WITHMATCHLEN]",SUBSTR:"key start end",HSET:"key field value [field value ...]",HGET:"key field",HMSET:"key field value [field value ...]",HMGET:"key field [field ...]",HGETALL:"key",HDEL:"key field [field ...]",HEXISTS:"key field",HINCRBY:"key field increment",HINCRBYFLOAT:"key field increment",HKEYS:"key",HVALS:"key",HLEN:"key",HSETNX:"key field value",HRANDFIELD:"key [count [WITHVALUES]]",HSCAN:"key cursor [MATCH pattern] [COUNT count]",LPUSH:"key element [element ...]",RPUSH:"key element [element ...]",LPOP:"key [count]",RPOP:"key [count]",LLEN:"key",LRANGE:"key start stop",LINDEX:"key index",LSET:"key index element",LINSERT:"key BEFORE|AFTER pivot element",LREM:"key count element",LTRIM:"key start stop",LPOS:"key element [RANK rank] [COUNT count] [MAXLEN len]",LMOVE:"source destination LEFT|RIGHT LEFT|RIGHT",BLPOP:"key [key ...] timeout",BRPOP:"key [key ...] timeout",BLMOVE:"source destination LEFT|RIGHT LEFT|RIGHT timeout",RPOPLPUSH:"source destination",BRPOPLPUSH:"source destination timeout",LPUSHX:"key element [element ...]",RPUSHX:"key element [element ...]",LMPOP:"numkeys key [key ...] LEFT|RIGHT [COUNT count]",BLMPOP:"timeout numkeys key [key ...] LEFT|RIGHT [COUNT count]",SADD:"key member [member ...]",SREM:"key member [member ...]",SMEMBERS:"key",SISMEMBER:"key member",SMISMEMBER:"key member [member ...]",SCARD:"key",SPOP:"key [count]",SRANDMEMBER:"key [count]",SINTER:"key [key ...]",SUNION:"key [key ...]",SDIFF:"key [key ...]",SINTERSTORE:"destination key [key ...]",SUNIONSTORE:"destination key [key ...]",SDIFFSTORE:"destination key [key ...]",SMOVE:"source destination member",SINTERCARD:"numkeys key [key ...] [LIMIT limit]",SSCAN:"key cursor [MATCH pattern] [COUNT count]",ZADD:"key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]",ZREM:"key member [member ...]",ZSCORE:"key member",ZMSCORE:"key member [member ...]",ZRANK:"key member [WITHSCORE]",ZREVRANK:"key member [WITHSCORE]",ZRANGE:"key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]",ZRANGEBYSCORE:"key min max [WITHSCORES] [LIMIT offset count]",ZREVRANGEBYSCORE:"key max min [WITHSCORES] [LIMIT offset count]",ZRANGEBYLEX:"key min max [LIMIT offset count]",ZREVRANGEBYLEX:"key max min [LIMIT offset count]",ZRANGESTORE:"dst src min max [BYSCORE|BYLEX] [REV] [LIMIT offset count]",ZCARD:"key",ZCOUNT:"key min max",ZLEXCOUNT:"key min max",ZINCRBY:"key increment member",ZINTERSTORE:"destination numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX]",ZUNIONSTORE:"destination numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX]",ZDIFFSTORE:"destination numkeys key [key ...]",ZINTER:"numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]",ZUNION:"numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]",ZDIFF:"numkeys key [key ...] [WITHSCORES]",ZRANDMEMBER:"key [count [WITHSCORES]]",ZPOPMIN:"key [count]",ZPOPMAX:"key [count]",BZPOPMIN:"key [key ...] timeout",BZPOPMAX:"key [key ...] timeout",ZMPOP:"numkeys key [key ...] MIN|MAX [COUNT count]",BZMPOP:"timeout numkeys key [key ...] MIN|MAX [COUNT count]",ZSCAN:"key cursor [MATCH pattern] [COUNT count]",DEL:"key [key ...]",UNLINK:"key [key ...]",EXISTS:"key [key ...]",EXPIRE:"key seconds [NX|XX|GT|LT]",PEXPIRE:"key milliseconds [NX|XX|GT|LT]",EXPIREAT:"key unix-time-seconds [NX|XX|GT|LT]",PEXPIREAT:"key unix-time-milliseconds [NX|XX|GT|LT]",PERSIST:"key",TTL:"key",PTTL:"key",EXPIRETIME:"key",PEXPIRETIME:"key",TYPE:"key",RENAME:"key newkey",RENAMENX:"key newkey",KEYS:"pattern",SCAN:"cursor [MATCH pattern] [COUNT count] [TYPE type]",RANDOMKEY:"",DUMP:"key",RESTORE:"key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]",COPY:"source destination [DB destination-db] [REPLACE]",MOVE:"key db",OBJECT:"ENCODING|FREQ|HELP|IDLETIME|REFCOUNT key",SORT:"key [BY pattern] [LIMIT offset count] [GET pattern ...] [ASC|DESC] [ALPHA] [STORE destination]",SORT_RO:"key [BY pattern] [LIMIT offset count] [GET pattern ...] [ASC|DESC] [ALPHA]",TOUCH:"key [key ...]",WAIT:"numreplicas timeout",WAITAOF:"numlocal numreplicas timeout",XADD:"key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|id field value [field value ...]",XREAD:"[COUNT count] [BLOCK ms] STREAMS key [key ...] id [id ...]",XREADGROUP:"GROUP group consumer [COUNT count] [BLOCK ms] [NOACK] STREAMS key [key ...] id [id ...]",XRANGE:"key start end [COUNT count]",XREVRANGE:"key end start [COUNT count]",XLEN:"key",XINFO:"CONSUMERS|GROUPS|STREAM key [FULL [COUNT count]]",XACK:"key group id [id ...]",XCLAIM:"key group consumer min-idle-time id [id ...] [IDLE ms] [TIME ms] [RETRYCOUNT count] [FORCE] [JUSTID] [LASTID id]",XAUTOCLAIM:"key group consumer min-idle-time start [COUNT count] [JUSTID]",XDEL:"key id [id ...]",XTRIM:"key MAXLEN|MINID [=|~] threshold [LIMIT count]",XGROUP:"CREATE|CREATECONSUMER|DELCONSUMER|DESTROY|SETID key group [id|$] [MKSTREAM] [ENTRIESREAD entries-read]",XPENDING:"key group [[IDLE min-idle-time] start end count [consumer]]",XSETID:"key last-id [ENTRIESREAD entries-read]",INFO:"[section ...]",DBSIZE:"",FLUSHDB:"[ASYNC|SYNC]",FLUSHALL:"[ASYNC|SYNC]",SELECT:"index",PING:"[message]",ECHO:"message",QUIT:"",CONFIG:"GET|SET|RESETSTAT|REWRITE parameter [value]",SLOWLOG:"GET|LEN|RESET [count]",CLIENT:"GETNAME|ID|INFO|KILL|LIST|NO-EVICT|PAUSE|REPLY|SETNAME|UNPAUSE|NO-TOUCH ...",MEMORY:"DOCTOR|HELP|MALLOC-STATS|PURGE|STATS|USAGE key [SAMPLES count]",TIME:"",LASTSAVE:"",BGSAVE:"[SCHEDULE]",BGREWRITEAOF:"",SAVE:"",SHUTDOWN:"[NOSAVE|SAVE] [NOW] [FORCE]",ACL:"CAT|DELUSER|GENPASS|GETUSER|LIST|LOAD|LOG|SAVE|SETUSER|WHOAMI ...",MODULE:"LIST|LOAD|LOADEX|UNLOAD ...",COMMAND:"[COUNT|DOCS|GETKEYS|INFO|LIST ...]",DEBUG:"subcommand [arg ...]",LATENCY:"HISTORY|LATEST|RESET event",SWAPDB:"index1 index2",REPLICAOF:"host port",SLAVEOF:"host port",FAILOVER:"[TO host port [FORCE]] [ABORT] [TIMEOUT ms]",CLUSTER:"INFO|NODES|SLOTS|MYID|RESET|KEYSLOT|...",PUBLISH:"channel message",SUBSCRIBE:"channel [channel ...]",UNSUBSCRIBE:"[channel ...]",PSUBSCRIBE:"pattern [pattern ...]",PUNSUBSCRIBE:"[pattern ...]",PUBSUB:"CHANNELS|NUMSUB|NUMPAT|SHARDCHANNELS|SHARDNUMSUB [argument ...]",EVAL:'"script" numkeys [key ...] [arg ...]',EVALSHA:"sha1 numkeys [key ...] [arg ...]",EVALRO:'"script" numkeys [key ...] [arg ...]',EVALSHA_RO:"sha1 numkeys [key ...] [arg ...]",SCRIPT:"DEBUG|EXISTS|FLUSH|LOAD ...",FCALL:"function numkeys [key ...] [arg ...]",FCALL_RO:"function numkeys [key ...] [arg ...]",FUNCTION:"CREATE|DELETE|DUMP|FLUSH|LIST|LOAD|RESTORE|STATS ...",GEOADD:"key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]",GEOPOS:"key member [member ...]",GEODIST:"key member1 member2 [M|KM|FT|MI]",GEOSEARCH:"key FROMMEMBER member|FROMLONLAT lng lat BYRADIUS radius M|KM|FT|MI|BYBOX width height M|KM|FT|MI [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]",GEOSEARCHSTORE:"destination source FROMMEMBER member|FROMLONLAT lng lat BYRADIUS radius unit|BYBOX width height unit [ASC|DESC] [COUNT count [ANY]] [STOREDIST]",GEORADIUS:"key longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]",GEORADIUSBYMEMBER:"key member radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]",GEOHASH:"key member [member ...]",PFADD:"key element [element ...]",PFCOUNT:"key [key ...]",PFMERGE:"destkey sourcekey [sourcekey ...]",SETBIT:"key offset value",GETBIT:"key offset",BITCOUNT:"key [start end [BYTE|BIT]]",BITPOS:"key bit [start [end [BYTE|BIT]]]",BITOP:"AND|OR|XOR|NOT destkey key [key ...]",BITFIELD:"key [GET encoding offset | SET encoding offset value | INCRBY encoding offset increment | OVERFLOW WRAP|SAT|FAIL] ...",BITFIELD_RO:"key GET encoding offset [GET encoding offset ...]","JSON.SET":"key path value [NX|XX]","JSON.GET":"key [path ...]","JSON.DEL":"key [path]","JSON.MGET":"key [key ...] path","JSON.TYPE":"key [path]","JSON.NUMINCRBY":"key path value","JSON.NUMMULTBY":"key path value","JSON.STRAPPEND":"key [path] value","JSON.STRLEN":"key [path]","JSON.ARRAPPEND":"key path value [value ...]","JSON.ARRINDEX":"key path value [start [stop]]","JSON.ARRINSERT":"key path index value [value ...]","JSON.ARRLEN":"key [path]","JSON.ARRPOP":"key [path [index]]","JSON.ARRTRIM":"key path start stop","JSON.OBJKEYS":"key [path]","JSON.OBJLEN":"key [path]","JSON.RESP":"key [path]","FT.CREATE":"index [ON HASH|JSON] [PREFIX count prefix ...] SCHEMA field TYPE [SORTABLE] ...","FT.SEARCH":"index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS] [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER field min max ...] [GEOFILTER field lon lat radius unit] [INKEYS count key ...] [INFIELDS count field ...] [RETURN count field ...] [SUMMARIZE ...] [HIGHLIGHT ...] [SLOP slop] [TIMEOUT timeout] [INORDER] [LANGUAGE lang] [EXPANDER exp] [SCORER scorer] [SORTBY field [ASC|DESC]] [LIMIT offset num]","FT.AGGREGATE":"index query [LOAD count field ...] [GROUPBY count field ... REDUCE func nargs arg ... [AS name]] [SORTBY count field [ASC|DESC] ...] [APPLY expr AS name] [LIMIT offset num] [FILTER expr]","FT.INFO":"index","FT.DROPINDEX":"index [DD]","FT._LIST":"","FT.ALTER":"index SCHEMA ADD field TYPE ...","FT.ALIASADD":"alias index","FT.ALIASDEL":"alias","FT.ALIASUPDATE":"alias index","FT.TAGVALS":"index field","FT.EXPLAIN":"index query","TS.CREATE":"key [RETENTION retentionPeriod] [ENCODING UNCOMPRESSED|COMPRESSED] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value ...]","TS.ADD":"key timestamp value [RETENTION retentionPeriod] [ENCODING UNCOMPRESSED|COMPRESSED] [CHUNK_SIZE size] [ON_DUPLICATE policy] [LABELS label value ...]","TS.MADD":"key timestamp value [key timestamp value ...]","TS.INCRBY":"key value [TIMESTAMP timestamp] [RETENTION retentionPeriod] [LABELS label value ...]","TS.DECRBY":"key value [TIMESTAMP timestamp] [RETENTION retentionPeriod] [LABELS label value ...]","TS.DEL":"key fromTimestamp toTimestamp","TS.RANGE":"key fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [COUNT count] [AGGREGATION aggregator bucketDuration]","TS.REVRANGE":"key fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [COUNT count] [AGGREGATION aggregator bucketDuration]","TS.MRANGE":"fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [WITHLABELS] [COUNT count] [AGGREGATION aggregator bucketDuration] FILTER filter ...","TS.MREVRANGE":"fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [WITHLABELS] [COUNT count] [AGGREGATION aggregator bucketDuration] FILTER filter ...","TS.GET":"key [LATEST]","TS.MGET":"[LATEST] [WITHLABELS] FILTER filter ...","TS.INFO":"key [DEBUG]","TS.ALTER":"key [RETENTION retentionPeriod] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value ...]","TS.QUERYINDEX":"filter ...","TS.CREATERULE":"sourceKey destKey AGGREGATION aggregator bucketDuration [alignTimestamp]","TS.DELETERULE":"sourceKey destKey"};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import r from"./redis-command-hints.mjs";const t={"@string":"String","@hash":"Hash","@list":"List","@set":"Set","@sortedset":"Sorted Set","@stream":"Stream","@geo":"Geo","@hyperloglog":"HyperLogLog","@bitmap":"Bitmap","@keyspace":"Key","@connection":"Connection","@server":"Server","@generic":"Generic","@pubsub":"Pub/Sub","@scripting":"Scripting","@transactions":"Transactions","@cluster":"Cluster","@slow":null,"@fast":null,"@read":null,"@write":null,"@dangerous":null,"@admin":null};export function buildCommandMeta(r){const t={};if(Array.isArray(r))for(const n of r)e(n,t);else if(r&&"object"==typeof r)for(const n of Object.keys(r))e(r[n],t);return t}function e(e,n){if(!Array.isArray(e)||e.length<7)return;const o=e[0]?.toUpperCase();if(!o)return;const s=e[6]||[];let i="Other";for(const r of s)if(t.hasOwnProperty(r)&&null!==t[r]){i=t[r];break}const a=r[o]??"";n[o]={syntax:a,group:i};const c=e[9];if(Array.isArray(c))for(const t of c){if(!Array.isArray(t)||t.length<7)continue;const e=t[0]?.toUpperCase();if(!e)continue;const o=e.replace("|"," "),s=r[o]??"";n[o]={syntax:s,group:i}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import r from"node:zlib";import t from"snappyjs";import e from"lz4js";import{decompress as n}from"fzstd";export async function tryDecompress(c){if(!Buffer.isBuffer(c)||c.length<2)return null;const s=c[0],f=c[1];if(31===s&&139===f){try{const t=r.gunzipSync(c);if(o(t))return{algorithm:"gzip",decompressed:t}}catch{}return null}if(c.length>=4&&80===s&&75===f&&3===c[2]&&4===c[3]){try{const t=await async function(t){const e=t.readUInt16LE(8),n=t.readUInt32LE(18),o=30+t.readUInt16LE(26)+t.readUInt16LE(28);if(o+n>t.length)return null;const c=t.subarray(o,o+n);return 0===e?Buffer.from(c):8===e?r.inflateRawSync(c):null}(c);if(t&&o(t))return{algorithm:"zip",decompressed:t}}catch{}return null}if(120===s&&(30720+f)%31==0){try{const t=r.inflateSync(c);if(o(t))return{algorithm:"zlib",decompressed:t}}catch{}return null}if(c.length>=4&&40===s&&181===f&&47===c[2]&&253===c[3]){try{const r=Buffer.from(n(c));if(o(r))return{algorithm:"zstd",decompressed:r}}catch{}return null}if(c.length>=4&&4===s&&34===f&&77===c[2]&&24===c[3]){try{const r=Buffer.from(e.decompress(c));if(o(r))return{algorithm:"lz4",decompressed:r}}catch{}return null}if(!o(c)){try{const r=Buffer.from(t.uncompress(c));if(r.length>0&&o(r))return{algorithm:"snappy",decompressed:r}}catch{}try{const t=r.brotliDecompressSync(c);if(t.length>0&&o(t))return{algorithm:"brotli",decompressed:t}}catch{}}return null}function o(r){const t=Math.min(r.length,64);for(let e=0;e<t;e++){const t=r[e];if(t<9||t>13&&t<32||127===t)return!1}return!0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"express";import e from"fs";import r from"path";import s from"http";import{fileURLToPath as o}from"url";import{resolveConfiguredHttpAuth as i,verifyAuthorizationHeader as n}from"../../lib/http-auth.mjs";const p=r.dirname(o(import.meta.url));export default function(){this.boot=async()=>{const o=t();this.app=o,o.disable("x-powered-by"),o.get("/health",(t,e)=>{
|
|
1
|
+
import t from"express";import e from"fs";import r from"path";import s from"http";import{fileURLToPath as o}from"url";import{resolveConfiguredHttpAuth as i,verifyAuthorizationHeader as n}from"../../lib/http-auth.mjs";const p=r.dirname(o(import.meta.url));export default function(){this.boot=async()=>{const o=t();this.app=o,o.disable("x-powered-by"),o.get("/health",(t,e)=>{e.json({status:"ok",version:p3xrs.cfg?.version||"unknown",uptime:process.uptime()})}),o.use((t,e,r)=>{if(!i().enabled)return void r();const s=t.get("authorization");n(s)?r():(e.set("WWW-Authenticate",'Basic realm="P3X Redis UI"'),e.status(401).json({error:"http_auth_required"}))});let a,u=!1;"string"==typeof p3xrs.cfg.static&&(u=!0,a=(t=>{if(t.startsWith("~")){return((t,s)=>{let o=p;for(;o!==r.resolve(o,"..");){const t=r.join(o,s);if(e.existsSync(t))return t;o=r.resolve(o,"..")}throw new Error("The specified module could not be found in any node_modules directory")})(0,t.substring(1))}return r.resolve(process.cwd(),t)})(p3xrs.cfg.static),o.use(t.static(a))),u?o.use((t,e,s)=>{t.path.startsWith("/socket.io")?s():e.sendFile(r.resolve(a,"index.html"),t=>{t&&s(t)})}):o.use((t,e)=>{e.json({status:"operational"})}),o.use((t,e,r,s)=>{console.error("express server error",t),r.headersSent?s(t):r.status(500).json({error:"internal_server_error"})});const c=s.createServer(o);this.server=c,c.listen(p3xrs.cfg.http.port||7843,p3xrs.cfg.http.bind?p3xrs.cfg.http.bind:"0.0.0.0")}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"groq-sdk";export default async n=>{const{socket:s,payload:t}=n;try{const{prompt:
|
|
1
|
+
import e from"groq-sdk";export default async n=>{const{socket:s,payload:t}=n;try{const{prompt:i,context:r}=t;if(!i||"string"!=typeof i||0===i.trim().length)throw new Error("AI_PROMPT_REQUIRED");if(!1===p3xrs.cfg.aiEnabled)throw new Error("AI_DISABLED");const a=p3xrs.cfg.groqApiKey||"";let o;!0===p3xrs.cfg.aiUseOwnKey&&a?(console.info("ai-redis-query: using direct Groq API (own key)"),o=await async function(n,s,t){const i=new e({apiKey:t}),r=function(e){let n='You are an expert Redis command generator embedded in a Redis GUI console. Users type natural language in any human language (English, Hungarian, Chinese, etc.) and you translate it into valid Redis CLI commands.\n\n# Output Format\nOne or more Redis commands (one per line), then a separator, then an explanation:\n\n```\nCOMMAND1\nCOMMAND2\n---\nBrief explanation in the user\'s language\n```\n\n- For simple requests: output a single command line\n- For complex requests needing multiple steps: output multiple command lines (one per line)\n- For bulk operations: prefer a single EVAL script, but use multiple commands if clearer\n- The --- separator is REQUIRED between commands and explanation\n- The explanation should be in the SAME LANGUAGE as the user\'s input\n\n# Core Principles\n1. Generate ONLY real, valid Redis commands that a Redis server will accept\n2. Never invent key names, index names, or field names \u2014 use only what is provided in context or use wildcard patterns\n3. The user\'s Redis GUI will execute your command directly \u2014 it must be syntactically correct\n4. Support all human languages as input \u2014 always output a Redis command regardless of input language\n\n# Command Selection Guide\n\n## Key Discovery & Listing\n- "show all keys" / "list keys" \u2192 KEYS *\n- "find keys matching user" \u2192 KEYS user:*\n- "keys starting with session" \u2192 KEYS session:*\n- "how many keys" \u2192 DBSIZE\n\n## Key Type Filtering\nWhen user asks for keys of a specific data type, use SCAN with TYPE filter:\n- "show all hash keys" \u2192 SCAN 0 MATCH * TYPE hash COUNT 10000\n- "show all json keys" / "rejson keys" \u2192 SCAN 0 MATCH * TYPE ReJSON-RL COUNT 10000\n- "show all set keys" \u2192 SCAN 0 MATCH * TYPE set COUNT 10000\n- "show all list keys" \u2192 SCAN 0 MATCH * TYPE list COUNT 10000\n- "show all string keys" \u2192 SCAN 0 MATCH * TYPE string COUNT 10000\n- "show all stream keys" \u2192 SCAN 0 MATCH * TYPE stream COUNT 10000\n- "show all sorted set keys" \u2192 SCAN 0 MATCH * TYPE zset COUNT 10000\n- For checking a single key\'s type \u2192 TYPE keyname\nNote: SCAN returns [cursor, [keys...]]. cursor=0 means scan complete.\n\n## Reading Values\n- String: GET key\n- Hash: HGETALL key | HGET key field\n- List: LRANGE key 0 -1\n- Set: SMEMBERS key\n- Sorted Set: ZRANGE key 0 -1 WITHSCORES\n- Stream: XRANGE key - +\n- JSON/ReJSON: JSON.GET key $ | JSON.GET key $.fieldname\n- Multiple strings: MGET key1 key2\n- Multiple JSON: JSON.MGET key1 key2 $\n\n## Writing Values\n- String: SET key value [EX seconds]\n- Hash: HSET key field value [field value ...]\n- List: LPUSH/RPUSH key value [value ...]\n- Set: SADD key member [member ...]\n- Sorted Set: ZADD key score member [score member ...]\n- Stream: XADD key * field value [field value ...]\n- JSON: JSON.SET key $ \'jsonvalue\'\n\n## Key Management\n- Delete: DEL key [key ...]\n- Rename: RENAME key newkey\n- TTL check: TTL key | PTTL key\n- Set expiry: EXPIRE key seconds | PEXPIRE key ms\n- Persist (remove TTL): PERSIST key\n- Check existence: EXISTS key [key ...]\n\n## Server & Info\n- Server info: INFO [section] (sections: server, clients, memory, stats, replication, cpu, modules, keyspace, all)\n- Memory usage: MEMORY USAGE key | INFO memory\n- Connected clients: CLIENT LIST\n- Config: CONFIG GET parameter\n- Slow log: SLOWLOG GET [count]\n- Database size: DBSIZE\n- Flush database: FLUSHDB\n- Flush all: FLUSHALL\n- Last save: LASTSAVE\n- Server time: TIME\n\n## RediSearch (only when explicitly requested)\n- Search: FT.SEARCH indexname query\n- List indexes: FT._LIST\n- Index info: FT.INFO indexname\n- Aggregate: FT.AGGREGATE indexname query\n- Create index: FT.CREATE indexname ON HASH PREFIX 1 prefix: SCHEMA field TYPE ...\n- Drop index: FT.DROPINDEX indexname\n\n## Pub/Sub\n- Publish: PUBLISH channel message\n- Subscribe: SUBSCRIBE channel\n\n## Cluster\n- Cluster info: CLUSTER INFO\n- Cluster nodes: CLUSTER NODES\n\n## Multi-step operations \u2014 PREFER multiple commands over EVAL\nWhen the user needs multiple Redis operations, output them as separate commands (one per line):\n- SET test:str hello\n- HSET test:hash f1 v1 f2 v2\n- RPUSH test:list a b c\nThis is ALWAYS preferred over EVAL unless a loop is needed.\n\n## Scripting (EVAL) \u2014 ONLY for loops or atomic operations\nUse EVAL ONLY when a loop or atomicity is required (e.g. "generate 100 random keys"):\n- EVAL "lua_script" numkeys [key ...] [arg ...]\n- Write Lua code with REAL line breaks inside the quotes \u2014 the console supports multi-line input\n- NEVER use literal \\n escape sequences \u2014 they cause Redis script compilation errors\n- CORRECT example:\nEVAL "\nfor i=1,3 do\n redis.call(\'SET\',\'k\'..i,i)\nend\nreturn \'done\'\n" 0\n- WRONG: EVAL "for i=1,3 do\\nredis.call(\'SET\',\'k\'..i,i)\\nend" 0\n\n# Redis Type Names (for TYPE command responses)\n- string, list, set, zset, hash, stream, ReJSON-RL\n\n# Critical Rules\n- NEVER use FT.SEARCH or FT.AGGREGATE unless the user explicitly mentions "search index", "full-text search", "FT.", or "RediSearch"\n- NEVER fabricate key names \u2014 if unsure, use patterns like KEYS * or KEYS prefix:*\n- NEVER fabricate index names \u2014 if indexes are provided in context, use those exact names\n- When the user mentions "rejson", "json keys", or "JSON type", they mean keys stored with the RedisJSON module\n- Prefer simple commands \u2014 KEYS over SCAN for readability in a GUI console\n- If the user asks something that needs multiple steps, output multiple commands (one per line)';if(e){const s=[];e.redisVersion&&s.push(`Redis version: ${e.redisVersion}`),e.redisMode&&s.push(`Mode: ${e.redisMode}`),e.usedMemory&&s.push(`Memory: ${e.usedMemory}`),e.connectedClients&&s.push(`Clients: ${e.connectedClients}`),e.os&&s.push(`OS: ${e.os}`),e.modules&&s.push(`Loaded modules: ${JSON.stringify(e.modules)}`),e.databases&&e.databases.length>0&&s.push(`Databases: ${e.databases.join(", ")}`),s.length>0&&(n+=`\n\n# Connected Redis Server\n${s.join("\n")}`),e.indexes&&e.indexes.length>0&&(n+=`\n\nAvailable RediSearch indexes: ${e.indexes.join(", ")}`),e.schema&&(n+=`\n\nSchema information: ${JSON.stringify(e.schema)}`),e.uiLanguage&&(n+="\n\n# Response Language\nYou MUST write the explanation (line 2) in the SAME language as the user's prompt. If they write in Hungarian, respond in Hungarian. If in English, respond in English. Always match the user's language.")}return n}(s),a=await i.chat.completions.create({messages:[{role:"system",content:r},{role:"user",content:n}],model:"openai/gpt-oss-120b",max_tokens:4096,temperature:.1});return function(e){const n=e.indexOf("\n---");if(-1!==n)return{command:e.substring(0,n).trim(),explanation:e.substring(n).replace(/^[\n\r]*---[\n\r]*/,"").trim()};const s=e.split("\n").filter(e=>e.trim().length>0);return{command:s[0]||"",explanation:s.slice(1).join(" ")||""}}(a.choices?.[0]?.message?.content?.trim()||"")}(i.trim(),r,a)):(console.info("ai-redis-query: using network proxy"),o=await async function(e,n,s){const t="string"==typeof p3xrs.cfg.aiNetworkUrl&&p3xrs.cfg.aiNetworkUrl.length>0?p3xrs.cfg.aiNetworkUrl:"development"===process.env.NODE_ENV?"http://localhost:8003":"https://network.corifeus.com";let i;try{i=await fetch(`${t}/public/ai/redis-query`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:e,context:n||{},apiKey:s||void 0})})}catch(e){throw new Error("AI service is not reachable")}if(!(i.headers.get("content-type")||"").includes("application/json"))throw new Error(`AI service returned invalid response (${i.status})`);const r=await i.json();if("ok"!==r.status)throw new Error(r.message||"AI query failed");return{command:r.data.command,explanation:r.data.explanation}}(i.trim(),r,a||void 0)),s.emit(n.responseEvent,{status:"ok",command:o.command,explanation:o.explanation})}catch(e){console.error("ai-redis-query error",e),s.emit(n.responseEvent,{status:"error",error:e.message})}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../../../lib/ioredis-cluster/index.mjs";import*as s from"../shared.mjs";import n from"../../../lib/redis-static-commands.mjs";const
|
|
1
|
+
import e from"../../../lib/ioredis-cluster/index.mjs";import*as s from"../shared.mjs";import n from"../../../lib/redis-static-commands.mjs";import{buildCommandMeta as o}from"../../../lib/redis-command-meta.mjs";const t="socket.io connection-connect",r=async e=>{const{socket:t,redis:r,payload:a}=e,{db:i}=a;let c,d=n;if(!0===e.payload.connection.cluster)c=1;else try{c=(await r.config("get","databases"))[1],console.info(e.payload.connection.name,"instance successfully works the database listing")}catch(s){console.warn(e.payload.connection.name,"instance get databases listing is disabled",s),c=await(async()=>{let e=!0,s=0,n=0;for(await new Promise(e=>setTimeout(e,1e3));e;)try{await r.call("select",s),s>512&&(console.warn("limiting to max 512 database index, as it could crash with a big db index number"),e=!1),s++}catch(n){console.error(n),console.warn("found wrong current db index",s),e=!1}if(n=s,i<=n)try{await r.call("select",i)}catch(e){console.error(e)}return console.log("calculated max databases index",n),n})()}console.info(e.payload.connection.name,"databases got",c);try{d=await r.command(),console.info(e.payload.connection.name,"instance command listing is available")}catch(s){console.warn(e.payload.connection.name,"instance command listing is not available, not all redis instances are not available command listing",s)}const l=await s.detectModules(r);l.length>0&&console.info(e.payload.connection.name,"modules detected:",l.map(e=>e.name).join(", ")),await s.getFullInfoAndSendSocket({setDb:!0,redis:r,responseEvent:e.responseEvent,socket:t,extend:{databases:c,commands:d,commandsMeta:o(d),modules:l},payload:a})};export default async n=>{const{socket:o,payload:a}=n,{connection:i,db:c}=a;try{if(o.p3xrs.connectionId!==i.id&&s.disconnectRedis({socket:o}),p3xrs.redisConnections.hasOwnProperty(i.id)||(p3xrs.redisConnections[i.id]={connection:i,clients:[]}),p3xrs.redisConnections[i.id].clients.includes(o.id)||(console.info(t,"added new socket.id",o.id,"to",i.id,"name with",i.name),p3xrs.redisConnections[i.id].clients.push(o.id)),void 0!==o.p3xrs.ioredis)console.info(t,"redis was already connected"),o.p3xrs.connectionId=i.id,await r({redis:o.p3xrs.ioredis,socket:o,responseEvent:n.responseEvent,payload:a}),s.sendStatus({socket:o});else{const a=p3xrs.connections.list.find(e=>n.payload.connection.id===e.id);if(void 0===a)throw new Error("auto-connection-failed");i.askAuth&&(a.username=void 0,a.password=void 0,i.username&&(a.username=i.username),i.password&&(a.password=i.password));let c=Object.assign({},a);const d=c.sentinelName;delete c.name,delete c.id,c.retryStrategy=null,c.clusterRetryStrategy=null,c.tlsWithoutCert?c.tls={servername:c.host}:"string"==typeof c.tlsCa&&""!==c.tlsCa.trim()&&(c.tls={cert:c.tlsCrt,key:c.tlsKey,ca:c.tlsCa,servername:c.host}),c.hasOwnProperty("tls")&&(c.tls.rejectUnauthorized=void 0!==c.tlsRejectUnauthorized&&c.tlsRejectUnauthorized,c.tls.hasOwnProperty("servername")||(c.tls.servername=c.host));const l=()=>{s.disconnectRedis({socket:o}),o.p3xrs.connectionId=void 0,o.p3xrs.ioredis=void 0,o.p3xrs.ioredisSubscriber=void 0};let p=a.nodes||[];if(!0===c.ssh){const{createTunnel:e}=await import("tunnel-ssh"),s=await import("net"),t={host:c.sshHost,port:c.sshPort,username:c.sshUsername};c.sshPrivateKey?t.privateKey=c.sshPrivateKey:t.password=c.sshPassword;const r=[];let[a,i]=await e({autoClose:!0},null,t,{dstAddr:c.host,dstPort:c.port});if(r.push(a),c.port=a.address().port,p.length>0){p=p.map(e=>Object.assign({},e));for(const e of p){const n=await new Promise((n,o)=>{const t=s.createServer(s=>{i.forwardOut("127.0.0.1",0,e.host||"localhost",e.port,(e,n)=>{e?s.end():s.pipe(n).pipe(s)})});t.listen(0,"127.0.0.1",()=>n(t)),t.on("error",o)});r.push(n),e.port=n.address().port}}o.p3xrs.tunnels=r,o.p3xrs.sshClient=i,i.on("error",async e=>{console.error("ssh client error",e),l(),o.emit(n.responseEvent,{status:"error",error:e.message})});for(const e of r)e.on("error",async e=>{console.error("ssh tunnel server error",e),l(),o.emit(n.responseEvent,{status:"error",error:e.message})})}(c.hasOwnProperty("sentinel")&&!0===c.sentinel||!0===c.cluster)&&(c=[c].concat(p)),Array.isArray(c)&&c[0].hasOwnProperty("sentinel")&&!0===c[0].sentinel&&(c={sentinels:c,name:d,sentinelPassword:c[0].password,sentinelRetryStrategy:()=>!1});let m=new e(c),u=new e(c);o.p3xrs.connectionId=i.id,o.p3xrs.readonly=!0===a.readonly&&isProOrEnterpriseTier(),o.p3xrs.ioredis=m,o.p3xrs.ioredisSubscriber=u;let y=!1;const w=async function(e){e||((e=new Error("Connection is closed.")).p3xCode="disconnect");const t="socket.io connection-connect redis error fun";console.warn(t,i.id,i.name,"error"),console.error(e),console.warn(t,"didConnected",y),y||o.emit(n.responseEvent,{status:"error",error:e.message});const r={connectionId:o.p3xrs.connectionId,error:e.message,status:"error"};console.warn(t,"disconnectedData",r),o.p3xrs.io.emit("redis-disconnected",r);try{await s.disconnectRedis({socket:o})}catch(e){console.warn(t,"disconnectRedis"),console.error(e)}l(),s.sendStatus({socket:o})};m.on("error",w),m.on("disconnect",w),u.on("error",w),m.on("connect",async function(){try{console.info(t,n.payload.connection.id,n.payload.connection.name,"connected"),y=!0,await r({redis:m,socket:o,responseEvent:n.responseEvent,payload:n.payload})}catch(e){console.error(e),o.emit(n.responseEvent,{status:"error",error:e.message})}finally{s.sendStatus({socket:o})}})}}catch(e){console.error(e),o.emit(n.responseEvent,{status:"error",error:e.message})}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default async
|
|
1
|
+
import{tryDecompress as e}from"../../decompress.mjs";export default async r=>{const{socket:s,payload:i}=r;try{let t=s.p3xrs.ioredis;const o=i.key;let a=await t.type(o);"ReJSON-RL"===a&&(a="json"),"TSDB-TYPE"===a&&(a="timeseries");const l=t.pipeline();switch(a){case"string":l.getBuffer(o);break;case"list":l.lrangeBuffer(o,0,-1);break;case"hash":l.hgetallBuffer(o);break;case"set":l.smembersBuffer(o);break;case"zset":l.zrangeBuffer(o,0,-1,"WITHSCORES");break;case"stream":l.xrangeBuffer(o,"-","+");break;case"json":l.call("JSON.GET",o,"$");break;case"timeseries":l.call("TS.INFO",o)}switch(l.ttl(o),"json"!==a&&"timeseries"!==a&&l.object("encoding",o),a){case"stream":l.xlen(o);break;case"hash":l.hlen(o);break;case"list":l.llen(o);break;case"set":l.scard(o);break;case"zset":l.zcard(o)}const n=await l.exec();let c=n[0][1];const f=n[1][1];let m,d,g=2;if("timeseries"===a){m="timeseries";const e={};if(Array.isArray(c))for(let r=0;r<c.length;r+=2){const s=c[r];let i=c[r+1];if("labels"===s&&Array.isArray(i)){const e={};for(const r of i)Array.isArray(r)&&2===r.length&&(e[r[0]]=r[1]);i=e}"rules"===s&&Array.isArray(i)&&(i=i.map(e=>Array.isArray(e)?{destKey:e[0],bucketDuration:e[1],aggregationType:e[2]}:e)),e[s]=i}c=Buffer.from(JSON.stringify(e)),d=e.totalSamples||0}else"json"===a?(m="json","string"==typeof c&&(c=Buffer.from(c))):(m=n[g][1],g++);"string"!==a&&"json"!==a&&"timeseries"!==a&&(d=n[g][1]);let p=null;if("string"===a&&Buffer.isBuffer(c)){const r=await e(c);r&&(p={algorithm:r.algorithm,originalSize:c.length,decompressedSize:r.decompressed.length,ratio:+(100*(1-c.length/r.decompressed.length)).toFixed(1)},c=r.decompressed)}else if("list"===a&&Array.isArray(c)){for(let r=0;r<c.length;r++)if(Buffer.isBuffer(c[r])){const s=await e(c[r]);s&&(p||(p={algorithm:s.algorithm,items:0,originalSize:0,decompressedSize:0}),p.items++,p.originalSize+=c[r].length,p.decompressedSize+=s.decompressed.length,c[r]=s.decompressed)}p&&(p.ratio=+(100*(1-p.originalSize/p.decompressedSize)).toFixed(1))}else if("hash"===a&&c&&"object"==typeof c&&!Array.isArray(c)){for(const r of Object.keys(c))if(Buffer.isBuffer(c[r])){const s=await e(c[r]);s&&(p||(p={algorithm:s.algorithm,items:0,originalSize:0,decompressedSize:0}),p.items++,p.originalSize+=c[r].length,p.decompressedSize+=s.decompressed.length,c[r]=s.decompressed)}p&&(p.ratio=+(100*(1-p.originalSize/p.decompressedSize)).toFixed(1))}else if("set"!==a&&"zset"!==a||!Array.isArray(c)){if("stream"===a&&Array.isArray(c)){for(const r of c){if(!Array.isArray(r)||!Array.isArray(r[1]))continue;const s=r[1];for(let r=0;r<s.length;r++)if(Buffer.isBuffer(s[r])){const i=await e(s[r]);i&&(p||(p={algorithm:i.algorithm,items:0,originalSize:0,decompressedSize:0}),p.items++,p.originalSize+=s[r].length,p.decompressedSize+=i.decompressed.length,s[r]=i.decompressed)}}p&&(p.ratio=+(100*(1-p.originalSize/p.decompressedSize)).toFixed(1))}}else{for(let r=0;r<c.length;r++)if(Buffer.isBuffer(c[r])){const s=await e(c[r]);s&&(p||(p={algorithm:s.algorithm,items:0,originalSize:0,decompressedSize:0}),p.items++,p.originalSize+=c[r].length,p.decompressedSize+=s.decompressed.length,c[r]=s.decompressed)}p&&(p.ratio=+(100*(1-p.originalSize/p.decompressedSize)).toFixed(1))}const h={length:d,key:o,status:"ok",type:a,valueBuffer:c,ttl:f,encoding:m,compression:p};s.emit(r.responseEvent,h)}catch(e){console.error(e),s.emit(r.responseEvent,{status:"error",error:e.message})}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default async e=>{const{socket:t,payload:o}=e;try{const s=t.p3xrs.ioredis;if(!s)return void t.emit(e.responseEvent,{status:"error",error:"Not connected to Redis"});const n=o.maxScanKeys||5e3,r=o.topN||20,a=500,i=await s.dbsize();let m="0";const u=[];do{const[e,t]=await s.scan(m,"COUNT",500);if(m=e,u.push(...t),u.length>=n)break}while("0"!==m);const c={},d={},p={},l=[];let y=0,_=0,f=0;for(let e=0;e<u.length;e+=a){const t=u.slice(e,e+a),o=s.pipeline();for(const e of t)o.type(e),o.call("MEMORY","USAGE",e),o.ttl(e);const n=await o.exec();for(let e=0;e<t.length;e++){const o=t[e],s=n[3*e][0],r=n[3*e][1]||"unknown",a=n[3*e+1][0],i=n[3*e+1][1],m=n[3*e+2][0],u=n[3*e+2][1],h=s?"unknown":r,k=a||"number"!=typeof i?0:i;c[h]=(c[h]||0)+1,d[h]=(d[h]||0)+k;const v=o.indexOf(":"),b=v>0?o.substring(0,v+1):"(no prefix)";p[b]||(p[b]={keyCount:0,totalBytes:0}),p[b].keyCount++,p[b].totalBytes+=k,m||"number"!=typeof u?_++:u>=0?(y++,f+=u):_++,k>0&&l.push({key:o,bytes:k,type:h})}}l.sort((e,t)=>t.bytes-e.bytes);const h=l.slice(0,r),k=Object.entries(p).map(([e,t])=>({prefix:e,...t})).sort((e,t)=>t.totalBytes-e.totalBytes).slice(0,50),v=await s.info("server"),b={},w={redis_version:"version",redis_mode:"mode",uptime_in_seconds:"uptime"};for(const e of v.split("\r\n")){const[t,o]=e.split(":");t&&w[t]&&(b[w[t]]="uptime_in_seconds"===t?parseInt(o)||0:o||"unknown")}b.mode||(b.mode="standalone");const g=await s.info("memory"),x={},N={used_memory:"used",used_memory_human:"usedHuman",used_memory_rss:"rss",used_memory_rss_human:"rssHuman",used_memory_peak:"peak",used_memory_peak_human:"peakHuman",used_memory_lua:"lua",used_memory_overhead:"overhead",used_memory_dataset:"dataset",mem_fragmentation_ratio:"fragRatio",mem_allocator:"allocator"};for(const e of g.split("\r\n")){const[t,o]=e.split(":");t&&N[t]&&(x[N[t]]=isNaN(o)?o:Number(o))}t.emit(e.responseEvent,{status:"ok",data:{totalScanned:u.length,dbSize:i,typeDistribution:c,typeMemory:d,prefixMemory:k,topKeys:h,expirationOverview:{withTTL:y,persistent:_,avgTTL:y>0?Math.round(f/y):0},memoryInfo:x,serverInfo:b}})}catch(o){console.error(o),t.emit(e.responseEvent,{status:"error",error:o.message})}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default async o=>{const{socket:r,payload:e}=o;try{if(e.enabled){if(!r.p3xrs.ioredisMonitor){const o=r.p3xrs.ioredis;let e;for(let r=0;r<3;r++)try{e=await o.monitor();break}catch(o){if(2===r)throw o;await new Promise(o=>setTimeout(o,200))}r.p3xrs.ioredisMonitor=e,r.p3xrs.ioredisMonitor.on("monitor",(o,e,t,i)=>{r.emit("monitor-data",{time:o,args:e,source:t,database:i})}),console.info("MONITOR started for",r.id)}}else r.p3xrs.ioredisMonitor&&(r.p3xrs.ioredisMonitor.disconnect(),r.p3xrs.ioredisMonitor=void 0,console.info("MONITOR stopped for",r.id));r.emit(o.responseEvent,{status:"ok"})}catch(e){console.error("Monitor error:",e),r.emit(o.responseEvent,{status:"error",error:e.message})}};
|
|
1
|
+
export default async o=>{const{socket:r,payload:e}=o;try{if(!r.p3xrs.ioredis)return void r.emit(o.responseEvent,{status:"error",error:"Not connected to Redis"});if(e.enabled){if(!r.p3xrs.ioredisMonitor){const o=r.p3xrs.ioredis;let e;for(let r=0;r<3;r++)try{e=await o.monitor();break}catch(o){if(2===r)throw o;await new Promise(o=>setTimeout(o,200))}r.p3xrs.ioredisMonitor=e,r.p3xrs.ioredisMonitor.on("monitor",(o,e,t,i)=>{r.emit("monitor-data",{time:o,args:e,source:t,database:i})}),console.info("MONITOR started for",r.id)}}else r.p3xrs.ioredisMonitor&&(r.p3xrs.ioredisMonitor.disconnect(),r.p3xrs.ioredisMonitor=void 0,console.info("MONITOR stopped for",r.id));r.emit(o.responseEvent,{status:"ok"})}catch(e){console.error("Monitor error:",e),r.emit(o.responseEvent,{status:"error",error:e.message})}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default async
|
|
1
|
+
export default async r=>{const{socket:s,payload:e}=r;try{if(!s.p3xrs.ioredisSubscriber)return void s.emit(r.responseEvent,{status:"error",error:"Not connected to Redis"});await s.p3xrs.ioredisSubscriber.punsubscribe(),s.p3xrs.ioredisSubscriber.removeAllListeners("pmessage"),s.p3xrs.ioredisSubscriber.removeAllListeners("pmessageBuffer"),s.p3xrs.subscription=e.subscription,"string"==typeof e.subscriberPattern&&0!==e.subscriberPattern.trim().length||(e.subscriberPattern="*"),!0===s.p3xrs.subscription&&(await s.p3xrs.ioredisSubscriber.psubscribe(e.subscriberPattern),s.p3xrs.ioredisSubscriber.on("pmessageBuffer",(r,e,t)=>{const i=e.toString("utf-8");console.log("socket.p3xrs.ioredisSubscriber.on(pmessageBuffer)",r.toString("utf-8"),i),s.emit("pubsub-message",{channel:i,message:t})})),s.emit(r.responseEvent,{status:"ok"})}catch(e){console.error("Subscription error:",e),s.emit(r.responseEvent,{status:"error",error:e.message})}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cloneDeep as e}from"lodash-es";const s=e=>{const{connectionId:s,code:t,socket:o}=e;p3xrs.redisConnections.hasOwnProperty(s)&&(delete p3xrs.redisConnections[s],o.p3xrs.io.emit("redis-disconnected",{connectionId:s,status:"code",code:t}),n({socket:o}))},n=e=>{const{socket:s}=e,n={};Object.keys(p3xrs.redisConnections).forEach(e=>{n[e]={},Object.keys(p3xrs.redisConnections[e]).forEach(s=>{n[e][s]=p3xrs.redisConnections[e][s]})}),s.p3xrs.io.emit("redis-status",{redisConnections:n})},t="socket.io shared disconnect redis",o=e=>{const{socket:s}=e;if(p3xrs.redisConnections.hasOwnProperty(s.p3xrs.connectionId))if(console.warn(t,`includes ${p3xrs.redisConnections[s.p3xrs.connectionId].clients.includes(s.id)} length === 1 ${p3xrs.redisConnections[s.p3xrs.connectionId].clients.length}`),p3xrs.redisConnections[s.p3xrs.connectionId].clients.includes(s.id)&&1===p3xrs.redisConnections[s.p3xrs.connectionId].clients.length)delete p3xrs.redisConnections[s.p3xrs.connectionId];else{let e=p3xrs.redisConnections[s.p3xrs.connectionId].clients.indexOf(s.id);console.warn(t,s.p3xrs.connectionId,p3xrs.redisConnections[s.p3xrs.connectionId].clients,s.id,e),e>-1&&p3xrs.redisConnections[s.p3xrs.connectionId].clients.splice(e,1)}p3xrs.redisConnections.hasOwnProperty(s.p3xrs.connectionId)&&p3xrs.redisConnections[s.p3xrs.connectionId].hasOwnProperty("clients")&&0===p3xrs.redisConnections[s.p3xrs.connectionId].clients.length&&delete p3xrs.redisConnections[s.p3xrs.connectionId],i(e),s.p3xrs.connectionId=void 0},r=s=>{const{socket:n}=s,t=e(p3xrs.connections);let o=t.list.map(e=>(delete e.password,delete e.tlsCrt,delete e.tlsKey,delete e.tlsCa,delete e.sshPassword,delete e.sshPrivateKey,Array.isArray(e.nodes)&&(e.nodes=e.nodes.map(e=>(delete e.password,e))),e));t.list=o,n.p3xrs.io.emit("connections",{status:"ok",connections:t})},i=e=>{const{socket:s}=e;if(console.warn("shared disconnectRedisIo","try"),void 0!==s.p3xrs.ioredis&&(console.warn("shared disconnectRedisIo","executed"),s.p3xrs.ioredis.disconnect(),s.p3xrs.ioredisSubscriber.disconnect(),s.p3xrs.ioredis=void 0,s.p3xrs.ioredisSubscriber=void 0),Array.isArray(s.p3xrs.tunnels)&&s.p3xrs.tunnels.length>0){for(const e of s.p3xrs.tunnels)e.close();s.p3xrs.tunnels=[]}s.p3xrs.sshClient&&(s.p3xrs.sshClient.end(),s.p3xrs.sshClient=void 0)},c=e=>{const{redis:s}=e;let{dbsize:n,maxKeys:t}=e;return new Promise(async(n,o)=>{try{(isNaN(t)||t<5||t>1e5)&&(t=1e4);let o=Math.round(t/10);o<5&&(o=5);const r=s.scanStream({match:e.match,count:o});let i=[],c=!1;r.on("data",e=>{t&&i.length<t?(i=i.concat(e),i.length>=t&&(c=!0,n(i))):c||(c=!0,n(i))}),r.on("end",()=>{c||n(i)})}catch(e){o(e)}})},d=async e=>{const{redis:s,keys:n}=e,t=s.pipeline();for(let e of n)t.type(e);const o=await t.exec(),r={},i=s.pipeline();for(let e in n){const s=o[
|
|
1
|
+
import{cloneDeep as e}from"lodash-es";const s=e=>{const{connectionId:s,code:t,socket:o}=e;p3xrs.redisConnections.hasOwnProperty(s)&&(delete p3xrs.redisConnections[s],o.p3xrs.io.emit("redis-disconnected",{connectionId:s,status:"code",code:t}),n({socket:o}))},n=e=>{const{socket:s}=e,n={};Object.keys(p3xrs.redisConnections).forEach(e=>{n[e]={},Object.keys(p3xrs.redisConnections[e]).forEach(s=>{n[e][s]=p3xrs.redisConnections[e][s]})}),s.p3xrs.io.emit("redis-status",{redisConnections:n})},t="socket.io shared disconnect redis",o=e=>{const{socket:s}=e;if(p3xrs.redisConnections.hasOwnProperty(s.p3xrs.connectionId))if(console.warn(t,`includes ${p3xrs.redisConnections[s.p3xrs.connectionId].clients.includes(s.id)} length === 1 ${p3xrs.redisConnections[s.p3xrs.connectionId].clients.length}`),p3xrs.redisConnections[s.p3xrs.connectionId].clients.includes(s.id)&&1===p3xrs.redisConnections[s.p3xrs.connectionId].clients.length)delete p3xrs.redisConnections[s.p3xrs.connectionId];else{let e=p3xrs.redisConnections[s.p3xrs.connectionId].clients.indexOf(s.id);console.warn(t,s.p3xrs.connectionId,p3xrs.redisConnections[s.p3xrs.connectionId].clients,s.id,e),e>-1&&p3xrs.redisConnections[s.p3xrs.connectionId].clients.splice(e,1)}p3xrs.redisConnections.hasOwnProperty(s.p3xrs.connectionId)&&p3xrs.redisConnections[s.p3xrs.connectionId].hasOwnProperty("clients")&&0===p3xrs.redisConnections[s.p3xrs.connectionId].clients.length&&delete p3xrs.redisConnections[s.p3xrs.connectionId],i(e),s.p3xrs.connectionId=void 0},r=s=>{const{socket:n}=s,t=e(p3xrs.connections);let o=t.list.map(e=>(delete e.password,delete e.tlsCrt,delete e.tlsKey,delete e.tlsCa,delete e.sshPassword,delete e.sshPrivateKey,Array.isArray(e.nodes)&&(e.nodes=e.nodes.map(e=>(delete e.password,e))),e));t.list=o,n.p3xrs.io.emit("connections",{status:"ok",connections:t})},i=e=>{const{socket:s}=e;if(console.warn("shared disconnectRedisIo","try"),void 0!==s.p3xrs.ioredis&&(console.warn("shared disconnectRedisIo","executed"),s.p3xrs.ioredis.disconnect(),s.p3xrs.ioredisSubscriber.disconnect(),s.p3xrs.ioredis=void 0,s.p3xrs.ioredisSubscriber=void 0),Array.isArray(s.p3xrs.tunnels)&&s.p3xrs.tunnels.length>0){for(const e of s.p3xrs.tunnels)e.close();s.p3xrs.tunnels=[]}s.p3xrs.sshClient&&(s.p3xrs.sshClient.end(),s.p3xrs.sshClient=void 0)},c=e=>{const{redis:s}=e;let{dbsize:n,maxKeys:t}=e;return new Promise(async(n,o)=>{try{(isNaN(t)||t<5||t>1e5)&&(t=1e4);let o=Math.round(t/10);o<5&&(o=5);const r=s.scanStream({match:e.match,count:o});let i=[],c=!1;r.on("data",e=>{t&&i.length<t?(i=i.concat(e),i.length>=t&&(c=!0,n(i))):c||(c=!0,n(i))}),r.on("end",()=>{c||n(i)})}catch(e){o(e)}})},d=async e=>{const{redis:s,keys:n}=e,t=s.pipeline();for(let e of n)t.type(e),t.ttl(e);const o=await t.exec(),r={},i=s.pipeline();for(let e in n){const s=2*e,t=o[s],c=o[s+1],d=n[e],a={type:t[1],ttl:c[1]};switch("ReJSON-RL"===a.type&&(a.type="json"),"TSDB-TYPE"===a.type&&(a.type="timeseries"),a.type){case"stream":i.xlen(d);break;case"hash":i.hlen(d);break;case"list":i.llen(d);break;case"set":i.scard(d);break;case"zset":i.zcard(d);break;case"timeseries":i.call("TS.INFO",d)}r[d]=a}const c=await i.exec();for(let e in n){const s=r[n[e]];if("string"===s.type||"none"===s.type||"json"===s.type)continue;const t=c.shift();if(void 0!==t)if("timeseries"===s.type){const e=t[1];if(Array.isArray(e))for(let n=0;n<e.length;n+=2)if("totalSamples"===e[n]){s.length=e[n+1];break}}else s.length=t[1]}return r},a=()=>{if(!0===p3xrs.cfg.readonlyConnections)throw new Error("readonly-connection-mode")},l=({socket:e})=>{if(!0===e.p3xrs.readonly)throw new Error("readonly-connection-mode")},p=async e=>{const{redis:s}=e;let{payload:n}=e;void 0===n&&(n={});const t=await s.dbsize(),o=await Promise.all([s.info(),c({dbsize:t,redis:s,match:n.match,maxKeys:n.maxKeys}),s.pubsub("channels","*")]),r=o[1];let i={};return r.length<11e4&&(i=await d({redis:s,keys:r})),{info:o[0],keys:r,keysInfo:i,dbsize:t,channels:o[2]}},x=async e=>{const{redis:s,socket:n,payload:t,setDb:o}=e;if(!0===o)try{await s.call("select",t.db||0)}catch(e){console.warn(e)}const r=await p({redis:s,payload:t});let{extend:i}=e;void 0===i&&(i={}),n.emit(e.responseEvent,Object.assign(i,{status:"ok",info:r.info,keys:r.keys,keysInfo:r.keysInfo,keysInfoFetchedAt:Date.now(),dbsize:r.dbsize}))},h=(e,s,n)=>{const t=s||/\s/g;let o=!1,r=!1,i=[];const c=[];console.log("argumentParser input",e);const d=e.split("");for(let e=0;e<d.length;++e){let a=d[e],l=a.match(t);"'"!==a||r?'"'!==a||o?o||r||!l?i.push(a):i.length>0?(c.push(i.join("")),i=[]):s&&c.push(a):(!0===n&&i.push(a),r=!r):(!0===n&&i.push(a),o=!o)}return i.length>0?c.push(i.join("")):s&&c.push(""),c},y=e=>{if(!Array.isArray(e))return[];const s=[];for(const n of e){if(!Array.isArray(n))continue;const e={};for(let s=0;s<n.length;s+=2)e[n[s]]=n[s+1];e.name&&s.push(e)}return s},f=async e=>{try{const s=await e.call("MODULE","LIST");return y(s)}catch(e){return[]}};export{h as argumentParser,a as ensureReadonlyConnections,s as triggerDisconnect,c as getStreamKeys,i as disconnectRedisIo,r as sendConnections,n as sendStatus,o as disconnectRedis,d as getKeysInfo,p as getFullInfo,x as getFullInfoAndSendSocket,l as ensureReadonlyConnection,y as parseModuleList,f as detectModules};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "p3x-redis-ui-server",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.312",
|
|
4
4
|
"description": "🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface",
|
|
5
5
|
"corifeus": {
|
|
6
6
|
"icon": "fas fa-flag-checkered",
|
|
@@ -54,14 +54,18 @@
|
|
|
54
54
|
"terser": "^5.46.1"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
+
"@msgpack/msgpack": "^3.1.3",
|
|
57
58
|
"bcryptjs": "^3.0.3",
|
|
58
59
|
"chalk": "^5.6.2",
|
|
59
60
|
"commander": "^14.0.3",
|
|
60
61
|
"corifeus-utils": "^2025.4.123",
|
|
61
62
|
"express": "^5.2.1",
|
|
63
|
+
"fzstd": "^0.1.1",
|
|
62
64
|
"groq-sdk": "^1.1.2",
|
|
63
65
|
"ioredis": "^5.10.1",
|
|
64
66
|
"lodash-es": "^4.18.1",
|
|
67
|
+
"lz4js": "^0.2.0",
|
|
68
|
+
"snappyjs": "^0.7.0",
|
|
65
69
|
"socket.io": "^4.8.3",
|
|
66
70
|
"tunnel-ssh": "^5.2.0"
|
|
67
71
|
},
|