sys_version = Config::get('taoler.version'); $this->pn = Config::get('taoler.appname'); $this->sys = $this->getSystem(); } /** 升级界面 */ public function index() { //字符隐藏 $key = Str::func_substr_replace($this->sys['key']); $sys_base = [ 'key' => $key, 'upcheck_url' => $this->sys['upcheck_url'], 'upgrade_url' => $this->sys['upgrade_url'], ]; View::assign('ver_num',$sys_base); return View::fetch(); } //设置key public function key() { $data = Request::only(['key']); if(empty($data['key'])){ return json(['code'=>0,'msg'=>'请填写正确的key']); } $res = Db::name('system')->cache('system')->update(['key'=>$data['key'],'id'=>1]); if($res){ $res = ['code'=>0,'msg'=>'保存成功']; } else { $res = ['code'=>-1,'msg'=>'保存失败']; } return json($res); } //修改key public function keyedit() { $key = Db::name('system')->field('key,upcheck_url,upgrade_url')->find(1); if(Request::isAjax()){ $data = Request::only(['key','upcheck_url','upgrade_url']); if(empty($data['key'])){ return json(['code'=>-1,'msg'=>'请正确填写申请到的key']); } $res = Db::name('system')->cache('system')->update(['key'=>$data['key'],'upcheck_url'=>$data['upcheck_url'],'upgrade_url'=>$data['upgrade_url'],'id'=>1]); if($res){ $res = ['code'=>0,'msg'=>'修改成功']; } else { $res = ['code'=>-1,'msg'=>'修改失败']; } return json($res); } View::assign('key',$key); return View::fetch(); } //升级前的版本检测 public function check() { $cy = Api::urlPost($this->sys['base_url'],['u'=>$this->sys['domain']]); if($cy->code == 0 && $cy->level !== $this->sys['clevel']){ Db::name('system')->cache('system')->update(['clevel'=>$cy->level,'id'=>1]); } $versions = Api::urlPost($this->sys['upcheck_url'],['pn'=>$this->pn,'ver'=>$this->sys_version]); //判断服务器状态 $version_code = $versions->code; if($version_code == -1){ $res = json(['code'=>$version_code,'msg'=>$versions->msg]); } if($version_code == 1){ $res = json(['code'=>$versions->code,'msg'=>$versions->msg,'version'=>$versions->version,'upnum'=>$versions->up_num]); } if($version_code == 0){ $res = json(['code'=>$versions->code,'msg'=>$versions->msg]); } return $res; } /** * 备份 * @param string $dir * @param string $backDir * @param array $ex * @return \think\response\Json */ public function backFile(string $dir,string $backDir,array $ex) { $backRes = Files::copydirs($dir, $backDir, $ex); $backData = $backRes->getData(); if($backData['code'] == -1){ Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'error','progress'=>'25%','msg'=>'备份失败!']); return json(['code'=>-1,'msg'=>$backRes['msg']]); } Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'success','progress'=>'30%','msg'=>'执行文件备份成功!']); } /** * 在线更新 */ public function upload() { $versions = Api::urlPost($this->sys['upgrade_url'],['url'=>$this->sys['domain'],'key'=>$this->sys['key'],'pn'=>$this->pn,'ver'=>$this->sys_version]); Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'check','progress'=>'0%','msg'=>'===>升级检测开始===>']); //判断服务器状态 $version_code = $versions->code; if($version_code == -1){ Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'check eroor','progress'=>'5%','msg'=>'===>服务器链接失败===>']); return json(['code'=>$version_code,'msg'=>$versions->msg]); } $version_num = $versions->version; $file_url = $versions->src; //判断远程文件是否可用存在 $header = get_headers($file_url, true); if(!isset($header[0]) && (strpos($header[0], '200') || strpos($header[0], '304'))){ return json(['code'=>-1,'msg'=>'获取远程文件失败']); } //把远程文件放入本地 //拼接路径 //$upload_dir = substr($this->upload_dir,-1) == '/' ? $this->upload_dir : $this->upload_dir.'/'; $upload_dir = Files::getDirPath($this->upload_dir); Files::mkdirs($upload_dir); $package_file = $upload_dir.'taoler_'.$version_num.'.zip'; //升级的压缩包文件 $cpfile = copy($file_url,$package_file); if(!$cpfile) { return json(['code'=>-1,'msg'=>'下载升级文件失败']); } //记录下日志 Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'success','progress'=>'20%','msg'=>'上传升级包'.$version_num.'成功!']); //升级前备份代码 $ex = array('.git','.idea','runtime','data','addons','config','extend','mysql','public','vendor','view'); // 排除备份文件夹 $this->backFile($this->root_dir,$this->backup_dir,$ex); //执行升级 $updateRes = $this->execute_update($package_file); $upDate = $updateRes->getData(); if($upDate['code'] == -1){ return json(['code'=>-1,'msg'=>$upDate['msg']]); } //清除 Files::delDirAndFile($this->upload_dir); Files::delDirAndFile($this->backup_dir); //清除无用目录和文件 $delFiles = '../runtime/remove.txt'; if(file_exists($delFiles)){ $str = file_get_contents($delFiles); //将整个文件内容读入到一个字符串中 $str = str_replace("\r\n",",",$str); $delArr = explode(',',$str); foreach($delArr as $v){ if(is_dir($v)){ //删除文件夹 Files::delDirAndFile($v.'/',true); } else { //删除文件 if(file_exists($v)){ unlink($v); } } } unlink($delFiles); } //清理缓存 $this->clearSysCache(); //更新版本 //Db::name('system')->update(['sys_version_num'=>$version_num,'id'=>1]); $setConf = new SetConf; $value = [ 'version' => $version_num ]; $upRes = $setConf->setConfig('taoler',$value); $result = $upRes->getData(); if($result['code'] == -1){ return json(['code'=>-1,'msg'=>'代码写入成功,但'.$result['msg']]); } return json(['code'=>0,'msg'=>'升级成功']); } /**升级执行 * @param string $package_file * @return \think\response\Json */ private function execute_update(string $package_file) { //解压 zip文件有密码的话需要解密 //$uzip = new ZipFile(); $zip = new Zip; $zipDir = strstr($package_file, '.zip',true); //返回文件名后缀前的字符串 $zipPath = Files::getDirPath($zipDir); //转换为带/的路径 压缩文件解压到的路径 //$unzip_res = $uzip->unzip($package_file,$zipPath,true); $unzip_res = $zip->unzip($package_file,$zipPath); if(!$unzip_res) { return json(['code'=>-1,'msg'=>'解压失败']); } //解压成功,得到文件夹 //$package_name = str_replace('.zip','',$package_file); Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'success','progress'=>'50%','msg'=>'升级文件解压成功!']); //升级PHP if(is_dir($zipPath)) { //升级前的写入文件权限检查 $allUpdateFiles = Files::getAllFile($zipPath); if (empty($allUpdateFiles)) return json(['code' => -1, 'msg' => '无可更新文件。']); $checkString = ''; foreach ($allUpdateFiles as $updateFile) { $coverFile = ltrim(str_replace($zipPath, '', $updateFile), DIRECTORY_SEPARATOR); $dirPath = dirname('../'.$coverFile); if (file_exists('../'.$coverFile)) { if (!is_writable('../'.$coverFile)) $checkString .= $coverFile . ' [' . '无写入权限' . ']
'; } else { if (!is_dir($dirPath)) @mkdir($dirPath, 0777, true); if (!is_writable($dirPath)) $checkString .= $dirPath . ' [' . '无写入权限' . ']
'; } } if (!empty($checkString)) return json(['code' => -1, 'msg' => $checkString]); $cpRes = Files::copyDirs($zipPath,$this->root_dir); $cpData = $cpRes->getData(); //更新失败 if($cpData['code'] == -1) { //数据库回滚 /* if(file_exists($this->upload_dir.'/'.$package_file.'/mysql/mysql_rockback.sql')) { $this->database_operation($this->upload_dir.'/'.$package_file.'/mysql/mysql_rockback.sql'); } */ //php代码回滚 升级前备份的代码 Files::copydirs($this->backup_dir, $this->root_dir); return json(['code'=>-1,'msg'=>$cpData['msg']]); } Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'success','progress'=>'70%','msg'=>'升级文件执行成功!']); } //升级sql操作 $upSql = $zipPath.'runtime/update.sql'; if(file_exists($upSql)) { $sqlRes = $this->db_update($upSql); $upDate = $sqlRes->getData(); if($upDate['code'] == -1){ return json(['code'=>-1,'msg'=>$upDate['msg']]); } //删除sql语句 unlink($upSql); } Log::channel('update')->info('update:{type} {progress} {msg}',['type'=>'success','progress'=>'100%','msg'=>'升级成功!']); //更新系统的版本号了 //更新php的版本号了(应该跟svn/git的版本号一致) //更新数据库的版本号了(应该跟svn/git的版本号一致) return json(['code'=>0,'msg'=>'升级文件执行成功']); } /** * 处理升级包上传 */ public function uploadZip() { $files = request()->file('file'); $mime = $files->getMime(); if($mime !== 'application/zip'){ return json(['code'=>-1,'msg'=>'文件类型不对']); } $name = $files->getOriginalName(); //校验后缀 $ext = pathinfo($name,PATHINFO_EXTENSION); //文件后缀 if($ext != 'zip') { return json(['code'=>-1,'msg'=>'上传文件格式不对']); } //对比版本号 $fname = pathinfo($name,PATHINFO_FILENAME); //无后缀文件名 $version_num = array_pop(explode('_',$fname)); $verRes = version_compare($version_num,$this->sys_version,'>'); if(!$verRes){ return json(['code'=>-1,'msg'=>'不能降级,请选择正确版本']); } $upDir = $this->upload_dir.$fname; //$mv = $files->move('/../tmp/web/upload_dir',$version_num); $mfile = move_uploaded_file($files,$upDir); if(!$mfile) { return json(['code'=>0,'msg'=>'上传文件失败']); } //升级前备份代码 $ex = array('.git','.idea','runtime','data','addons','config','extend'); // 排除备份文件夹 $this->backFile($this->root_dir,$this->backup_dir,$ex); //执行升级 $upres = $this->execute_update($mfile); //更新版本 //Db::name('system')->update(['sys_version_num'=>$version_num,'id'=>1]); $setConf = new SetConf; $value = [ 'version' => $version_num ]; $upRes = $setConf->setConfig('taoler',$value); $result = $upRes->getData(); if($result['code'] == -1){ return json(['code'=>-1,'msg'=>$result['msg']]); } return json(['code'=>0,'msg'=>'升级成功']); } /** * 数据库操作 */ public function database_operation($file) { $mysqli = new mysqli('localhost','root','root','test'); if($mysqli->connect_errno) { return json(['code'=>0,'msg'=>'Connect failed:'.$mysqli->connect_error]); } $sql = file_get_contents($file); $a = $mysqli->multi_query($sql); return ['code'=>1,'msg'=>'数据库操作OK']; } /** * 执行数据库操作 */ public function db_update($file) { $sqlf = new SqlFile(); $sql_array = $sqlf->load_sql_file($file); foreach($sql_array as $v){ $sqlRes = Db::execute($v); if ($sqlRes === false) { return json(['code'=>-1,'msg'=>'数据库升级失败']); } } return json(['code'=>0,'msg'=>'数据库升级成功']); } }