From 0f80487badf6e2380027cc07a3d82e2860aa651b Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 6 Dec 2022 18:04:51 +0000 Subject: [PATCH] feat: independent migration route from plugin active method --- Access_Core.php | 4 + Access_Migration.php | 140 ++++++++++++++++++++++++++++++++ Action.php | 21 +++++ Plugin.php | 45 +--------- page/console.js | 13 +++ page/console.php | 2 + page/routes/migration/index.css | 7 ++ page/routes/migration/index.js | 36 ++++++++ page/routes/migration/index.php | 17 ++++ 9 files changed, 241 insertions(+), 44 deletions(-) create mode 100644 Access_Migration.php create mode 100644 page/console.js create mode 100644 page/routes/migration/index.css create mode 100644 page/routes/migration/index.js create mode 100644 page/routes/migration/index.php diff --git a/Access_Core.php b/Access_Core.php index 52f91de..00b2113 100644 --- a/Access_Core.php +++ b/Access_Core.php @@ -43,6 +43,10 @@ class Access_Core $ipdbPath = dirname(__file__).'/lib/ipipfree.ipdb'; $this->ipdb = new Access_IpDb($ipdbPath); switch ($this->request->get('action')) { + case 'migration': + $this->action = 'migration'; + $this->title = _t('数据迁移'); + break; case 'logs': $this->action = 'logs'; $this->title = _t('访问日志'); diff --git a/Access_Migration.php b/Access_Migration.php new file mode 100644 index 0000000..bf436c2 --- /dev/null +++ b/Access_Migration.php @@ -0,0 +1,140 @@ + 'GET', + 'migrate' => 'POST', + ]; + + private $config; + private $db; + private $request; + + public function __construct($request) { + $this->db = Typecho_Db::get(); + $this->request = $request; + $this->config = Typecho_Widget::widget('Widget_Options')->plugin('Access'); + } + + /** + * 显示旧版本数据数量 + * + * @access private + * @return ?array + * @throws Exception + */ + private function overview() + { + $resp = [ 'total' => 0 ]; + $prefix = $this->db->getPrefix(); + // 统计 v1 版本数据 + if ($this->db->fetchRow($this->db->query("SHOW TABLES LIKE '{$prefix}access';", Typecho_Db::READ))) { + $resp['v1'] = $this->db->fetchRow($this->db->select('COUNT(1) AS cnt')->from('table.access'))['cnt']; + $resp['total'] += $resp['v1']; + } + // 统计 v2 版本数据 + if ($this->db->fetchRow($this->db->query("SHOW TABLES LIKE '{$prefix}access_log';", Typecho_Db::READ))) { + $resp['v2'] = intval($this->db->fetchRow($this->db->select('COUNT(1) AS cnt')->from('table.access_log'))['cnt']); + $resp['total'] += $resp['v2']; + } + return $resp; + } + + /** + * 迁移旧版本数据,单次1000条 + * + * @access private + * @return ?array + * @throws Exception + */ + private function migrate() + { + $resp = [ 'count' => 0, 'remain' => 0 ]; + $step = 1000; + $prefix = $this->db->getPrefix(); + // 迁移 v1 版本数据 + if ($this->db->fetchRow($this->db->query("SHOW TABLES LIKE '{$prefix}access';", Typecho_Db::READ))) { + $remain = intval($this->db->fetchRow($this->db->select('COUNT(1) AS cnt')->from('table.access'))['cnt']); + if ($resp['count'] === 0) { + $rows = $this->db->fetchAll($this->db->select()->from('table.access')->limit($step)); + foreach ($rows as $row) { + $id = $row['id']; + unset($row['id']); + $ua = new Access_UA($row['ua']); + $row['browser_id' ] = $ua->getBrowserID(); + $row['browser_version' ] = $ua->getBrowserVersion(); + $row['os_id' ] = $ua->getOSID(); + $row['os_version' ] = $ua->getOSVersion(); + $row['path' ] = parse_url($row['url'], PHP_URL_PATH); + $row['query_string' ] = parse_url($row['url'], PHP_URL_QUERY); + $row['ip' ] = $row['ip']; + $row['entrypoint' ] = $row['referer']; + $row['entrypoint_domain'] = $row['referer_domain']; + $row['time' ] = $row['date']; + $row['robot' ] = $ua->isRobot() ? 1 : 0; + $row['robot_id' ] = $ua->getRobotID(); + $row['robot_version' ] = $ua->getRobotVersion(); + unset($row['date']); + $this->db->query($this->db->insert('table.access_logs')->rows($row)); + $resp['count'] += 1; + $this->db->query($this->db->delete('table.access')->where('id = ?', $id)); + $remain -= 1; + } + } + if ($remain === 0) { + $this->db->query("DROP TABLE `{$prefix}access`;", Typecho_Db::WRITE); + } + $resp['remain'] += $remain; + } + // 迁移 v2 版本数据 + if ($this->db->fetchRow($this->db->query("SHOW TABLES LIKE '{$prefix}access_log';", Typecho_Db::READ))) { + $remain = intval($this->db->fetchRow($this->db->select('COUNT(1) AS cnt')->from('table.access_log'))['cnt']); + if ($resp['count'] === 0) { + $rows = $this->db->fetchAll($this->db->select()->from('table.access_log')->limit($step)); + foreach ($rows as $row) { + $id = $row['id']; + unset($row['id']); + $row['ip'] = long2ip($row['ip']); + $this->db->query($this->db->insert('table.access_logs')->rows($row)); + $resp['count'] += 1; + $this->db->query($this->db->delete('table.access_log')->where('id = ?', $id)); + $remain -= 1; + } + } + if ($remain === 0) { + $this->db->query("DROP TABLE `{$prefix}access_log`;", Typecho_Db::WRITE); + } + $resp['remain'] += $remain; + } + return $resp; + } + + /** + * 业务调度入口 + * + * @access public + * @param string rpcType 调用过程类型 + * @return ?array + * @throws Exception + */ + public function invoke(string $rpcType): ?array { + if(!method_exists($this, $rpcType) || !array_key_exists($rpcType, Access_Migration::$rpcTypes)) + throw new Exception('Bad Request', 400); + $method = Access_Migration::$rpcTypes[$rpcType]; + if ( + ($method === 'GET' && !$this->request->isGet()) + || ($method === 'POST' && !$this->request->isPost()) + || ($method === 'PUT' && !$this->request->isPut()) + ) { + throw new Exception('Method Not Allowed', 405); + } + return $this->$rpcType(); + } +} diff --git a/Action.php b/Action.php index ac93fdf..180f2e6 100644 --- a/Action.php +++ b/Action.php @@ -35,6 +35,27 @@ class Access_Action extends Typecho_Widget implements Widget_Interface_Do } } + public function migration() { + try { + $this->checkAuth(); # 鉴权 + $rpcType = $this->request->get('rpc'); # 业务类型 + $logs = new Access_Migration($this->request); + $data = $logs->invoke($rpcType); # 进行业务分发并调取数据 + $errCode = 0; + $errMsg = 'ok'; + } catch (Exception $e) { + $data = null; + $errCode = $e->getCode(); + $errMsg = $e->getMessage(); + } + + $this->response->throwJson([ + 'code' => $errCode, + 'message' => $errMsg, + 'data' => $data + ]); + } + public function logs() { try { $this->checkAuth(); # 鉴权 diff --git a/Plugin.php b/Plugin.php index e4e75b3..b1db57e 100644 --- a/Plugin.php +++ b/Plugin.php @@ -24,6 +24,7 @@ class Access_Plugin implements Typecho_Plugin_Interface $msg = Access_Plugin::install(); Helper::addPanel(1, self::$panel, _t('Access控制台'), _t('Access插件控制台'), 'subscriber'); Helper::addRoute("access_track_gif", "/access/log/track.gif", "Access_Action", 'writeLogs'); + Helper::addRoute("access_migration", "/access/migration", "Access_Action", 'migration'); Helper::addRoute("access_logs", "/access/logs", "Access_Action", 'logs'); Helper::addRoute('access_statistic_view', '/access/statistic/view', 'Access_Action', 'statistic'); Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('Access_Plugin', 'backend'); @@ -127,50 +128,6 @@ class Access_Plugin implements Typecho_Plugin_Interface } $msg = _t('成功创建数据表,插件启用成功,') . $configLink; } - # 处理旧版本数据 - if ($db->fetchRow($db->query("SHOW TABLES LIKE '{$prefix}access';", Typecho_Db::READ))) { - $rows = $db->fetchAll($db->select()->from('table.access')); - foreach ($rows as $row) { - $ua = new Access_UA($row['ua']); - $time = Helper::options()->gmtTime + (Helper::options()->timezone - Helper::options()->serverTimezone); - $row['browser_id' ] = $ua->getBrowserID(); - $row['browser_version' ] = $ua->getBrowserVersion(); - $row['os_id' ] = $ua->getOSID(); - $row['os_version' ] = $ua->getOSVersion(); - $row['path' ] = parse_url($row['url'], PHP_URL_PATH); - $row['query_string' ] = parse_url($row['url'], PHP_URL_QUERY); - $row['ip' ] = $row['ip']; - $row['entrypoint' ] = $row['referer']; - $row['entrypoint_domain'] = $row['referer_domain']; - $row['time' ] = $row['date']; - $row['robot' ] = $ua->isRobot() ? 1 : 0; - $row['robot_id' ] = $ua->getRobotID(); - $row['robot_version' ] = $ua->getRobotVersion(); - unset($row['date']); - try { - $db->query($db->insert('table.access_logs')->rows($row)); - } catch (Typecho_Db_Exception $e) { - if ($e->getCode() != 23000) - throw new Typecho_Plugin_Exception(_t('导入旧版数据失败,插件启用失败,错误信息:%s。', $e->getMessage())); - } - } - $db->query("DROP TABLE `{$prefix}access`;", Typecho_Db::WRITE); - $msg = _t('成功创建数据表并更新数据,插件启用成功,') . $configLink; - } - if ($db->fetchRow($db->query("SHOW TABLES LIKE '{$prefix}access_log';", Typecho_Db::READ))) { - $rows = $db->fetchAll($db->select()->from('table.access_log')); - foreach ($rows as $row) { - $row['ip'] = long2ip($row['ip']); - try { - $db->query($db->insert('table.access_logs')->rows($row)); - } catch (Typecho_Db_Exception $e) { - if ($e->getCode() != 23000) - throw new Typecho_Plugin_Exception(_t('导入旧版数据失败,插件启用失败,错误信息:%s。', $e->getMessage())); - } - } - $db->query("DROP TABLE `{$prefix}access_log`;", Typecho_Db::WRITE); - $msg = _t('成功创建数据表并更新数据,插件启用成功,') . $configLink; - } return $msg; } catch (Typecho_Db_Exception $e) { throw new Typecho_Plugin_Exception(_t('数据表建立失败,插件启用失败,错误信息:%s。', $e->getMessage())); diff --git a/page/console.js b/page/console.js new file mode 100644 index 0000000..d255e39 --- /dev/null +++ b/page/console.js @@ -0,0 +1,13 @@ +$().ready(function () { + $.ajax({ + url: "/access/migration", + method: "get", + dataType: "json", + data: { rpc: 'overview' }, + success: function (res) { + if (res.data.total > 0) { + $('#migration-tab-li').show(); + } + }, + }); +}); diff --git a/page/console.php b/page/console.php index 9345d20..f7c7bc2 100644 --- a/page/console.php +++ b/page/console.php @@ -5,6 +5,7 @@ include 'menu.php'; require_once __DIR__ . '/../Access_Bootstrap.php'; $access = new Access_Core(); ?> +
@@ -15,6 +16,7 @@ $access = new Access_Core();
    action == 'overview' ? ' class="current"' : '')?>> action == 'logs' ? ' class="current"' : '')?>> + action == 'migration' ? ' class="current"' : '')?> style="display: none" id="migration-tab-li">
diff --git a/page/routes/migration/index.css b/page/routes/migration/index.css new file mode 100644 index 0000000..8ad3f08 --- /dev/null +++ b/page/routes/migration/index.css @@ -0,0 +1,7 @@ +.typecho-access-migration-main { + text-align: center; +} + +.typecho-access-migration-summary { + margin: 20px 0; +} diff --git a/page/routes/migration/index.js b/page/routes/migration/index.js new file mode 100644 index 0000000..00c44cc --- /dev/null +++ b/page/routes/migration/index.js @@ -0,0 +1,36 @@ +$(document).ready(function () { + function start() { + $('body').loadingModal({ text: '正在准备迁移...', backgroundColor: '#292d33' }).show(); + $.ajax({ + url: "/access/migration", + method: "post", + dataType: "json", + data: { rpc: 'migrate' }, + success: function (res) { + if (res.code === 0) { + if (res.data.remain === 0) { + $('body').loadingModal('hide'); + } else { + $('body').loadingModal('text', '迁移中,剩余' + res.data.remain + '条等待迁移...'); + start(); + } + $('#ancient-logs-count').text(res.data.remain); + } + }, + }); + } + + $('[data-action="migrate"]').click(function() { + start(); + }); + + $.ajax({ + url: "/access/migration", + method: "get", + dataType: "json", + data: { rpc: 'overview' }, + success: function (res) { + $('#ancient-logs-count').text(res.data.total); + }, + }); +}); diff --git a/page/routes/migration/index.php b/page/routes/migration/index.php new file mode 100644 index 0000000..9982f4a --- /dev/null +++ b/page/routes/migration/index.php @@ -0,0 +1,17 @@ + + + + + + +
+
+
+ 存在历史数据: + loading + +
+ + +
+