diff --git a/server/www/teleport/static/js/maintenance/upgrade.js b/server/www/teleport/static/js/maintenance/upgrade.js new file mode 100644 index 0000000..8d8e4bb --- /dev/null +++ b/server/www/teleport/static/js/maintenance/upgrade.js @@ -0,0 +1,317 @@ +"use strict"; + +$app.on_init = function (cb_stack, cb_args) { + $app.dom = { + btn_upgrade: $('#btn-upgrade'), + steps_detail: $('#steps-detail'), + // db_info: $('#db-info'), + // account: $('#sysadmin-account'), + // email: $('#sysadmin-email'), + // password: $('#password'), + // password2: $('#password-again'), + message: $('#message'), + step2: $('#step2') + }; + + $app._make_info = function (key, value) { + return '' + key + ':' + value + ''; + }; + + // var html = []; + // if ($app.options.db.type === DB_TYPE_SQLITE) { + // html.push($app._make_info('数据库类型', 'SQLite')); + // html.push($app._make_info('数据库文件', $app.options.db.sqlite_file)); + // } else if ($app.options.db.type === DB_TYPE_MYSQL) { + // html.push($app._make_info('数据库类型', 'MySQL')); + // html.push($app._make_info('MySQL主机', $app.options.db.mysql_host)); + // html.push($app._make_info('MySQL端口', $app.options.db.mysql_port)); + // html.push($app._make_info('数据库名称', $app.options.db.mysql_db)); + // html.push($app._make_info('用户名', $app.options.db.mysql_user)); + // + // var _t = []; + // _t.push('
'); + // _t.push(' 注意:请确保您在执行后续创建操作之前,已经在MySQL中使用 UTF8字符集 创建了库“'); + // _t.push($app.options.db.mysql_db); + // _t.push('”,并且用户“'); + // _t.push($app.options.db.mysql_user); + // _t.push('”拥有在此库创建表的权限!'); + // _t.push('
'); + // $app.dom.db_info.after(_t.join('')); + // } else { + // html.push($app._make_info('数据库类型', '未知的数据库类型,请检查您的配置文件!')); + // $app.dom.btn_upgrade.attr('disabled', 'disabled').hide(); + // } + // $app.dom.db_info.append(html.join('')); + + $app.hide_op_box = function () { + $app.dom.message.hide(); + }; + + $app.show_op_box = function (op_type, op_msg) { + $app.dom.message.html(op_msg); + $app.dom.message.removeClass().addClass('op_box op_' + op_type); + $app.dom.message.show(); + }; + + $app.dom.btn_upgrade.click(function () { + // var str_account = $app.dom.account.val(); + // var str_email = $app.dom.email.val(); + // var str_password = $app.dom.password.val(); + // var str_password2 = $app.dom.password2.val(); + // + // if (str_account.length === 0) { + // $app.show_op_box('error', '请填写系统管理员登录账号名称!'); + // $app.dom.account.focus(); + // return; + // } + // if (str_email.length === 0) { + // $app.show_op_box('error', '请填写系统管理员的电子邮件地址!'); + // $app.dom.email.focus(); + // return; + // } + // if (!tp_is_email(str_email)) { + // $app.show_op_box('error', '电子邮件地址格式错啦,你会收不到邮件的!'); + // $app.dom.email.focus(); + // return; + // } + // if (str_password.length === 0) { + // $app.show_op_box('error', '请设置系统管理员登录密码!'); + // $app.dom.password.focus(); + // return; + // } + // if (str_password2.length === 0) { + // $app.show_op_box('error', '请再次输入系统管理员登录密码!'); + // $app.dom.password.focus(); + // return; + // } + // if (str_password !== str_password2) { + // $app.show_op_box('error', '两次输入的密码不一致!'); + // $app.dom.password2.focus().select(); + // return; + // } + + $app.dom.btn_upgrade.attr('disabled', 'disabled').hide(); + $app.hide_op_box(); + $app.dom.steps_detail.show(); + + $tp.ajax_post_json('/maintenance/rpc', {cmd: 'upgrade_db'}, + function (ret) { + if (ret.code === TPE_OK) { + + var cb_stack = CALLBACK_STACK.create(); + cb_stack + .add_delay(500, $app.get_task_ret, {task_id: ret.data.task_id}) + .exec(); + } + + }, + function () { +// $app.show_message('error', '无法连接到服务器!'); + $app.show_op_box('error', '无法连接到服务器!'); + } + ); + + }); + + $app.get_task_ret = function (cb_stack, cb_args) { + var task_id = cb_args.task_id || 0; + if (task_id === 0) { + console.log('task-id', task_id); + return; + } + + $tp.ajax_post_json('/maintenance/rpc', {cmd: 'get_task_ret', 'tid': task_id}, + function (ret) { + if (ret.code === TPE_OK) { + + // show step progress. + var all_ok = true; + var steps = ret.data.steps; + $app.dom.steps_detail.empty(); + + var html = []; + var icon_class = ''; + var err_class = ''; + for (var i = 0; i < steps.length; ++i) { + if (steps[i].stat === 0) + icon_class = 'fa-check'; + else + icon_class = 'fa-cog fa-spin'; + + if (steps[i].code !== 0) { + icon_class = 'fa-exclamation-circle'; + err_class = ' class="error"'; + steps[i].msg += ' 失败!'; + all_ok = false; + } + else { + err_class = ''; + } + + html.push(' '); + html.push(steps[i].msg); + html.push('

') + } + $app.dom.steps_detail.html(html.join('')); + $('html').animate({scrollTop: $(document).height()}, 300); + + if (!ret.data.running) { + if (all_ok) { + + $tp.ajax_post_json('/auth/do-logout', {}, + function () { + }, + function () { + } + ); + + $app.dom.step2.show('fast', function () { + // 确保页面滚动到最低端,使得下一步提示能够被看到。 + $('html').animate({scrollTop: $(document).height()}, 300); + }); + } + return; + } + + cb_stack + .add_delay(500, $app.get_task_ret, {task_id: task_id}) + .exec(); + } + + }, + function () { + $app.show_op_box('error', '无法连接到服务器!'); + } + ); + + }; + + cb_stack.exec(); +}; + + + + + + + + + + + + + + + + + + + + + + + + + + ywl.on_init = function (cb_stack, cb_args) { + ywl.dom = { + btn_upgrade_db: $('#btn-upgrade-db'), + steps_detail: $('#steps-detail') + }; + + ywl.dom.btn_upgrade_db.click(function () { + + ywl.dom.btn_upgrade_db.attr('disabled', 'disabled').hide(); + ywl.dom.steps_detail.show(); + + console.log('upgrade-db-click'); + ywl.ajax_post_json('/maintenance/rpc', {cmd: 'upgrade_db'}, + function (ret) { + console.log('upgrade-db:', ret); + if (ret.code === 0) { + + var cb_stack = CALLBACK_STACK.create(); + cb_stack + .add(ywl.get_task_ret, {task_id: ret.data.task_id}) + .add(ywl.delay_exec, {delay_ms: 500}) + .exec(); + } + + }, + function () { + ywl.show_message('error', '无法连接到服务器!'); + } + ); + + }); + + ywl.get_task_ret = function (cb_stack, cb_args) { + var task_id = cb_args.task_id || 0; + if (task_id === 0) { + console.log('task-id', task_id); + return; + } + + ywl.ajax_post_json('/maintenance/rpc', {cmd: 'get_task_ret', 'tid': task_id}, + function (ret) { + console.log('get_task_ret:', ret); + if (ret.code === 0) { + + // show step progress. + var steps = ret.data.steps; + ywl.dom.steps_detail.empty(); + + var html = []; + var icon_class = ''; + var err_class = ''; + for(var i = 0; i < steps.length; ++i) { + if(steps[i].code !== 0) { + err_class = ' class="error"'; + icon_class = 'fa-times-circle'; + } + else { + err_class = ''; + icon_class = 'fa-check'; + } + + if(steps[i].stat === 0) + ;//icon_class = 'fa-check'; + else + icon_class = 'fa-cog fa-spin'; + + html.push(' '); + html.push(steps[i].msg); + html.push('

') + } + ywl.dom.steps_detail.html(html.join('')); + + + if (!ret.data.running) { + $('#step2').show('fast'); + return; + } + + cb_stack + .add(ywl.get_task_ret, {task_id: task_id}) + .add(ywl.delay_exec, {delay_ms: 500}) + .exec(); + } + + }, + function () { + ywl.show_message('error', '无法连接到服务器!'); + } + ); + + }; + + cb_stack.exec(); + }; diff --git a/server/www/teleport/view/maintenance/upgrade.mako b/server/www/teleport/view/maintenance/upgrade.mako index 41233ba..b0bce8f 100644 --- a/server/www/teleport/view/maintenance/upgrade.mako +++ b/server/www/teleport/view/maintenance/upgrade.mako @@ -1,180 +1,43 @@ <%! + import app.app_ver as app_ver page_title_ = '升级TELEPORT服务' %> -<%inherit file="../page_maintenance_base.mako"/> -<%block name="breadcrumb"> - +<%inherit file="../page_single_base.mako"/> + +<%block name="extend_js_file"> + -<%block name="embed_css"> - +<%block name="page_header"> +
+
+ +
+ ## Begin Main Body. -
+
+

欢迎升级到 TELEPORT v${app_ver.TP_SERVER_VER} 社区版!现在还剩下一点点操作需要执行!

-
-
+
+

第一步:升级数据库

+ -

升级TELEPORT服务

-
+
+
-

第一步:升级数据库

-
-
- -
-
-
- - - -
+
- - -<%block name="embed_js"> - - diff --git a/server/www/teleport/webroot/app/base/database/create.py b/server/www/teleport/webroot/app/base/database/create.py index 6de0e82..d24212f 100644 --- a/server/www/teleport/webroot/app/base/database/create.py +++ b/server/www/teleport/webroot/app/base/database/create.py @@ -82,6 +82,7 @@ class DatabaseInit: def _create_core_server(self): """ 核心服务(为分布式准备) + v7 新增 特别注意:分布式部署时,核心服务的RPC通讯端口仅允许来自web服务的IP访问 """ @@ -152,7 +153,7 @@ class DatabaseInit: f.append('`surname` varchar(64) DEFAULT ""') # type 1=本地账号,2=LDAP(待扩展) f.append('`type` int(11) DEFAULT 1') - # ldap_dn: 用户的ldap全路径名称,仅用于LDAP导入的用户 + # ldap_dn: 用户的ldap全路径名称,仅用于LDAP导入的用户(v7版新增) f.append('`ldap_dn` varchar(128) DEFAULT ""') # avatar: 用户头像图片地址 f.append('`avatar` varchar(64) DEFAULT ""') @@ -186,6 +187,11 @@ class DatabaseInit: # last_ip: 最近一次成功登录IP f.append('`last_ip` varchar(40) DEFAULT ""') + # valid_from: 有效期起始时间,为0则不限(v7版新增) + f.append('`valid_from` int(11) DEFAULT 0') + # valid_to: 有效期终止时间,为0则不限(v7版新增) + f.append('`valid_to` int(11) DEFAULT 0') + # creator_id: 创建者的用户id,0=系统默认创建 f.append('`creator_id` int(11) DEFAULT 0') # create_time: 创建时间 @@ -321,11 +327,11 @@ class DatabaseInit: # 下面三个主机相关字段用于显示(注意更新host表时同步更新此字段) - # host_ip: 主机IP地址 + # host_ip: 主机IP地址(v7版新增) f.append('`host_ip` varchar(40) NOT NULL') - # router_ip: 路由IP + # router_ip: 路由IP(v7版新增) f.append('`router_ip` varchar(40) DEFAULT ""') - # router_port: 路由端口 + # router_port: 路由端口(v7版新增) f.append('`router_port` int(11) DEFAULT 0') # protocol_type: 协议类型,0=?,1=SSH,2=RDP,3=TELNET @@ -737,13 +743,13 @@ class DatabaseInit: # id: 自增主键 f.append('`id` integer PRIMARY KEY {}'.format(self.db.auto_increment)) - # core_uuid: + # core_sn:(v7版新增) f.append('`core_sn` varchar(5) DEFAULT "0000"') # flag: 是否已审查/是否要永久保留,异或方式设置,0=初始,1=已审查,2=要永久保留 f.append('`flag` int(11) DEFAULT 0') - # reason: 本次运维的原因 + # reason: 本次运维的原因(v7版新增) f.append('`reason` varchar(255) DEFAULT ""') # sid: 会话ID diff --git a/server/www/teleport/webroot/app/base/database/upgrade.py b/server/www/teleport/webroot/app/base/database/upgrade.py new file mode 100644 index 0000000..6a8a886 --- /dev/null +++ b/server/www/teleport/webroot/app/base/database/upgrade.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- + +# from app.const import * +# from app.logic.auth.password import tp_password_generate_secret +# from app.base.utils import tp_timestamp_utc_now +from app.base.logger import log +# import shutil + + +class DatabaseUpgrade: + def __init__(self, db, step_begin, step_end): + self.db = db + self.step_begin = step_begin + self.step_end = step_end + + def do_upgrade(self): + for i in range(self.db.DB_VERSION): + if self.db.current_ver < i + 1: + _f_name = '_upgrade_to_v{}'.format(i + 1) + if _f_name in dir(self): + if self.__getattribute__(_f_name)(): + self.db.current_ver = i + 1 + else: + return False + + return True + + def _db_exec(self, msg, sql): + _step = self.step_begin(msg) + + ret = False + if type(sql) == str: + ret = self.db.exec(sql) + elif type(sql) == list or type(sql) == set: + for s in sql: + ret = self.db.exec(s) + if not ret: + break + else: + raise RuntimeError('[FAILED] internal error.') + + if not ret: + self.step_end(_step, -1) + raise RuntimeError('[FAILED] {}'.format(sql)) + else: + self.step_end(_step, 0) + + def _upgrade_to_v7(self): + # 注意:v2.x的最后版本时,数据库版本号为v6,但是v3.0.0技术预览版未升级数据库版本号,仍然为v6 + # 因此升级时要做检查,如果是当前数据库版本号为v6,要进一步判断是否为v2.x系列的数据库。 + # 服务端升级到v3.2.2时,数据库有部分调整 + _step = self.step_begin('检查数据库版本 v7...') + + try: + # 检查是否是 v2.x 版本的数据库(也是v6版数据库) + # 依据为,v3.x的服务端开始,数据库中有 tp_role 数据表。 + ret = self.db.is_table_exists('{}role'.format(self.db.table_prefix)) + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + self.step_end(_step, -1, '抱歉,不支持从v2.x升级到v3.x,数据库不兼容!请卸载旧版本,全新安装新版本!') + return True + except: + log.e('failed.\n') + self.step_end(_step, -1) + return False + + self.step_end(_step, 0, '需要升级') + + try: + # 1. 创建缺失的 core_server 表 + _step = self.step_begin(' - 检查 core_server 数据表...') + ret = self.db.is_table_exists('{}core_server'.format(self.db.table_prefix)) + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + _step = self.step_begin(' - 创建数据表 core_server...') + self._v7_create_core_server() + self._db_exec( + ' - 设置本机核心服务配置项...', + 'INSERT INTO `{}core_server` (`sn`, `secret`, `ip`, `port`, `state`) VALUES ' + '("0000", "", "127.0.0.1", 52080, 1);' + ''.format(self.db.table_prefix) + ) + self.step_end(_step, 0) + + # 2. 检查 user 表中是否有 ldap_dn/valid_from/valid_to 字段 + _step = self.step_begin(' - 检查 user 数据表 ldap_dn 字段...') + ret = self.db.is_field_exists('{}user'.format(self.db.table_prefix), 'ldap_dn') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}user` ADD `ldap_dn` VARCHAR(128) DEFAULT ""'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + else: + self.step_end(_step, 0) + + _step = self.step_begin(' - 检查 user 数据表 valid_from 字段...') + ret = self.db.is_field_exists('{}user'.format(self.db.table_prefix), 'valid_from') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}user` ADD `valid_from` INT(11) DEFAULT 0'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + else: + self.step_end(_step, 0) + + _step = self.step_begin(' - 检查 user 数据表 valid_to 字段...') + ret = self.db.is_field_exists('{}user'.format(self.db.table_prefix), 'valid_to') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}user` ADD `valid_to` INT(11) DEFAULT 0'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + else: + self.step_end(_step, 0) + + # 3. 检查 acc 表中是否有 host_ip/router_ip/router_port 字段 + _step = self.step_begin(' - 检查 acc 数据表 host_ip 字段...') + ret = self.db.is_field_exists('{}acc'.format(self.db.table_prefix), 'host_ip') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}acc` ADD `host_ip` VARCHAR(40) DEFAULT ""'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + self.step_end(_step, 0) + + _step = self.step_begin(' - 检查 acc 数据表 router_ip 字段...') + ret = self.db.is_field_exists('{}acc'.format(self.db.table_prefix), 'router_ip') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}acc` ADD `router_ip` VARCHAR(40) DEFAULT ""'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + self.step_end(_step, 0) + + _step = self.step_begin(' - 检查 acc 数据表 router_port 字段...') + ret = self.db.is_field_exists('{}acc'.format(self.db.table_prefix), 'router_port') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}acc` ADD `router_port` INT(11) DEFAULT 0'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + self.step_end(_step, 0) + + # 4. 检查 record 表中是否有 core_sn/reason 字段 + _step = self.step_begin(' - 检查 record 数据表 core_sn 字段...') + ret = self.db.is_field_exists('{}record'.format(self.db.table_prefix), 'core_sn') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}record` ADD `core_sn` VARCHAR(5) DEFAULT "0000"'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + self.step_end(_step, 0) + + _step = self.step_begin(' - 检查 record 数据表 reason 字段...') + ret = self.db.is_field_exists('{}record'.format(self.db.table_prefix), 'reason') + if ret is None: + self.step_end(_step, -1, '无法连接到数据库') + return False + elif not ret: + if not self.db.exec('ALTER TABLE `{}record` ADD `reason` VARCHAR(255) DEFAULT ""'.format(self.db.table_prefix)): + self.step_end(_step, -1, '失败') + return False + self.step_end(_step, 0) + + _step = self.step_begin(' - 更新数据库版本号...') + if not self.db.exec('UPDATE `{}config` SET `value`="7" WHERE `name`="db_ver";'.format(self.db.table_prefix)): + self.step_end(_step, -1, '无法更新数据库版本号') + return False + self.step_end(_step, 0) + + _step = self.step_begin('升级到 v7 完成') + self.step_end(_step, 0) + + return True + + except: + log.e('failed.\n') + self.step_end(_step, -1) + return False + + def _v7_create_core_server(self): + """ 核心服务(为分布式准备) + v7 新增 + 特别注意:分布式部署时,核心服务的RPC通讯端口仅允许来自web服务的IP访问 + """ + + f = list() + + # id: 自增主键 + f.append('`id` integer PRIMARY KEY {}'.format(self.db.auto_increment)) + # sn: 核心服务主机编号(4位数字构成的字符串,全0表示运行在与web服务同一台主机上) + f.append('`sn` varchar(5) NOT NULL') + # desc: 核心服务主机描述 + f.append('`desc` varchar(255) DEFAULT ""') + # secret: 核心服务主机密钥(核心服务主机需要配置此密钥才能连接web服务) + f.append('`secret` varchar(64) DEFAULT ""') + # ip: 核心服务主机的RPC服务IP和端口,用于合成RPC访问地址,例如 http://127.0.0.1:52080/rpc + f.append('`ip` varchar(128) NOT NULL') + f.append('`port` int(11) DEFAULT 0') + # state: 状态,1=正常,2=禁用,3=离线,4=重启中,5=版本不匹配 + f.append('`state` int(3) DEFAULT 1') + self._db_exec( + ' - 创建核心服务器表...', + 'CREATE TABLE `{}core_server` ({});'.format(self.db.table_prefix, ','.join(f)) + ) diff --git a/server/www/teleport/webroot/app/base/db.py b/server/www/teleport/webroot/app/base/db.py index 291964f..8608b21 100644 --- a/server/www/teleport/webroot/app/base/db.py +++ b/server/www/teleport/webroot/app/base/db.py @@ -12,7 +12,7 @@ from app.base.configs import tp_cfg from app.base.utils import AttrDict, tp_make_dir from app.base.logger import log from .database.create import DatabaseInit -# from .database.upgrade import DatabaseUpgrade +from .database.upgrade import DatabaseUpgrade from .database.export import export_database __all__ = ['get_db', 'SQL'] @@ -23,7 +23,8 @@ __all__ = ['get_db', 'SQL'] class TPDatabase: # 注意,每次调整数据库结构,必须增加版本号,并且在升级接口中编写对应的升级操作 - DB_VERSION = 6 + # 20190123: server-v3.2.2, db-v7 + DB_VERSION = 7 DB_TYPE_UNKNOWN = 0 DB_TYPE_SQLITE = 1 @@ -267,15 +268,15 @@ class TPDatabase: log.e('database create and initialize failed.\n') return False - # def upgrade_database(self, step_begin, step_end): - # log.v('start database upgrade process.\n') - # if DatabaseUpgrade(self, step_begin, step_end).do_upgrade(): - # log.v('database upgraded.\n') - # self.need_upgrade = False - # return True - # else: - # log.e('database upgrade failed.\n') - # return False + def upgrade_database(self, step_begin, step_end): + log.v('start database upgrade process.\n') + if DatabaseUpgrade(self, step_begin, step_end).do_upgrade(): + log.v('database upgraded.\n') + self.need_upgrade = False + return True + else: + log.e('database upgrade failed.\n') + return False def alter_table(self, table_names, field_names=None): """ diff --git a/server/www/teleport/webroot/app/controller/__init__.py b/server/www/teleport/webroot/app/controller/__init__.py index ca48d37..d121189 100755 --- a/server/www/teleport/webroot/app/controller/__init__.py +++ b/server/www/teleport/webroot/app/controller/__init__.py @@ -276,7 +276,7 @@ controllers = [ # - 初始安装设置(新安装,未创建数据库时自动跳转到此页面) (r'/maintenance/install', maintenance.InstallHandler), # - 升级(数据库版本发生变化时跳转到此页面) - # (r'/maintenance/upgrade', maintenance.UpgradeHandler), + (r'/maintenance/upgrade', maintenance.UpgradeHandler), # - [json] 维护过程中页面与后台的通讯接口 (r'/maintenance/rpc', maintenance.RpcHandler), diff --git a/server/www/teleport/webroot/app/controller/auth.py b/server/www/teleport/webroot/app/controller/auth.py index 2ca9d2a..b022dcd 100644 --- a/server/www/teleport/webroot/app/controller/auth.py +++ b/server/www/teleport/webroot/app/controller/auth.py @@ -17,8 +17,8 @@ class LoginHandler(TPBaseHandler): if tp_cfg().app_mode == APP_MODE_MAINTENANCE and get_db().need_create: _user = { 'id': 0, - 'username': 'installer', - 'surname': '安装程序', + 'username': 'maintainer', + 'surname': '系统维护-安装', 'role_id': 0, 'role': '', 'privilege': TP_PRIVILEGE_SYS_CONFIG, @@ -28,6 +28,20 @@ class LoginHandler(TPBaseHandler): self.redirect('/maintenance/install') return + if tp_cfg().app_mode == APP_MODE_MAINTENANCE and get_db().need_upgrade: + _user = { + 'id': 0, + 'username': 'maintainer', + 'surname': '系统维护-升级', + 'role_id': 0, + 'role': '', + 'privilege': TP_PRIVILEGE_SYS_CONFIG, + '_is_login': True + } + self.set_session('user', _user) + self.redirect('/maintenance/upgrade') + return + _user = self.get_current_user() _ref = quote(self.get_argument('ref', '/'))