├── .htaccess ├── Dockerfile ├── LICENSE ├── app ├── controller │ ├── cfginit.php │ ├── index.php │ └── teambition │ │ ├── pan.php │ │ └── project.php ├── function.php ├── route.php └── view │ ├── cfginit.html │ ├── common │ ├── foot.html │ └── head.html │ ├── index │ ├── _preview.html │ ├── index.html │ └── pan.html │ └── password.html ├── assets └── index.css ├── config └── sys.cfg.php ├── extend └── teambition.php ├── favicon.ico ├── framework ├── flx.php ├── function.php ├── route.php └── view │ ├── app_error.php │ └── sys_error.php ├── index.php ├── nginx └── readme.md /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Options +FollowSymlinks -Multiviews 3 | RewriteEngine On 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | RewriteCond %{REQUEST_FILENAME} !-f 6 | RewriteRule ^(.*)$ index.php/?s=$1 [QSA,PT,L] 7 | 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.0-apache 2 | 3 | RUN apt-get update && \ 4 | apt-get clean 5 | 6 | RUN a2enmod rewrite 7 | 8 | COPY ./ /var/www/html/ 9 | 10 | RUN chown -R www-data:www-data /var/www/html/ 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 FlxSNX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/controller/cfginit.php: -------------------------------------------------------------------------------- 1 | _CFG = $Flx->_CFG; 8 | if(!$this->_CFG['teambition'] && !$this->_CFG['pan']){ 9 | if(is_ajax()){ 10 | $type = authstr($_POST['type']) ?: NULL; 11 | $title = authstr($_POST['title']) ?: NULL; 12 | $url = authstr($_POST['url']) ?: NULL; 13 | $cookie = authstr($_POST['cookie']) ?: NULL; 14 | if(!$type || !$title || !$url || !$cookie)exit('{"code":500,"msg":"参数错误"}'); 15 | if($type == 'pan'){ 16 | $result = teambition::get_pan_config($cookie); 17 | if($result){ 18 | $config = << '$type', 22 | 'url' => '$url', 23 | 'pan' => [ 24 | 'cookie' => '$cookie', 25 | 'orgId' => '$result[orgId]', 26 | 'spaceId' => '$result[spaceId]', 27 | 'driveId' => '$result[driveId]', 28 | 'rootId' => '$result[rootId]', 29 | 'maxCount' => 1000 30 | ], 31 | 'title' => '$title' 32 | ]; 33 | TEXT; 34 | 35 | $savecfg = file_put_contents(ROOTDIR.'config/app.cfg.php', $config); 36 | if($savecfg){ 37 | exit('{"code":200,"msg":"配置成功"}'); 38 | }else{ 39 | exit('{"code":500,"msg":"保存信息时出错"}'); 40 | } 41 | }else{ 42 | exit('{"code":500,"msg":"获取网盘信息失败:cookie错误或未开通网盘"}'); 43 | } 44 | }elseif($type == 'project'){ 45 | $projectId = authstr($_POST['projectId']) ?: NULL; 46 | if(!$projectId)exit('{"code":500,"msg":"缺少projectId"}'); 47 | $project = teambition::get_project($projectId,$cookie); 48 | if($project){ 49 | $config = << '$type', 53 | 'url' => '$url', 54 | 'teambition' => [ 55 | 'cookie' => '$cookie', 56 | 'projectId' => '$projectId', 57 | 'maxCount' => 1000 58 | ], 59 | 'title' => '$title' 60 | ]; 61 | TEXT; 62 | 63 | $savecfg = file_put_contents(ROOTDIR.'config/app.cfg.php', $config); 64 | if($savecfg){ 65 | exit('{"code":200,"msg":"配置成功"}'); 66 | }else{ 67 | exit('{"code":500,"msg":"保存信息时出错"}'); 68 | } 69 | }else{ 70 | exit('{"code":500,"msg":"获取项目信息失败:cookie或projectId错误"}'); 71 | } 72 | } 73 | } 74 | assign([ 75 | 'title' => '配置向导', 76 | 'version' => '1.14' 77 | ]); 78 | view('cfginit'); 79 | }else{ 80 | app_error([ 81 | 'code' => 403, 82 | 'title' => '拒绝访问', 83 | 'error' => '请问重复配置', 84 | 'info' => '如果需要重新配置,请将配置文件{app.cfg.php}清空!' 85 | ]); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /app/controller/index.php: -------------------------------------------------------------------------------- 1 | _CFG = $Flx->_CFG; 11 | $this->cookie = $this->_CFG['teambition']['cookie']; 12 | assign(['_CFG' => $this->_CFG,'version' => $version]); 13 | // 全局密码 14 | if(isset($this->_CFG['password']) && $this->_CFG['password'] != false){ 15 | session_start(); 16 | if(is_ajax()){ 17 | if(!$_POST['id']){ 18 | $password = $_POST['password']; 19 | if($_POST['password'] == $this->_CFG['password']){ 20 | $_SESSION['password'] = $_POST['password']; 21 | exit('{"code":200,"msg":"密码正确,3秒后跳转"}'); 22 | }else{ 23 | exit('{"code":500,"msg":"密码错误"}'); 24 | } 25 | } 26 | } 27 | if($_SESSION['password'] != $this->_CFG['password']){ 28 | assign(['title' => $this->_CFG['title']]); 29 | view('password'); 30 | exit; 31 | } 32 | } 33 | } 34 | 35 | public function index($id=false){ 36 | if(!$this->_CFG['teambition'] && !$this->_CFG['pan']){ 37 | header('Location:init'); 38 | }else{ 39 | if($this->_CFG['type'] == 'project'){ 40 | $project = new project($this->_CFG); 41 | 42 | if(!$id)$id = $project->info['_rootCollectionId']; 43 | 44 | $list = $project->get_list($id); 45 | 46 | $dirlist = $list['dirs']; 47 | $filelist = $list['files']; 48 | $dir = $project->get_dir($id); 49 | 50 | // 查询是否有.password文件 51 | $fileindex = 0; 52 | $pass = false; 53 | foreach($filelist as $file){ 54 | if($file['fileName'] == '.password'){ 55 | // 如果文件大于2kb忽略掉 56 | if($file['fileSize'] < 2048){ 57 | $pass = curl($file['downloadUrl']); 58 | unset($filelist[$fileindex]); 59 | $filelist = array_values($filelist); 60 | } 61 | break; 62 | } 63 | $fileindex++; 64 | } 65 | 66 | assign([ 67 | 'dirlist' => $dirlist, 68 | 'filelist' => $filelist, 69 | 'title' => $this->_CFG['title'], 70 | 'dir' => $dir 71 | ]); 72 | 73 | // 目录密码验证 74 | if($pass !== false){ 75 | session_start(); 76 | if(is_ajax() && !empty($_POST['id'])){ 77 | if($_POST['password'] == $pass){ 78 | $_SESSION[$_POST['id']] = $_POST['password']; 79 | exit('{"code":200,"msg":"密码正确,3秒后跳转"}'); 80 | }else{ 81 | exit('{"code":500,"msg":"密码错误"}'); 82 | } 83 | } 84 | if($_SESSION[$id] == $pass){ 85 | view(); 86 | }else{ 87 | assign(['id' => $id]); 88 | view('password'); 89 | } 90 | }else{ 91 | view(); 92 | } 93 | }elseif($this->_CFG['type'] == 'pan'){ 94 | $pan = new pan($this->_CFG); 95 | if(!$id)$id = $this->_CFG['pan']['rootId']; 96 | 97 | $list = $pan->get_list($id); 98 | $dir = $pan->get_dir($id); 99 | 100 | // 查询是否有.password文件 101 | $fileindex = 0; 102 | $pass = false; 103 | foreach($list['data'] as $file){ 104 | if($file['kind'] != 'folder' && $file['name'] == '.password'){ 105 | // 如果文件大于2kb忽略掉 106 | if($file['fileSize'] < 2048){ 107 | $pass = curl($file['downloadUrl']); 108 | unset($list['data'][$fileindex]); 109 | $list['data'] = array_values($list['data']); 110 | } 111 | break; 112 | } 113 | $fileindex++; 114 | } 115 | 116 | assign([ 117 | 'panlist' => $list, 118 | 'title' => $this->_CFG['title'], 119 | 'dir' => $dir 120 | ]); 121 | 122 | // 目录密码验证 123 | if($pass !== false){ 124 | session_start(); 125 | if(is_ajax() && !empty($_POST['id'])){ 126 | if($_POST['password'] == $pass){ 127 | $_SESSION[$_POST['id']] = $_POST['password']; 128 | exit('{"code":200,"msg":"密码正确,3秒后跳转"}'); 129 | }else{ 130 | exit('{"code":500,"msg":"密码错误"}'); 131 | } 132 | } 133 | if($_SESSION[$id] == $pass){ 134 | view('index/pan'); 135 | }else{ 136 | assign(['id' => $id]); 137 | view('password'); 138 | } 139 | }else{ 140 | view('index/pan'); 141 | } 142 | }else{ 143 | app_error([ 144 | 'code' => 500, 145 | 'title' => '应用配置错误', 146 | 'error' => '配置错误', 147 | 'info' => '类型只能为project或pan' 148 | ]); 149 | } 150 | } 151 | } 152 | 153 | public function getDownload($id){ 154 | $type = authstr($_GET['type']) ?: 302; 155 | if($this->_CFG['type'] == 'project'){ 156 | $result = teambition::get_download_url($id,$this->cookie); 157 | if($result){ 158 | if($result['downloadUrl']){ 159 | if(isset($_GET['preview'])){ 160 | assign([ 161 | 'fileid' => $id, 162 | 'filename' => $result['fileName'], 163 | 'filetype' => $result['fileType'], 164 | 'downloadUrl' => $result['downloadUrl'], 165 | 'title' => $this->_CFG['title'] 166 | ]); 167 | view('index/_preview'); 168 | }else{ 169 | if($type == 'json'){ 170 | $result = [ 171 | 'code' => 200, 172 | 'msg' => '解析成功', 173 | 'data' => $result 174 | ]; 175 | exit(json_encode($result)); 176 | }elseif($type == 302){ 177 | header('Location:'.$result['downloadUrl']); 178 | }elseif($type == 'url'){ 179 | exit($result['downloadUrl']); 180 | } 181 | } 182 | }else{ 183 | exit('{"code":500,"msg":"解析失败"}'); 184 | } 185 | }else{ 186 | exit('{"code":500,"msg":"解析失败"}'); 187 | } 188 | }elseif($this->_CFG['type'] == 'pan'){ 189 | $result = teambition::get_pan_file($this->_CFG['pan']['cookie'],$this->_CFG['pan']['orgId'],$this->_CFG['pan']['spaceId'],$this->_CFG['pan']['driveId'],$id); 190 | if($result){ 191 | if($result['downloadUrl']){ 192 | if(isset($_GET['preview'])){ 193 | assign([ 194 | 'fileid' => $id, 195 | 'filename' => $result['name'], 196 | 'filetype' => $result['ext'], 197 | 'downloadUrl' => $result['downloadUrl'], 198 | 'title' => $this->_CFG['title'] 199 | ]); 200 | view('index/_preview'); 201 | }else{ 202 | if($type == 'json'){ 203 | $result = [ 204 | 'code' => 200, 205 | 'msg' => '解析成功', 206 | 'data' => $result 207 | ]; 208 | exit(json_encode($result)); 209 | }elseif($type == 302){ 210 | header('Location:'.$result['downloadUrl']); 211 | }elseif($type == 'url'){ 212 | exit($result['downloadUrl']); 213 | } 214 | } 215 | }else{ 216 | exit('{"code":500,"msg":"解析失败"}'); 217 | } 218 | } 219 | } 220 | } 221 | } -------------------------------------------------------------------------------- /app/controller/teambition/pan.php: -------------------------------------------------------------------------------- 1 | cookie = $_CFG['pan']['cookie']; 15 | $this->_CFG = $_CFG; 16 | }else{ 17 | app_error([ 18 | 'code' => 500, 19 | 'title' => '应用配置错误', 20 | 'error' => 'cookie配置错误', 21 | 'info' => '请检查cookie是否已失效后重新配置' 22 | ]); 23 | } 24 | } 25 | 26 | public function get_list($id,$order='updateTime'){ 27 | $result = teambition::get_pan_list($this->_CFG['pan']['cookie'],$this->_CFG['pan']['orgId'],$this->_CFG['pan']['spaceId'],$this->_CFG['pan']['driveId'],$id,$this->_CFG['pan']['maxCount']); 28 | if($result['data']['message']){ 29 | return app_error(404); 30 | }else{ 31 | return $result; 32 | } 33 | } 34 | 35 | public function get_dir($id){ 36 | $result = teambition::get_pan_file($this->_CFG['pan']['cookie'],$this->_CFG['pan']['orgId'],$this->_CFG['pan']['spaceId'],$this->_CFG['pan']['driveId'],$id); 37 | if($result['status'] != '400'){ 38 | return $result; 39 | }else{ 40 | return app_error(404); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/controller/teambition/project.php: -------------------------------------------------------------------------------- 1 | cookie = $_CFG['teambition']['cookie']; 17 | $this->projectId = $_CFG['teambition']['projectId']; 18 | $this->info = $project; 19 | $this->_CFG = $_CFG; 20 | }else{ 21 | app_error([ 22 | 'code' => 500, 23 | 'title' => '应用配置错误', 24 | 'error' => 'cookie或projectId配置错误', 25 | 'info' => '请检查projectId是否配置正确或cookie是否已失效后重新配置' 26 | ]); 27 | } 28 | } 29 | 30 | public function get_list($id=false,$order='updatedDesc'){ 31 | if(!$id)$id = $this->project['_rootCollectionId']; 32 | $dirs = $this->get_dirs($id,$order); 33 | $files = $this->get_files($id,$order); 34 | return ['dirs' => $dirs,'files' => $files]; 35 | } 36 | 37 | public function get_dirs($id,$order='updatedDesc'){ 38 | // 因为project的获取目录和文件是分开的所以数量除以2 39 | $maxCount = ceil($this->_CFG['teambition']['maxCount']/2); 40 | $result = teambition::get_dirs($this->projectId,$id,$this->cookie,$maxCount,$order); 41 | // 如果有message则代表ID错误 返回404 42 | if($result['message'])return app_error(404); 43 | return $result; 44 | } 45 | 46 | public function get_files($id,$order='updatedDesc'){ 47 | // 因为project的获取目录和文件是分开的所以数量除以2 48 | $maxCount = ceil($this->_CFG['teambition']['maxCount']/2); 49 | $result = teambition::get_files($this->projectId,$id,$this->cookie,$maxCount,$order); 50 | // 如果有message则代表ID错误 返回404 51 | if($result['message'])return app_error(404); 52 | return $result; 53 | } 54 | 55 | public function get_dir($id){ 56 | $result = teambition::get_dir($id,$this->cookie); 57 | if($result){ 58 | return $result; 59 | }else{ 60 | return app_error(404); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /app/function.php: -------------------------------------------------------------------------------- 1 | = 1073741824) { 5 | $filesize = round($filesize / 1073741824 * 100) / 100 . ' GB'; 6 | 7 | } elseif($filesize >= 1048576) { 8 | $filesize = round($filesize / 1048576 * 100) / 100 . ' MB'; 9 | 10 | } elseif($filesize >= 1024) { 11 | $filesize = round($filesize / 1024 * 100) / 100 . ' KB'; 12 | 13 | } else { 14 | $filesize = $filesize . ' 字节'; 15 | 16 | } 17 | return $filesize; 18 | } 19 | 20 | function getFileicon($type){ 21 | $type = strtolower($type); 22 | if($type == 'mp4'){ 23 | return 'play_circle_filled'; 24 | }elseif(in_array($type,["gif","jpeg","jpg","png"])){ 25 | return 'photo'; 26 | }elseif(in_array($type,["mp3","ogg","wav"])){ 27 | return 'music_note'; 28 | }else{ 29 | return 'insert_drive_file'; 30 | } 31 | } 32 | 33 | function isPreview($type){ 34 | $type = strtolower($type); 35 | if($type == 'mp4' || in_array($type,["gif","jpeg","jpg","png"]) || in_array($type,["mp3","ogg","wav"])){ 36 | return '?preview'; 37 | }else{ 38 | return false; 39 | } 40 | } 41 | 42 | function curl($url,$post=false,$cookie=false,$header=false,$split=false,$referer=false){ 43 | $ch = curl_init(); 44 | if($header){ 45 | curl_setopt($ch,CURLOPT_HEADER, 1); 46 | }else{ 47 | curl_setopt($ch,CURLOPT_HEADER, 0); 48 | } 49 | curl_setopt($ch, CURLOPT_URL,$url); 50 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 51 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 52 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 53 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); 54 | curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'); 55 | if($post){ 56 | curl_setopt($ch, CURLOPT_POST, 1); 57 | curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post)); 58 | } 59 | if($cookie){ 60 | curl_setopt($ch, CURLOPT_COOKIE,$cookie); 61 | } 62 | if($referer){ 63 | curl_setopt($ch, CURLOPT_REFERER, $referer); 64 | } 65 | $result = curl_exec($ch); 66 | if($split){ 67 | $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 68 | $header = substr($result, 0, $headerSize); 69 | $body = substr($result, $headerSize); 70 | $result=array(); 71 | $result['header']=$header; 72 | $result['body']=$body; 73 | } 74 | curl_close($ch); 75 | return $result; 76 | } 77 | 78 | function is_post(){ 79 | return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD'])=='POST'; 80 | } 81 | 82 | function is_ajax(){ 83 | return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtoupper($_SERVER['HTTP_X_REQUESTED_WITH'])=='XMLHTTPREQUEST'; 84 | } -------------------------------------------------------------------------------- /app/route.php: -------------------------------------------------------------------------------- 1 | where([ 9 | 'dirid' => '[A-Za-z0-9]+' 10 | ]); 11 | 12 | Route::get('download/{fileid}','index@getDownload')->where([ 13 | 'fileid' => '[A-Za-z0-9]+' 14 | ]); -------------------------------------------------------------------------------- /app/view/cfginit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 网站标题 7 | 8 | 9 | 10 | 网站地址 11 | 12 | 如果放在二级目录,请填写对应地址 例如http://xxx.com/pan 13 | 14 | 15 | 挂载类型 16 | 17 | 18 | 19 | 20 | 21 | Teambition网盘 22 | 23 | 24 | 25 | 26 | 27 | 28 | Teambition项目文件 29 | 30 | 31 | 32 | 33 | 34 | 项目ID 35 | 36 | 37 | 38 | 账号Cookie 39 | 40 | 41 | 确认配置 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/view/common/foot.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 40 |