diff --git a/README.md b/README.md index b1ed96a..2f33813 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,29 @@ TODO: ----------------------------------------------- + 更新日志: +v0.4.2 (2016-10-20) + +增加删除空闲Key的功能,配置详见config_global.php + +修正导出中的BUG + +修正因JS缺失而产生的BUG + +—————————————————— + + +v0.4.1 (2016-09-19) + +修正scan在集群模式下不可用的问题(由于官方文档缺失,暂时将集群模式读取key列表改回原来的keys命令,如有错误,请斧正) + +—————————————————— + v0.4.0 (2016-09-05) -采用scan命令读取key列表(redis >= 2.8),页面建树改用ztree (https://github.com/zTree/zTree_v3),单个节点超过10万时不建议展开(好吧,有可能展不开) +采用scan命令读取key列表(redis >= 2.8),页面建树改用ztree ( https://github.com/zTree/zTree_v3 ),单个节点超过10万时不建议展开(好吧,有可能展不开) 增加直接查看指定key的功能 diff --git a/application/config/config.php b/application/config/config.php index 32f34e9..24ab5e9 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -360,3 +360,28 @@ /* End of file config.php */ /* Location: ./application/config/config.php */ + + +//================== Readonly switch add by binyan.li +define('READONLY_SWITCH', TRUE); +//define('READONLY_SWITCH', FALSE); +$config['readonly'] = READONLY_SWITCH; +$config['deny_operater']= array( + 'none', +//== 下方为放行方法 +// 'delete', //删除键 +// 'rename', //修改键名 +// 'edit', //查看/修改键值 +// 'ttl', //存活时间查看/修改 +// 'save', //保存 +// 'import', //导入键值 +// 'clear_idle_key', +// 'export', //导出键值 +// 'idle', +// 'overview', //统计信息 +// 'info', //统计信息 +// 'view', //查看各类型数据(string/hash/set/zset/list) +// 'index', //首页 +// 'key', //列出键值 +); +//================== diff --git a/application/config/config_auth.php b/application/config/config_auth.php index ed05b54..edaac3b 100644 --- a/application/config/config_auth.php +++ b/application/config/config_auth.php @@ -21,7 +21,7 @@ /* * 验证码语言 en=英文 cn=中文 */ -$config['seccode_lang'] = 'en'; +$config['seccode_lang'] = 'cn'; /* * 验证码字数 默认为4, diff --git a/application/config/config_global.php b/application/config/config_global.php index ef1f6cb..643be02 100644 --- a/application/config/config_global.php +++ b/application/config/config_global.php @@ -1,7 +1,11 @@ _idle_key = get_custom_config('config_global', 'idle_key'); + } + + public function index() + { + $db_size = $this -> redis_model -> get_db_size(); + + $page_data = $this -> get_default_page_data(); + $page_data['db_size'] = $db_size; + $page_data['title'] = '检索/删除 空闲key'; + + $this -> load -> view('clear_idle_key', $page_data); + } + + public function view_idle_key_list() + { + $iterator = get_arg('iterator', -1, 'intval'); + $idle_time = get_arg('idle_time', 0, 'intval'); + $key_prefix = get_arg('key_prefix', '', 'trim'); + if ( $idle_time < 5 ) { + show_message(1, sprintf('设置的时间[%d]过短,空闲时间不建议小于5秒', $idle_time)); + + } + $iterator = ($iterator === -1) ? NULL : $iterator; + $sub_key = ''; + if ( $key_prefix != '' ) { + $sub_key = $key_prefix . '*'; + } + $result = $this -> redis_model -> scan_keys($sub_key, $iterator, TRUE, IDLE_KEY_PAGE_SIZE); + $redis_keys = $result['keys']; + $new_iterator = $result['iterator']; + $ret_arr = array(); + if ( ! empty($redis_keys) ) { + $redis = $this -> redis_model -> get_redis_instance(); + foreach($redis_keys as $key) { + $t = $redis -> object('idletime', $key); + if ( $t >= $idle_time ) { + $ret_arr[] = array( + 'k' => htmlspecialchars($key), + 't' => $t, + ); + } + } + } + show_message(0, '', array( + 'iterator' => $new_iterator, + 'idle_keys' => $ret_arr, + )); + } + + public function clear_keys() + { + $iterator = get_arg('iterator', -1, 'intval'); + $idle_time = get_arg('idle_time', 0, 'intval'); + $key_prefix = get_arg('key_prefix', '', 'trim'); + if ( $idle_time < 5 ) { + show_message(1, sprintf('设置的时间[%d]过短,空闲时间不建议小于5秒', $idle_time)); + + } + $iterator = ($iterator === -1) ? NULL : $iterator; + $sub_key = ''; + if ( $key_prefix != '' ) { + $sub_key = $key_prefix . '*'; + } + $result = $this -> redis_model -> scan_keys($sub_key, $iterator, TRUE, IDLE_KEY_PAGE_SIZE); + $redis_keys = $result['keys']; + $new_iterator = $result['iterator']; + $ret_arr = array(); + $affected = 0; + if ( ! empty($redis_keys) ) { + $redis = $this -> redis_model -> get_redis_instance(); + foreach($redis_keys as $key) { + $t = $redis -> object('idletime', $key); + if ( $t >= $idle_time ) { + $redis -> del($key); + $affected += 1; + } + } + } + show_message(0, '', array( + 'iterator' => $new_iterator, + 'affected' => $affected, + )); + } +} diff --git a/application/controllers/delete.php b/application/controllers/delete.php index 44e362e..4fde138 100644 --- a/application/controllers/delete.php +++ b/application/controllers/delete.php @@ -16,6 +16,13 @@ public function __construct() public function index() { $key = get_arg('key'); + + if(strpos($key,"\\")){ + $key_raw = urldecode(strtr($key, array('\x'=>'%'))); + } else { + $key_raw = $key; + } + $type = strtolower( get_arg('type') ); $allow_type = array('' ,'string', 'hash', 'list', 'set', 'zset'); @@ -24,7 +31,7 @@ public function index() && ( $key !== '' ) && ( in_array($type, $allow_type) ) ){ - $this -> _delete_key($type, $key); + $this -> _delete_key($type, $key_raw); $url = manager_site_url('view', 'index', 'key=' . urlencode($key)); die($url); @@ -45,7 +52,7 @@ private function _delete_tree($tree) $tree .= '*'; $keys = $redis -> keys($tree); foreach($keys as $key) { - $redis -> delete($key); + $redis -> del($key); } } @@ -55,7 +62,7 @@ private function _delete_key($type, $key) switch($type) { default: //如果传空,即是整key删除 case 'string': - $redis -> delete($key); + $redis -> del($key); break; @@ -91,7 +98,7 @@ private function _delete_key($type, $key) case 'zset': $value = get_arg('value'); if ( $value !== NULL ){ - $redis -> zDelete($key, $value); + $redis -> zRem($key, $value); } break; } diff --git a/application/controllers/edit.php b/application/controllers/edit.php index e11cdee..1e51733 100644 --- a/application/controllers/edit.php +++ b/application/controllers/edit.php @@ -133,7 +133,7 @@ private function _do_index() } elseif ( $type == 'list' ) { //list - $size = $redis -> lSize($key); + $size = $redis -> lLen($key); $index = get_post_arg('index', NULL, 'trim'); ($index === NULL) && ($index = ''); @@ -174,7 +174,7 @@ private function _do_index() if ( ( $value != $old_value ) || ( $score != $old_score ) ){ - $redis -> zDelete($key, $old_value); + $redis -> zRem($key, $old_value); $redis -> zAdd($key, $score, $value); } diff --git a/application/controllers/export.php b/application/controllers/export.php index 5bfebc0..136ef2c 100644 --- a/application/controllers/export.php +++ b/application/controllers/export.php @@ -118,38 +118,44 @@ private function _export_redis($key) // Hash else if ($type == 'hash') { $values = $redis -> hGetAll($key); - + $result = array(); foreach ($values as $k => $v) { - return 'HSET "' . addslashes($key) . '" "' . addslashes($k) . '" "' . addslashes($v) . '"'; + $result[] = 'HSET "' . addslashes($key) . '" "' . addslashes($k) . '" "' . addslashes($v) . '"'; } + return implode(PHP_EOL, $result); } // List else if ($type == 'list') { - $size = $redis -> lSize($key); - - for ($i = 0; $i < $size; ++$i) { - return 'RPUSH "' . addslashes($key) . '" "' . addslashes($redis -> lGet($key . $i)) . '"'; + $size = $redis -> lLen($key); + $result = array(); + for ($i = 0; $i < $size; $i++) { + $result[] = 'RPUSH "' . addslashes($key) . '" "' . addslashes($redis -> lGet($key, $i)) . '"'; } + return implode(PHP_EOL, $result); } // Set else if ($type == 'set') { $values = $redis -> sMembers($key); + $result = array(); foreach ($values as $v) { - return 'SADD "' . addslashes($key) . '" "' . addslashes($v) . '"'; + $result[] = 'SADD "' . addslashes($key) . '" "' . addslashes($v) . '"'; } + return implode(PHP_EOL, $result); } // ZSet else if ($type == 'zset') { $values = $redis -> zRange($key, 0, -1); + $result = array(); foreach ($values as $v) { $s = $redis -> zScore($key, $v); - return 'ZADD "' . addslashes($key) . '" ' . $s . ' "' . addslashes($v) . '"'; + $result[] = 'ZADD "' . addslashes($key) . '" ' . $s . ' "' . addslashes($v) . '"'; } + return implode(PHP_EOL, $result); } } @@ -186,7 +192,7 @@ private function _export_json($key) // List else if ($type == 'list') { - $size = $redis -> lSize($key); + $size = $redis -> lLen($key); $value = array(); for ($i = 0; $i < $size; ++$i) { diff --git a/application/controllers/index.php b/application/controllers/index.php index a9641b3..7c34741 100644 --- a/application/controllers/index.php +++ b/application/controllers/index.php @@ -100,5 +100,11 @@ public function idle() $this -> index(); } + public function clear_idle_key() + { + $this -> _current_method = 'clear_idle_key'; + $this -> index(); + } + } diff --git a/application/controllers/overview.php b/application/controllers/overview.php index 909718e..2612671 100644 --- a/application/controllers/overview.php +++ b/application/controllers/overview.php @@ -57,6 +57,7 @@ private function _overview_all() $can_connect = FALSE; try { $can_connect = $redis -> connect($server['host'], $server['port'], 0.5); + if($server['auth']) $redis -> auth($server['auth']); } catch (Exception $e) { $can_connect = TRUE; } diff --git a/application/controllers/rename.php b/application/controllers/rename.php index f41f253..4585748 100644 --- a/application/controllers/rename.php +++ b/application/controllers/rename.php @@ -25,14 +25,20 @@ public function index() show_error('没有找到参数Key'); } + if(strpos($key,"\\")){ + $key_raw = urldecode(strtr($key, array('\x'=>'%'))); + } else { + $key_raw = $key; + } + $redis = $this -> redis_model -> get_redis_instance(); - $key_exists = $redis -> exists($key); + $key_exists = $redis -> exists($key_raw); if ( ! $key_exists ) { show_error('Key[' . $key . ']不存在'); } $page_data = $this -> get_default_page_data(); - $page_data['key'] = $key; + $page_data['key'] = $key_raw; $page_data['title'] = '重命名Key[' . $key . ']'; $this -> load -> view('rename', $page_data); @@ -49,17 +55,23 @@ private function _do_index() show_error('没有找到新键名参数key'); } + if(strpos($old_key,"\\")){ + $old_key_raw = urldecode(strtr($old_key, array('\x'=>'%'))); + } else { + $old_key_raw = $old_key; + } + if ( strlen($key) > MAX_KEY_LEN ) { show_error('Key长度[' . strlen($key) . ']超过限制,当前限制为[' . MAX_KEY_LEN . ']'); } $redis = $this -> redis_model -> get_redis_instance(); - $key_exists = $redis -> exists($old_key); + $key_exists = $redis -> exists($old_key_raw); if ( ! $key_exists ) { show_error('Key[' . $old_key . ']不存在'); } - $redis -> rename($old_key, $key); + $redis -> rename($old_key_raw, $key); $url = manager_site_url('view', 'index', 'key=' . urlencode($key)); Header('Location:' . $url); diff --git a/application/controllers/ttl.php b/application/controllers/ttl.php index 48957fd..83dcadb 100644 --- a/application/controllers/ttl.php +++ b/application/controllers/ttl.php @@ -26,8 +26,14 @@ public function index() show_error('没有找到参数Key'); } + if(strpos($key,"\\")){ + $key_raw = urldecode(strtr($key, array('\x'=>'%'))); + } else { + $key_raw = $key; + } + $redis = $this -> redis_model -> get_redis_instance(); - $key_exists = $redis -> exists($key); + $key_exists = $redis -> exists($key_raw); if ( ! $key_exists ) { show_error('Key[' . $key . ']不存在'); } @@ -48,9 +54,15 @@ private function _do_index() { $key = get_post_arg('key'); + if(strpos($key,"\\")){ + $key_raw = urldecode(strtr($key, array('\x'=>'%'))); + } else { + $key_raw = $key; + } + $redis = $this -> redis_model -> get_redis_instance(); - $key_exists = $redis -> exists($key); + $key_exists = $redis -> exists($key_raw); if ( ! $key_exists ) { show_error('Key[' . $key . ']不存在'); @@ -58,9 +70,9 @@ private function _do_index() $ttl = get_post_arg('ttl'); if ( $ttl == '-1' ) { - $redis -> persist($key); + $redis -> persist($key_raw); } else { - $redis -> expire($key, $ttl); + $redis -> expire($key_raw, $ttl); } $url = manager_site_url('view', 'index', 'key=' . urlencode($key)); diff --git a/application/controllers/view.php b/application/controllers/view.php index baed2a1..6c14168 100644 --- a/application/controllers/view.php +++ b/application/controllers/view.php @@ -20,13 +20,19 @@ public function index() if ($key === NULL) { show_error('没有找到参数Key'); } - + + if(strpos($key,"\\")){ + $key_raw = urldecode(strtr($key, array('\x'=>'%'))); + } else { + $key_raw = $key; + } + $redis = $this -> redis_model -> get_redis_instance(); $page_data = $this -> get_default_page_data(); - $key_type = $this -> redis_model -> get_key_type($key); - $key_exists = $redis -> exists($key); + $key_type = $this -> redis_model -> get_key_type($key_raw); + $key_exists = $redis -> exists($key_raw); $page_data['exists'] = $key_exists; $page_data['key'] = $key; @@ -37,36 +43,36 @@ public function index() $redis_types = $this -> redis_model -> get_redis_types(); $type = $redis_types[$key_type]; - $ttl = $redis -> ttl($key); - $encoding = $redis -> object('encoding', $key); + $ttl = $redis -> ttl($key_raw); + $encoding = $redis -> object('encoding', $key_raw); switch ($type) { case 'string': - $values = $redis -> get($key); + $values = $redis -> get($key_raw); $size = strlen($values); $template = 'view_string'; break; case 'hash': - $values = $redis -> hGetAll($key); + $values = $redis -> hGetAll($key_raw); $size = count($values); $template = 'view_hash'; break; case 'list': - $size = $redis -> lSize($key); - $values = $redis -> lRange($key, 0, -1); + $size = $redis -> lLen($key_raw); + $values = $redis -> lRange($key_raw, 0, -1); $template = 'view_list'; break; case 'set': - $values = $redis -> sMembers($key); + $values = $redis -> sMembers($key_raw); $size = count($values); $template = 'view_set'; break; case 'zset': - $values = $redis -> zRange($key, 0, -1, 1); + $values = $redis -> zRange($key_raw, 0, -1, 1); $size = count($values); $template = 'view_zset'; break; diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index 423a7ec..b46c9d1 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -45,6 +45,36 @@ public function __construct(){ $this -> _auth_check(); } } + + //=================增加只读判断 add by binyan.li + if( config_item('readonly') ){ + $stopLabel= FALSE; + $deny_operater= config_item('deny_operater'); + if( is_array($deny_operater) && count($deny_operater)>0 ){ + if( in_array(get_arg('c'), $deny_operater) ){ + $stopLabel= TRUE; + //die("当前开启了只读功能,不允许进行此操作。"); + } + } else { + $stopLabel= TRUE; + //die("未定义禁止操作列表。"); + } + + if($stopLabel==TRUE){ + $key = get_arg('key'); + $tree = get_arg('tree'); + if ( ( $key !== NULL ) && ( $key !== '' ) ){ + $url = manager_site_url('view', 'index', 'key=' . urlencode($key)); + die($url); + + } elseif ( ( $tree !== NULL ) && ( $this -> is_post() ) ){ + $url = manager_site_url('index', 'overview'); + die($url); + } + die('抱歉,该操作不被允许!'); + } + } + //=================== /* * 服务器列表 @@ -85,6 +115,11 @@ public function __construct(){ * 异步加载建树时,一次读取的key的数量 */ define('TREE_KEY_PAGE_SIZE', get_custom_config('config_global', 'tree_key_page_size')); + + /* + * 异步查找空闲Key时,一次读取的key的数量 + */ + define('IDLE_KEY_PAGE_SIZE', get_custom_config('config_global', 'idle_key_page_size')); define('PROJECT_NAME', get_custom_config('config_global', 'project_name')); define('VERSION', get_custom_config('config_global', 'version')); diff --git a/application/helpers/common_helper.php b/application/helpers/common_helper.php index 1b39682..eca905f 100644 --- a/application/helpers/common_helper.php +++ b/application/helpers/common_helper.php @@ -20,9 +20,9 @@ function show_message($result, $msg = '', $data = array()) header( 'Content-type: text/javascript; charset=UTF-8' ); if (isset( $_REQUEST['callback'] ) ) { - $response = htmlspecialchars( $_REQUEST['callback'] ).'('.json_encode( $ret_arr ).');'; + $response = htmlspecialchars( $_REQUEST['callback'] ).'('.json_encode( $ret_arr, JSON_UNESCAPED_UNICODE ).');'; } else { - $response = json_encode( $ret_arr ); + $response = json_encode( $ret_arr, JSON_UNESCAPED_UNICODE); } echo $response; exit; @@ -103,7 +103,11 @@ function get_custom_config($config_file, $config_item) if (! function_exists('format_html') ) { function format_html($str) { - return htmlentities($str, ENT_COMPAT, 'UTF-8'); + if ( version_compare(PHP_VERSION, '5.4.0', 'ge') ) { + return htmlspecialchars($str, ENT_SUBSTITUTE + ENT_QUOTES); + } else { + return htmlspecialchars($str, ENT_QUOTES); + } } } diff --git a/application/models/redis_model.php b/application/models/redis_model.php index aa9d747..133c983 100644 --- a/application/models/redis_model.php +++ b/application/models/redis_model.php @@ -8,6 +8,10 @@ class Redis_Model extends CI_Model { private $_port; private $_db; private $_auth; + + private $_config; + + private $_is_cluster; public function __construct() { @@ -28,17 +32,21 @@ public function init($redis_config) ){ show_error('Redis Config Error'); } - + + $this -> _is_cluster = FALSE; + if ( isset($redis_config['host']) ) { return $this -> _init_redis($redis_config); } elseif ( ( isset($redis_config['cluster_list']) ) && ( is_array($redis_config['cluster_list']) ) ){ $this -> _init_cluster($redis_config); + $this -> _is_cluster = TRUE; } else{ show_error('Redis Config Error'); } + $this -> _config = $redis_config; } private function _init_cluster($redis_config) @@ -153,7 +161,9 @@ public function get_keys_count() */ public function get_all_keys($prefix = '', $need_sort = TRUE) { - if ( method_exists($this -> _redis, 'scan') ) { + if ( ( method_exists($this -> _redis, 'scan') ) + && ( ! $this -> _is_cluster ) + ){ return $this -> get_all_keys_by_scan($prefix, $need_sort); } @@ -185,13 +195,23 @@ public function get_all_keys_by_scan($prefix = '', $need_sort = TRUE) return $keys; } - public function scan_keys($prefix, $iterator, $need_sort = TRUE) + public function scan_keys($prefix, $iterator, $need_sort = TRUE, $page_size = TREE_KEY_PAGE_SIZE) { $keys = array(); - $scan_prefix = empty($prefix) ? NULL : $prefix . '*'; $this -> _redis -> setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); - $keys = $this -> _redis -> scan($iterator, $scan_prefix, TREE_KEY_PAGE_SIZE); - + if ( ! $this -> _is_cluster ) { + $scan_prefix = empty($prefix) ? NULL : $prefix . '*'; + $keys = $this -> _redis -> scan($iterator, $scan_prefix, $page_size); + } else { + $scan_prefix = empty($prefix) ? '*' : $prefix . '*'; + $keys = $this -> _redis -> keys($scan_prefix); + $iterator = 0; + } + foreach($keys as $key => $value) { + if(!mb_check_encoding($value, 'UTF-8') || strpos($value, "%") !== false) { + $keys[$key]=strtr(urlencode($value), array('%3A'=>':', '%3a'=>':', ' %'=>'\x', ' '=>'\x20')); + } + } if ( $need_sort ) { sort($keys); } diff --git a/application/views/clear_idle_key.php b/application/views/clear_idle_key.php new file mode 100644 index 0000000..c6f55a8 --- /dev/null +++ b/application/views/clear_idle_key.php @@ -0,0 +1,145 @@ + +

+ +

+
+
+ 当前db的Key总数为: + = 100000 ) { ?> + 数据较多,生成报表会较久 + +
+
+
+
+ 空闲时间(秒): + + 1天[86400] 3天[259200] 7天[604800] 30天[2592000] +
+
+ Key前缀过滤: + +
+
+ + + +
+
+

+
+ + + + + + + + + + + + + + diff --git a/application/views/index.php b/application/views/index.php index 2c28e82..f5b0f28 100644 --- a/application/views/index.php +++ b/application/views/index.php @@ -1,10 +1,17 @@ - +