Compare commits

...
162 Commits
Author SHA1 Message Date
HFO4 ddc0b6ad4b release:1.1.1 2018-10-20 17:08:50 +08:00
HFO4 75cbbcaa10 add: Crontab config reminder 2018-10-20 15:37:20 +08:00
HFO4 060f7a2081 feat: Manage page for task queue 2018-10-20 15:25:35 +08:00
HFO4 a67185c750 fix: installation error in command line 2018-10-19 16:31:51 +08:00
HFO4 38b11c1651 Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-10-19 16:30:41 +08:00
HFO4 96b7f4c97d fix: installation error in command line 2018-10-19 16:30:08 +08:00
AaronLiuandGitHub f600b21faa Merge pull request #95 from samnela/change-psr-autoload
moved to psr-4
2018-09-15 19:41:59 +08:00
AaronLiuandGitHub 1693bf800b Merge pull request #88 from Yuanuo/patch-02
PHP basename 中文乱码问题
2018-09-15 19:36:54 +08:00
Samuel NELA f39334125b moved to psr-4 2018-09-14 23:34:33 +02:00
HFO4 9ee144d870 change: Telegram group invite link 2018-09-13 22:16:49 +08:00
HFO4 755fe7331f fix: sql index error 2018-09-13 18:53:37 +08:00
HFO4 78f4a1eae9 fix: failed to close file stream in onedrive policy 2018-09-13 17:07:51 +08:00
HFO4 55ea76e35e fix: Onedrive token encoding error in some case 2018-09-13 16:49:26 +08:00
HFO4 c2a05d3f6c remove: temp file 2018-09-13 15:26:39 +08:00
HFO4 8c43f1fe1c feat: Web ui installer 2018-09-13 15:25:56 +08:00
HFO4 f81a433cbc update: to-do list and function description 2018-09-11 21:05:35 +08:00
HFO4 f2d94ea499 add: policy adding form for Onedrive 2018-09-11 20:42:07 +08:00
HFO4 ca30b650fc add: cron function to refresh token of Onedrive 2018-09-11 19:48:45 +08:00
HFO4 62eca52fe7 add: Onedrive adapter 2018-09-08 17:22:35 +08:00
HFO4 3462e6665b fix: misspelling 2018-09-08 13:53:35 +08:00
HFO4 2b646fb6e3 add: RemoteAdapter 2018-09-08 13:52:26 +08:00
HFO4 e29c94d8d0 add: Onedrive Bussiness SDK 2018-09-08 13:25:21 +08:00
HFO4 044ece0b80 add: S3Adapter & OssAdapter 2018-09-08 13:15:04 +08:00
HFO4 195d747c5d fix: error displaying capacity unit while editing user group 2018-09-07 20:48:13 +08:00
HFO4 16fe5d7f7d add: OssAdapter 2018-09-07 20:36:44 +08:00
HFO4 732d04c0a7 add QiniuAdapter 2018-09-07 19:46:54 +08:00
HFO4 6593cb7de9 add: LocalAdapter 2018-09-06 19:26:23 +08:00
HFO4 7a34a5ffb8 add: Onedrive large file uploading 2018-09-06 19:07:17 +08:00
HFO4 e989aa47d9 add: Single Onedrive reUpload handle function 2018-09-05 19:19:24 +08:00
HFO4 159ee86b60 add: Task model and database table 2018-09-05 16:43:52 +08:00
HFO4 61910ccf35 add: Onedrive 2018-09-05 15:55:51 +08:00
HFO4 1fe7be12c6 Fix: #67 2018-08-30 20:02:59 +08:00
HFO4 72ab679dd5 Fix #86 2018-08-30 18:27:40 +08:00
AaronLiuandGitHub 946408b642 Update composer.json 2018-08-20 18:19:04 +08:00
AaronLiuandGitHub 1d5f742ba7 Delete composer.lock 2018-08-20 18:12:26 +08:00
AaronLiuandGitHub 84c6e1a74c Update README.md 2018-08-20 18:11:28 +08:00
AaronLiuandGitHub a4cef20c01 Merge pull request #89 from Yuanuo/patch-03
修复X-Sendfile模式时附件文件名和Content-Type异常的问题。
2018-08-19 22:43:22 +08:00
AaronLiuandGitHub 5faea8f5ae Merge pull request #87 from Yuanuo/patch-01
分享文件列表根据分享时间排序而不应该是ID。
2018-08-19 22:42:28 +08:00
Yuanuo 899ee59c73 修复X-Sendfile模式时附件文件名和Content-Type异常的问题。
1,针对各浏览器的附件文件名的不同头信息格式。
2,解决Content-Type为text/html的问题。
2018-08-04 14:49:47 +08:00
Yuanuo 2807c1c79e PHP basename 中文乱码问题
php basename()无法获取带中文字符的文件名(乱码)
2018-08-04 14:08:59 +08:00
Yuanuo d5f5cbc05b 分享文件列表根据分享时间排序而不应该是ID。
显示分享文件列表时根据分享时间倒序排列,最新分享的文件显示在最前,因为分享顺序并不一定按照文件上传顺序决定。
2018-08-04 14:00:25 +08:00
HFO4 a1a9f7cdb0 complete thumb preivew in folder shareing page 2018-07-01 21:45:02 +08:00
HFO4 c7f7e20978 add thumb preview in folder sharing page 2018-06-30 19:44:24 +08:00
HFO4 c11710325d Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-06-30 19:28:45 +08:00
AaronLiuandGitHub a3599b399d Update README.md 2018-05-17 20:42:11 +08:00
AaronLiuandGitHub 8992627c48 Update README.md 2018-05-17 20:41:28 +08:00
HFO4 07fef77c4c 目录分享侧边连 2018-05-17 16:18:08 +08:00
HFO4 81abe89e84 修复部分情况下离线下载未完成被判定为已完成 2018-04-26 21:35:19 +08:00
HFO4 514ad474ea fix index error in admin panel 2018-04-22 22:11:06 +08:00
HFO4 b33df3da72 fix templates fliename error 2018-04-20 11:43:17 +08:00
HFO4 fd8fc70b07 fix templates fliename error 2018-04-20 11:41:09 +08:00
HFO4 91c9870f49 fix aria2 array index error 2018-04-19 23:18:02 +08:00
AaronLiuandGitHub f93edcc82b Merge pull request #65 from HFO4/dev
Dev
2018-04-19 19:46:10 +08:00
HFO4 b1f2774d41 turn off debug 2018-04-19 19:45:42 +08:00
HFO4 487f4e3139 fix maximum 2GB storage 2018-04-19 19:44:22 +08:00
AaronLiuandGitHub 9651caa219 Merge pull request #64 from HFO4/dev
remote download complete
2018-04-19 19:09:22 +08:00
HFO4 f5d69ebdae turn off debug switch 2018-04-19 19:08:06 +08:00
HFO4 36e6526259 complete remote download based on aria2 2018-04-19 19:07:22 +08:00
HFO4 334d25833c Fix #62 2018-04-18 20:40:56 +08:00
HFO4 412541a540 add list for completed remote download and fix storage check before starting downloading 2018-04-18 20:31:24 +08:00
HFO4 3326c9232e add download task list and optimize temp file recovery 2018-04-17 19:59:13 +08:00
HFO4 6d9b67ac20 add torrent download support 2018-04-16 20:47:48 +08:00
HFO4 ccbb6c7778 种子离线下载前端 2018-04-10 22:28:17 +08:00
HFO4 b8b63662ea 离线下载添加URL交互界面 2018-04-09 22:04:56 +08:00
HFO4 1b87365b8a aria2 种子下载及多文件处理 2018-04-07 15:16:57 +08:00
HFO4 ee56919747 aria2下载失败异常处理 2018-04-07 11:02:43 +08:00
AaronLiuandGitHub 4877a8e292 Merge pull request #58 from HFO4/dev
Merge Dev2
2018-04-06 21:29:21 +08:00
HFO4 1af411daee 关闭Debug,修改错误提示 2018-04-06 21:28:48 +08:00
HFO4 c4ecee4897 关闭Debug,修改错误提示 2018-04-06 21:24:56 +08:00
HFO4 f4a4ca02b5 aria2下载完成后的处理 2018-04-06 21:09:24 +08:00
AaronLiuandGitHub f3ff61cbc6 Merge pull request #56 from HFO4/dev
Merge Dev 01
2018-04-06 16:26:05 +08:00
HFO4 aabc32efb5 aria2状态查询 2018-04-06 16:21:40 +08:00
HFO4 126aacca63 离线下载请求及数据库记录 2018-04-06 10:26:10 +08:00
HFO4 2ade8861c9 aria2下载请求完成,并且修复一个远程服务端path的bug 2018-04-05 17:07:36 +08:00
HFO4 32ca1132f2 aria2初始化 2018-04-05 13:24:52 +08:00
HFO4 7536c8695b 更新remote类型接口URL 2018-03-29 21:41:40 +08:00
HFO4 622b80a680 开启Debug】 2018-03-29 21:39:04 +08:00
HFO4 0fe4e8a8db 发送测试邮件返回更多Debug信息(Fix #46) 2018-03-24 22:25:30 +08:00
HFO4 37b4357435 修复阿里云OSS公共空间无法下载文件 2018-03-23 18:23:12 +08:00
HFO4 45ea9404ba Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-03-11 19:22:47 +08:00
HFO4 764fdd7e36 优化移动端导航效果 2018-03-11 19:22:29 +08:00
AaronLiuandGitHub 219d894a08 Update README.md 2018-03-11 12:50:18 +08:00
HFO4 7d5e8fb2a1 1.0.3 2018-03-11 11:42:38 +08:00
HFO4 956deac89f 数据库升级逻辑 2018-03-11 11:39:33 +08:00
HFO4 4964f53dac 视频预览支持弹幕 2018-03-09 21:01:40 +08:00
AaronLiuandGitHub 037e6ad0b3 Update README.md 2018-03-08 22:44:06 +08:00
AaronLiuandGitHub 9c7494256c Update README.md 2018-03-08 22:43:18 +08:00
HFO4 21518300b4 定义移动端导航栏主题色 2018-03-07 20:07:25 +08:00
HFO4 99759b5a77 Fix #35 2018-03-05 19:20:12 +08:00
HFO4 d58b824d8b Fix #32 2018-03-05 19:09:19 +08:00
HFO4 33e56ca287 Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-03-05 19:03:37 +08:00
HFO4 c7901d55f0 修复 #25 2018-03-05 19:03:11 +08:00
AaronLiuandGitHub 53d01e5f6e Merge pull request #34 from Instrye/patch-1
修复 JS 获取 change 错误
2018-03-01 17:35:41 +08:00
InstryeandGitHub 4b58719e7c 修复 JS 获取 change 错误 2018-03-01 17:24:40 +08:00
AaronLiuandGitHub a82bee0216 Merge pull request #33 from Instrye/patch-1
修复 Admin删除分享文件的JS错误
2018-03-01 17:07:23 +08:00
InstryeandGitHub 0ea0902059 修复 Admi n删除分享文件的JS错误
修复 JQuery 获取 delete 对象错误
2018-03-01 17:05:05 +08:00
HFO4 a57dd79319 添加oss策略时加上http的提示 2018-02-19 17:55:16 +08:00
HFO4 228b898e9d 登录状态下访问首页跳转到文件页 2018-02-19 15:51:37 +08:00
HFO4 8f8b9df5a5 补齐.htaccess 2018-02-19 13:00:52 +08:00
HFO4 36925a63ef 删除锁文件 2018-02-18 21:51:00 +08:00
HFO4 df03461892 更新readme 2018-02-18 21:49:07 +08:00
HFO4 6361d4bd2e 修复二步验证二维码错误 2018-02-18 21:47:15 +08:00
AaronLiuandGitHub 937dfc77fd Merge pull request #18 from LinEvil/patch-1
Update TwoFactor.php
2018-02-18 21:34:43 +08:00
LinEvilandGitHub 31f1eb839f Update TwoFactor.php
安装的依赖endroid/qrcode已经弃用
请使用composer require endroid/qr-code
2018-02-18 21:32:18 +08:00
HFO4 12f7f691eb 修复因目录重名无法创建目录 2018-02-18 21:08:19 +08:00
HFO4 2d183acd0d 测试自动构建 2018-02-18 19:46:43 +08:00
HFO4 363da22bda 测试自动构建 2018-02-18 19:42:12 +08:00
HFO4 28d8d02508 测试自动构建 2018-02-18 19:39:50 +08:00
HFO4 662c3019d4 更新deploy.sh 2018-02-18 19:24:14 +08:00
HFO4 f5502b3666 添加自动打包 2018-02-18 19:20:06 +08:00
HFO4 4e550dcdb2 修复nginx无法使用sendfile 2018-02-16 11:59:09 +08:00
HFO4 59f8a9b58d 更新数据库版本 2018-02-16 10:46:07 +08:00
HFO4 b0898fcab4 支持通过sendfile下载文件 2018-02-16 10:31:53 +08:00
HFO4 8c8561dc31 拼写错误 2018-02-15 21:12:24 +08:00
HFO4 141de8ba54 修复部分情况加WebDAV无法使用 2018-02-15 21:08:26 +08:00
HFO4 159098decf 初步添加sendfile支持 2018-02-15 20:28:25 +08:00
HFO4 24789ca8a9 更新版本号 2018-02-14 18:11:57 +08:00
HFO4 544769bf04 Fix #12 2018-02-13 17:20:36 +08:00
HFO4 85119bc54d 新增发送安装反馈 2018-02-13 16:44:51 +08:00
HFO4 d3061f0526 README特性更新 2018-02-13 16:31:21 +08:00
HFO4 4492991bbb 修复一处url错误 2018-02-13 16:28:19 +08:00
HFO4 1e79428425 新增远程上传策略 2018-02-13 16:11:15 +08:00
HFO4 c28f08c210 自定远程服务端(未完成) 2018-02-10 14:14:40 +08:00
rainays 0e0f7d5258 添加登录按钮 2018-02-08 22:18:10 +08:00
HFO4 d2ca69b945 视频预览使用Dplayer 2018-02-07 14:40:46 +08:00
HFO4 9cb572f0da clear cache 2018-02-06 18:56:51 +08:00
HFO4 9994f12aa8 clear cache 2018-02-06 18:56:09 +08:00
HFO4 20b7d67adc 修复目录分享无法使用 2018-02-06 18:55:04 +08:00
HFO4 63cc404d74 修复目录分享无法使用 2018-02-06 18:54:08 +08:00
HFO4 47f334b6f4 修复部分服务器无法收到回调Authorization头 2018-02-06 17:34:49 +08:00
HFO4 1431f433d1 修复首页背景适应 2018-02-06 17:08:16 +08:00
HFO4 b6c1382e73 修复某些情况下验证码无效 2018-02-05 16:28:30 +08:00
HFO4 02ce111717 修复部分情况下安装报错 2018-02-05 13:59:19 +08:00
HFO4 02d00d9b19 更新安装说明 2018-02-05 13:27:51 +08:00
HFO4 25c86357da 权限设置 2018-02-05 12:23:31 +08:00
HFO4 bb49ff1b5f 拼写错误 2018-02-05 12:15:44 +08:00
HFO4 f33414c643 补全目录 2018-02-05 11:10:13 +08:00
HFO4 c1eebc7bf7 更新安装说明 2018-02-05 11:02:24 +08:00
HFO4 90a0b34b3e 安装程序执行发起 2018-02-05 10:52:09 +08:00
HFO4 60cd53895d 安装引导脚本 2018-02-05 10:48:11 +08:00
HFO4 a97df81a9e Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-02-05 08:41:37 +08:00
HFO4 3fdc327bb0 ready for merge 2018-02-05 08:41:30 +08:00
AaronLiuandGitHub d02899328d Update .htaccess 2018-02-05 07:33:04 +08:00
AaronLiuandGitHub 34767dd9b0 Merge pull request #4 from yrccondor/master
Change lang="en" to lang="zh_cn"
2018-02-04 20:16:12 +08:00
AxtonYaoandGitHub 85609ddbd8 fix 2018-02-04 20:12:10 +08:00
AxtonYaoandGitHub d6aa3ca86f Change lang="en" to lang="zh_cn" 2018-02-04 20:05:52 +08:00
AxtonYaoandGitHub 9e33129e7d Change lang="en" to lang="zh_cn" 2018-02-04 20:04:41 +08:00
HFO4 8c54632ccd Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-02-04 19:25:27 +08:00
HFO4 dda74f16ce Fix #2 2018-02-04 19:23:40 +08:00
AaronLiuandGitHub 69b540708e Update README.md 2018-02-04 18:29:59 +08:00
HFO4 f290658cd0 删除注释 2018-02-04 17:51:27 +08:00
HFO4 842f7bf052 修复找回密码表单判断 2018-02-04 17:45:53 +08:00
AaronLiuandGitHub 7998513e4e Update README.md 2018-02-04 17:22:05 +08:00
AaronLiuandGitHub dd3552fefd Update README.md 2018-02-04 17:20:58 +08:00
AaronLiuandGitHub f238dc14ca Delete database.php 2018-02-04 16:15:50 +08:00
AaronLiuandGitHub 865538f437 Update README.md 2018-02-04 16:09:34 +08:00
AaronLiuandGitHub c6fbca185d Update README.md 2018-02-04 15:53:23 +08:00
AaronLiuandGitHub 19fb84efa8 Update README.md 2018-02-04 15:44:11 +08:00
AaronLiuandGitHub 0be66f7897 Update README.md 2018-02-04 15:43:47 +08:00
AaronLiuandGitHub ebb99e9515 Update README.md 2018-02-04 15:33:12 +08:00
AaronLiuandGitHub 6f911908ed Update README.md 2018-02-04 15:32:37 +08:00
AaronLiuandGitHub d163808d45 更新Readme 2018-02-04 15:25:11 +08:00
115 changed files with 11489 additions and 2746 deletions
+2 -1
View File
@@ -4,5 +4,6 @@
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
</IfModule>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+7
View File
@@ -0,0 +1,7 @@
language: php
php:
- 7.1
before_script:
- composer install
- chmod +x ./deploy.sh
script: ./deploy.sh
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace CloudreveInstaller;
use Composer\Script\Event;
class Installer{
public static function startInstall(Event $event){
$version = json_decode(file_get_contents("application/version.json"),true)["version"];
$ioContext = $event->getIO();
$welcomMsg = "
___ _ _
/ __\ | ___ _ _ __| |_ __ _____ _____
/ / | |/ _ \| | | |/ _` | '__/ _ \ \ / / _ \
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver $version
================================================
";
$ioContext->write($welcomMsg);
$sqlInfo = self::getSqlInformation($event);
$ioContext->write("");
$siteUrl=$ioContext->ask("The full-url to access to your Cloudreve (e.g. https://pan.aoaoao.me/ , 'http' must be included in the front and '/' must be included at the end):");
$ioContext->write("");
if(!file_exists('mysql.sql')){
$ioContext->writeError("[Error] The file mysql.sql not exist.\nInstaller will exit.To retry, run 'composer install'");
exit();
}
$sqlSource = file_get_contents('mysql.sql');
$sqlSource = str_replace("https://cloudreve.org/", $siteUrl, $sqlSource);
$mysqli = @new \mysqli($sqlInfo["hostname"], $sqlInfo["username"], $sqlInfo["password"], $sqlInfo["database"], $sqlInfo["hostport"]);
$ioContext->write("=======================");
$ioContext->write("Starting import sql file...");
if ($mysqli->multi_query($sqlSource)) {
$ioContext->write("Writing complete.");
$ioContext->write("Writing database.php...");
if(file_exists('application/database.php')){
$ioContext->writeError("[Error] The file database.php already exist.\nInstaller will exit.To retry, run 'composer install'");
$ioContext->write("=======================");
exit();
}
self::writrConfig($event,$sqlInfo);
$ioContext->write("=======================");
}else{
$ioContext->writeError("[Error] Writing failed.Installer will exit. To retry, run 'composer install'");
$ioContext->write("=======================");
}
$ioContext->write("");
$ioContext->write("Congratulations! Cloudreve has been installed successfully.");
$ioContext->write("");
$ioContext->write("Here's some informatioin about yor Cloudreve:");
$ioContext->write("Homepage: $siteUrl");
$ioContext->write("Admin Panel: ".$siteUrl."Admin");
$ioContext->write("Default username: admin@cloudreve.org");
$ioContext->write("Default password: admin");
$ioContext->write("");
$ioContext->write("=======================");
$ioContext->write("IMPORTANT! You may still have to configure the URL Rewrite to set everthing to work.");
$ioContext->write("Refer to the install manual for more informatioin.");
$ioContext->write("=======================");
self::sendFeedBack($siteUrl);
}
public static function writrConfig(Event $event,$sqlInfo){
$ioContext = $event->getIO();
try {
$fileContent = file_get_contents("CloudreveInstaller/database_sample.php");
$replacement = array(
'{hostname}' => $sqlInfo["hostname"],
'{database}' => $sqlInfo["database"],
'{username}' => $sqlInfo["username"],
'{password}' => $sqlInfo["password"],
'{hostport}' => $sqlInfo["hostport"],
);
$fileContent = strtr($fileContent,$replacement);
file_put_contents('application/database.php',$fileContent);
}catch (Exception $e) {
$ioContext->writeError("[Error] Writing failed.Installer will exit. To retry, run 'composer install'");
}
$ioContext->write("Writing complete.");
}
public static function getSqlInformation(Event $event){
$ioContext = $event->getIO();
$hostname=$ioContext->ask("Input the hostname of your MySQL server (Default:127.0.0.1):","127.0.0.1");
$database=$ioContext->ask("The database name:","127.0.0.1");
$username=$ioContext->ask("The username of your MySQL server (Default:root):","root");
$password=$ioContext->ask("The password of your MySQL server:");
$hostport=$ioContext->ask("The hostport of your MySQL server (Default:3306):","3306");
$mysqli = @new \mysqli($hostname, $username, $password, $database, $hostport);
if ($mysqli->connect_error) {
$ioContext->writeError("[Error] Cannot connect to MySQL server, Message:".$mysqli->connect_error);
$ioContext->write("");
$ioContext->write("Please confirm your connection informatioin:");
@$mysqli->close();
return self::getSqlInformation($event);
}
return [
"hostname" => $hostname,
"database" => $database,
"username" => $username,
"password" => $password,
"hostport" => $hostport,
];
}
public static function sendFeedBack($url){
@file_get_contents("http://toy.aoaoao.me/feedback.php?url=".urlencode($url));
}
}
?>
@@ -13,15 +13,15 @@ return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '127.0.0.1',
'hostname' => '{hostname}',
// 数据库名
'database' => 'lite',
'database' => '{database}',
// 用户名
'username' => 'root',
'username' => '{username}',
// 密码
'password' => 'root',
'password' => '{password}',
// 端口
'hostport' => '3306',
'hostport' => '{hostport}',
// 连接dsn
'dsn' => '',
// 数据库连接参数
+473
View File
@@ -0,0 +1,473 @@
<?php
if(file_exists('../application/database.php')){
echo "application/database.php 已存在,请备份并删除后再试";
exit();
}
if(isset($_POST["mysqlServer"])){
error_reporting(0);
header('Content-Type:application/json; charset=utf-8');
if(!file_exists('../mysql.sql')){
echo json_encode(["error"=>true,"msg"=>"找不到mysql.sql"]);
exit();
}
$sqlSource = file_get_contents('../mysql.sql');
$sqlSource = str_replace("https://cloudreve.org/", $_POST["siteUrl"], $sqlSource);
$mysqli = @new \mysqli($_POST["mysqlServer"], $_POST["mysqlUser"], $_POST["mysqlPwd"], $_POST["mysqlDb"], (int)$_POST["mysqlPort"]);
if ($mysqli->connect_error) {
@$mysqli->close();
echo json_encode(["error"=>true,"msg"=>$mysqli->connect_error]);
exit();
}
if (!$mysqli->multi_query($sqlSource)) {
echo json_encode(["error"=>true,"msg"=>"无法写入数据表"]);
exit();
}
if(file_exists('../application/database.php')){
echo json_encode(["error"=>true,"msg"=>"application/database.php 已存在,请备份并删除后再试"]);
exit();
}
try {
$fileContent = file_get_contents("database_sample.php");
$replacement = array(
'{hostname}' => $_POST["mysqlServer"],
'{database}' => $_POST["mysqlDb"],
'{username}' => $_POST["mysqlUser"],
'{password}' => $_POST["mysqlPwd"],
'{hostport}' => $_POST["mysqlPort"],
);
$fileContent = strtr($fileContent,$replacement);
file_put_contents('../application/database.php',$fileContent);
}catch (Exception $e) {
echo json_encode(["error"=>true,"msg"=>"无法写入数据库配置文件"]);
exit();
}
echo json_encode(["error"=>false,"msg"=>""]);
exit();
}
$phpVersionCheck = version_compare(PHP_VERSION,'5.6.0', '>');
$success = '<span style="color: #009688;"><i class="fa fa-check-circle" aria-hidden="true"></i> 满足</span>';
$error = '<span style="color: #F44336;"><i class="fa fa-times-circle" aria-hidden="true"></i> 不满足</span>';
$runtimeDirCheck = is_writable("../runtime");
$applicationDirCheck = is_writable("../application");
$publicDownloadsDirCheck = is_writable("../public/downloads");
$publicAvatarsDirCheck = is_writable("../public/avatars");
$publicThumbDirCheck = is_writable("../public/thumb");
$publicUploadsDirCheck = is_writable("../public/uploads");
$publicUploadsChunksDirCheck = is_writable("../public/uploads/chunks");
$curlCheck = extension_loaded("curl");
$pdoCheck = extension_loaded("pdo");
$fileinfoCheck = extension_loaded("fileinfo");
$gdCheck = extension_loaded("gd");
$thinkCaptchaCheck = is_dir("../vendor/topthink/think-captcha");
$ossCheck = is_dir("../vendor/aliyuncs/oss-sdk-php");
$davCheck = is_dir("../vendor/sabre/dav");
$upyunCheck = is_dir("../vendor/upyun/sdk");
$googleauthenticatorCheck = is_dir("../vendor/phpgangsta/googleauthenticator");
$qrcodeCheck = is_dir("../vendor/endroid/qrcode");
$isOk = $phpVersionCheck && $runtimeDirCheck && $applicationDirCheck && $publicAvatarsDirCheck && $curlCheck && $pdoCheck && $fileinfoCheck;
?>
<html lang="zh-cn" data-ng-app="FileManagerApp">
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<meta name="theme-color" content="#4e64d9"/>
<title>安装向导- Cloudreve</title>
<!-- third party -->
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/material.css" />
<script src="/static/js/material.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<!-- /third party -->
<!-- Comment if you need to use raw source code -->
<link href="/static/css/toastr.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<!-- /Comment if you need to use raw source code -->
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" style="background-color: rgb(78, 100, 217);">
<div class="container-fluid">
<div class="container">
<div class="navbar-header">
<div>
<a class="navbar-brand waves-light waves-effect waves-block" href="/">
</a>
</div>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</div>
</nav>
<div class="container" id="enviromentCheck">
<h1>环境检查</h1><br>
<div class="panel panel-default">
<div class="panel-heading">基本环境</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>项目</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>当前</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $phpVersionCheck?"":"class='danger'"; ?>>
<td >1</td>
<td>PHP版本 >= 5.6</td>
<td>满足Cloudreve基本需求的最低PHP版本为5.6</td>
<td>必须</td>
<td><?php echo phpversion(); ?></td>
<td><?php echo $phpVersionCheck?$success:$error; ?></td>
</tr>
<tr id="rewriteCheck">
<td >2</td>
<td>URL Rewrite</td>
<td>服务器需正确配置URL重写规则(伪静态),否则各个页面将会返回404错误</td>
<td>必须</td>
<td id="rewriteStatus"></td>
<td>
<span id="rewriteSuccess" style="display:none"><?php echo $success?></span>
<span id="rewriteError" style="display:none"><?php echo $error?></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">读写权限</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>目录</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $runtimeDirCheck?"":"class='danger'"; ?>>
<td >1</td>
<td>runtime 可读写</td>
<td>runtime用于存放系统工作产生的临时文件、日志、缓存等数据</td>
<td>必须</td>
<td><?php echo $runtimeDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $applicationDirCheck?"":"class='danger'"; ?>>
<td >2</td>
<td>application 可读写</td>
<td>application用于安装程序写入数据库配置文件,仅安装时需要写入权限</td>
<td>必须(临时)</td>
<td><?php echo $applicationDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicAvatarsDirCheck?"":"class='danger'"; ?>>
<td >3</td>
<td>public/avatars 可读写</td>
<td>用于存放用户头像</td>
<td>必须</td>
<td><?php echo $publicAvatarsDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicUploadsDirCheck?"":"class='warning'"; ?>>
<td >4</td>
<td>public/uploads 可读写</td>
<td>用于存放本地策略上传的文件数据</td>
<td>可选</td>
<td><?php echo $publicUploadsDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicUploadsChunksDirCheck?"":"class='warning'"; ?>>
<td >5</td>
<td>public/uploads/chunks 可读写</td>
<td>用于存放本地策略上传文件的临时分片数据</td>
<td>可选</td>
<td><?php echo $publicUploadsChunksDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicDownloadsDirCheck?"":"class='warning'"; ?>>
<td >6</td>
<td>public/downloads 可读写</td>
<td>用于存放离线下载的文件数据</td>
<td>可选</td>
<td><?php echo $publicDownloadsDirCheck?$success:$error; ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">PHP扩展</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>扩展名</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $curlCheck?"":"class='danger'"; ?>>
<td >1</td>
<td>curl</td>
<td>发送网络请求</td>
<td>必须</td>
<td><?php echo $curlCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $pdoCheck?"":"class='danger'"; ?>>
<td >2</td>
<td>pdo</td>
<td>数据库操作</td>
<td>必须</td>
<td><?php echo $pdoCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $fileinfoCheck?"":"class='warnging'"; ?>>
<td >3</td>
<td>fileinfo</td>
<td>用于处理本地策略图像文件预览、用户头像展示</td>
<td>推荐</td>
<td><?php echo $fileinfoCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $gdCheck?"":"class='warnging'"; ?>>
<td >4</td>
<td>gd</td>
<td>用于生成验证码</td>
<td>推荐</td>
<td><?php echo $gdCheck?$success:$error; ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">依赖库</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>库名</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $thinkCaptchaCheck?"":"class=''"; ?>>
<td >1</td>
<td>think-captcha</td>
<td>生成验证码图像</td>
<td>可选</td>
<td><?php echo $thinkCaptchaCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $ossCheck?"":"class=''"; ?>>
<td >2</td>
<td>oss-sdk-php</td>
<td>阿里云OSS上传策略需要使用</td>
<td>可选</td>
<td><?php echo $ossCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $davCheck?"":"class=''"; ?>>
<td >3</td>
<td>dav</td>
<td>WebDAV功能需要使用</td>
<td>可选</td>
<td><?php echo $davCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $upyunCheck?"":"class=''"; ?>>
<td >4</td>
<td>upyun/sdk</td>
<td>又拍云上传策略需要使用</td>
<td>可选</td>
<td><?php echo $upyunCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $googleauthenticatorCheck?"":"class=''"; ?>>
<td >5</td>
<td>googleauthenticator</td>
<td>二步验证</td>
<td>可选</td>
<td><?php echo $googleauthenticatorCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $qrcodeCheck?"":"class=''"; ?>>
<td >5</td>
<td>endroid/qrcode</td>
<td>用于生成二步验证的二维码</td>
<td>可选</td>
<td><?php echo $qrcodeCheck?$success:$error; ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<div style="text-align:right;"><button class="btn btn-lg btn-primary btn-raised" id="doInstall"><?php echo $isOk?"下一步":"忽略问题,继续下一步"; ?></button></div>
</div>
<div class="container" id="installSuccess" style="display:none">
<div class="jumbotron">
<h2>安装完成</h2>
<p>您的Cloudreve站点初始管理员信息如下,请登陆后修改默认密码和邮箱。</p>
<div class="form-group">
<label for="adminUrl" class="col-md-2 control-label">管理后台地址</label>
<div class="col-md-10">
<input type="text" class="form-control" id="adminUrl" name="adminUrl" value="<?php $url='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"];
$mulu= dirname($url);
echo $mulu."/Admin";
?>">
</div>
</div>
<div class="form-group">
<label for="admin" class="col-md-2 control-label">管理员账号</label>
<div class="col-md-10">
<input type="text" class="form-control" id="admin" name="admin" value="admin@cloudreve.org">
</div>
</div>
<div class="form-group">
<label for="adminPwd" class="col-md-2 control-label">管理员密码</label>
<div class="col-md-10">
<input type="text" class="form-control" id="adminPwd" name="adminPwd" value="admin">
</div>
</div>
<br><br><br><br>
</div>
</div>
<div class="container" id="installForm" style="display:none">
<div class="jumbotron">
<h2>信息填写</h2>
<form id="setUpInfo">
<div class="form-group">
<label for="siteUrl" class="col-md-2 control-label">站点URL</label>
<div class="col-md-10">
<input type="text" class="form-control" id="siteUrl" name="siteUrl" placeholder="结尾需要加 / " value="<?php $url='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"];
$mulu= dirname($url);
echo $mulu."/";
?>">
</div>
</div>
<div class="form-group">
<label for="mysqlServer" class="col-md-2 control-label">MySQL服务器</label>
<div class="col-md-10">
<input type="text" class="form-control" id="mysqlServer" name="mysqlServer" value="localhost">
</div>
</div>
<div class="form-group">
<label for="mysqlPort" class="col-md-2 control-label">MySQL端口</label>
<div class="col-md-10">
<input type="number" class="form-control" id="mysqlPort" name="mysqlPort" value="3306">
</div>
</div>
<div class="form-group">
<label for="mysqlUser" class="col-md-2 control-label">MySQL用户名</label>
<div class="col-md-10">
<input type="text" class="form-control" id="mysqlUser" name="mysqlUser" value="root">
</div>
</div>
<div class="form-group">
<label for="mysqlPwd" class="col-md-2 control-label">MySQL密码</label>
<div class="col-md-10">
<input type="password" class="form-control" id="mysqlPwd" name="mysqlPwd">
</div>
</div>
<div class="form-group">
<label for="mysqlDb" class="col-md-2 control-label">数据库名</label>
<div class="col-md-10">
<input type="text" class="form-control" id="mysqlDb" name="mysqlDb" >
</div>
</div><br>
<div style="text-align:right;"><button type="button" class="btn btn-lg btn-primary btn-raised" id="startInstall">开始安装</button></div>
</div>
<br><br><br><br>
</form>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script type="text/javascript">
$.material.init();
$.get("/Member", function(result){
$("#rewriteStatus").html("正常");
$("#rewriteSuccess").show();
}).error(function(){
$("#rewriteStatus").html("异常");
$("#rewriteError").show();
$("#rewriteCheck").addClass("danger");
});
$("#doInstall").click(function(){
$("#enviromentCheck").fadeOut();
$("#installForm").fadeIn();
})
$("#startInstall").click(function(){
$.post("index.php",$("#setUpInfo").serialize(),function(data){
console.log(data);
if(data.error == true){
toastr["error"](data.msg);
}else{
$("#installForm").fadeOut();
$("#installSuccess").fadeIn();
}
}).error(function(){
toastr["error"]("安装出现未知错误");
})
})
</script>
+78 -7
View File
@@ -2,18 +2,21 @@
Cloudreve - Make the cloud easy for everyone
=========================
[![Latest Stable Version](https://poser.pugx.org/hfo4/cloudreve/v/stable)](https://packagist.org/packages/hfo4/cloudreve)
[![Packagist](https://img.shields.io/packagist/v/HFO4/Cloudreve.svg)](https://packagist.org/packages/hfo4/cloudreve)
[![Latest Unstable Version](https://poser.pugx.org/hfo4/cloudreve/v/unstable)](https://packagist.org/packages/hfo4/cloudreve)
[![License](https://poser.pugx.org/hfo4/cloudreve/license)](https://packagist.org/packages/hfo4/cloudreve)
[主页](https://cloudreve.org) | [论坛](https://forum.cloudreve.org) | [演示站](https://pan.aoaoao.me) | [QQ群](https://jq.qq.com/?_wv=1027&k=5TX6sJY) |[Telegram群组](https://t.me/cloudreve)
基于ThinkPHP构建的网盘系统,能够助您以较低成本快速搭建起公私兼备的网盘。
![TIM截图20180204124101.png](https://download.aoaoao.me/TIM截图20180204124101.png)
![homepage.png](https://download.aoaoao.me/homepage-linux.png)
目前已经实现的特性:
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3,当然,还有本地存储
* 可限制单文件最大大小、MMMEType、文件后缀、用户可用容量
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3、Onedrive、自建远程服务器,当然,还有本地存储
* 可限制单文件最大大小、MIMEType、文件后缀、用户可用容量
* 基于Aria2的离线下载
* 图片、音频、视频、文本、Markdown、Ofiice文档 在线预览
* 移动端全站响应式布局
* 文件、目录分享系统,可创建私有分享或公开分享链接
@@ -22,19 +25,87 @@ Cloudreve - Make the cloud easy for everyone
* 初步完善的后台,方便管理
* 拖拽上传、分片上传、断点续传、下载限速(*实验性功能)
* 多上传策略,可为不同用户组分配不同策略
* 用户组基础权限设置
* 用户组基础权限设置、二步验证
* WebDAV协议支持
To-do:
* - [x] 重写目录分享和单文件分享页面样式
* - [x] 增加保存其他用户的分享到自己账户(限Pro版)
* - [x] 推出辅助程序,并借此实现:
* - [ ] 压缩包解压缩、文件压缩
* - [ ] 对接Ondrive、Google Drive,上传模式为先上到自己服务器,然后中转
安装需求
------------
* LNMP/LAMP With PHP5.6+
* LNMP/AMP With PHP5.6+
* curl、fileinfo、gd扩展
* Composer
简要安装说明
------------
Coming Soon...
#### 1.使用Composer安装主程序
```
#安装开发版
$ composer create-project hfo4/cloudreve:dev-master
```
```
#等待安装依赖库后,会自动执行安装脚本,按照提示输入数据库账户信息
___ _ _
/ __\ | ___ _ _ __| |_ __ _____ _____
/ / | |/ _ \| | | |/ _` | '__/ _ \ \ / / _ \
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver XX
================================================
#按提示输入信息
......
```
```
#出现如下提示表示安装完成
Congratulations! Cloudreve has been installed successfully.
Here's some informatioin about yor Cloudreve:
Homepage: https://pan.aoaoao.me/
Admin Panel: https://pan.aoaoao.me/Admin
Default username: admin@cloudreve.org
Default password: admin
```
#### 2.目录权限
`runtime`目录需要写入权限,如果你使用本地存储,`public` 目录也需要有写入权限
#### 3.URL重写
对于Apache服务器,项目目录下的`.htaccess`已经配置好重写规则,如有需求酌情修改.
对于Nginx服务器,以下是一个可供参考的配置:
```
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
break;
}
}
```
#### 4.完成
后台地址:`http://您的域名/Admin` 初始用户名:`admin@cloudreve.org` 初始密码:`admin`
#### 后续操作
以下操作不是必须的,但仍推荐你完成这些操作:
* 修改初始账户密码
* 到 设置-基础设置 中更改站点URL,如果不更改,程序无法正常接受回调请求
* 添加Crontab定时任务 :你的域名/Cron
* 如果你打算使用本地上传策略并且不准备开启外链功能,请将·public/uploads·目录设置为禁止外部访问
* 如需启用二步验证功能,请依次执行`composer require phpgangsta/googleauthenticator:dev-master` `composer require endroid/qr-code`安装二步验证支持库
* 给本项目一个Star~
文档
------------
* [完整安装说明](https://github.com/HFO4/Cloudreve/wiki/%E5%AE%89%E8%A3%85%E8%AF%B4%E6%98%8E)
* [安装及初次使用FAQ](https://github.com/HFO4/Cloudreve/wiki/%E5%AE%89%E8%A3%85%E5%8F%8A%E5%88%9D%E6%AC%A1%E4%BD%BF%E7%94%A8FAQ)
许可证
------------
+1
View File
@@ -0,0 +1 @@
database.php
+3 -1
View File
@@ -9,4 +9,6 @@
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
return [];
return [
'app\index\command\Task',
];
+1 -1
View File
@@ -156,7 +156,7 @@ return [
'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
'error_message' => '很抱歉,出现错误 :(',
// 显示错误信息
'show_error_msg' => false,
// 异常处理handle类 留空使用 \think\exception\Handle
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace app\index\command;
use think\Db;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class Task extends Command
{
const SLEEP_TIME = 1;
protected function configure()
{
$this->setName('run')->setDescription('Start processing tasks for Cloudreve');
}
protected function Init(Output $output){
$output->writeln("Cloudreve tasks processor started.");
}
protected function setComplete($taskId,Output $output){
$output->writeln("Cloudreve tasks processor started.");
}
protected function execute(Input $input, Output $output)
{
self::Init($output);
while (1){
$newTaskInfo = Db::name("task")->where("status","todo")->find();
if(empty($newTaskInfo)){
sleep(self::SLEEP_TIME);
continue;
}
Db::name("task")->where("id",$newTaskInfo["id"])->update(["status"=>"processing"]);
$output->writeln("[New task] Name:".$newTaskInfo["task_name"]." Type:".$newTaskInfo["type"]);
$task = new \app\index\model\Task();
$task->taskModel = $newTaskInfo;
$task->input = $input;
$task->output = $output;
$task->Doit();
if($task->status=="error"){
$output->writeln("[Error] ".$task->errorMsg);
Db::name("task")->where("id",$newTaskInfo["id"])->update(["status"=>"error|".$task->errorMsg]);
}else{
$output->writeln("[Complete]");
Db::name("task")->where("id",$newTaskInfo["id"])->update(["status"=>"complete"]);
}
}
}
}
?>
+106 -1
View File
@@ -29,12 +29,50 @@ class Admin extends Controller{
}
public function index(){
if($this->adminObj->checkDbVersion()){
$this->redirect(url('/Admin/UpdateDb','',''));
exit();
}
if($this->adminObj->checkCron()){
$isCronOk = true;
}else{
$isCronOk = false;
}
return view('admin_index', [
'options' => $this->siteOptions,
'statics' => $this->adminObj->getStatics(),
'isCronOk' => $isCronOk,
]);
}
public function UpdateDb(){
if(!$this->adminObj->checkDbVersion()){
$this->redirect(url('/Admin','',''));
exit();
}
echo "<meta charset='utf-8'>";
echo "准备升级数据库,当前数据库版本:".$this->adminObj->dbVerInfo["now"];
echo ", 目标数据库版本:".$this->adminObj->dbVerInfo["require"].";<br>";
$updatePath = ROOT_PATH . "update_".$this->adminObj->dbVerInfo["now"]."to".$this->adminObj->dbVerInfo["require"].".sql";
if(!file_exists($updatePath)){
die("数据库更新文件(".$updatePath.")不存在,升级中止.");
}
echo "获取升级SQL文件(".$updatePath.")<br>";
$updateContent = file_get_contents($updatePath);
echo "将执行以下指令:<br>";
echo "<code>".htmlspecialchars($updateContent)."</code><br>";
$sqlSingle = explode(";", $updateContent);
foreach ($sqlSingle as $key => $value){
if(empty($value)){
continue;
}
if(!Db::execute($value)){
echo "<strong>执行$value 时出现错误</strong><br>";
}
}
echo "<strong>升级完成,<a href='/Admin'>返回管理面板</a></strong>";
}
public function Setting(){
return view('basic_setting', [
'options' => $this->siteOptions,
@@ -120,7 +158,7 @@ class Admin extends Controller{
public function SettingOther(){
return view('other_setting', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["file_edit","share","avatar","admin","storage_policy"]),
'optionsForSet' => Option::getValues(["file_edit","share","avatar","admin","storage_policy","download"]),
]);
}
@@ -216,6 +254,14 @@ class Admin extends Controller{
return $this->adminObj->saveMailSetting(input('post.'));
}
public function SaveAria2Setting(){
return $this->adminObj->saveAria2Setting(input('post.'));
}
public function SaveTaskOption(){
return $this->adminObj->saveTaskOption(input('post.'));
}
public function SendTestMail(){
return $this->adminObj->sendTestMail(input('post.'));
}
@@ -409,6 +455,18 @@ class Admin extends Controller{
]);
}
public function PolicyAddRemote(){
return view('add_policy_remote', [
'options' => $this->siteOptions,
]);
}
public function PolicyAddOnedrive(){
return view('add_policy_onedrive', [
'options' => $this->siteOptions,
]);
}
public function About(){
$verison = json_decode(file_get_contents(ROOT_PATH . "application/version.json"),true);
return view('about', [
@@ -426,6 +484,15 @@ class Admin extends Controller{
]);
}
public function Queue(){
$taskOption = Option::getValue("task_queue_token");
return view('task', [
'options' => $this->siteOptions,
'taskOption' => $taskOption,
'task' => $this->adminObj->getTasks(),
]);
}
public function PurchaseGroup(){
$groupData = json_decode(Option::getValue("group_sell_data"),true);
foreach ($groupData as $key => $value) {
@@ -444,5 +511,43 @@ class Admin extends Controller{
'policy' => $this->adminObj->getAvaliablePolicy(),
]);
}
public function RemoteDownload(){
$this->adminObj->listDownloads();
return view('download', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["aria2"]),
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function CancelDownload(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new \app\index\model\Aria2($aria2Options);
$downloadItem = Db::name("download")->where("id",input("post.id"))->find();
if(empty($downloadItem)){
return json(['error'=>1,'message'=>"未找到下载记录"]);
}
if($aria2->Remove($downloadItem["pid"],"")){
return json(['error'=>0,'message'=>"下载已取消"]);
}else{
return json(['error'=>1,'message'=>"取消失败"]);
}
}
public function UpdateOnedriveToken(){
$policyId = input("get.id");
$this->adminObj->updateOnedriveToken($policyId);
}
public function OneDriveCalllback(){
$code = input("get.code");
$this->adminObj->oneDriveCalllback($code);
}
}
@@ -25,6 +25,7 @@ class Callback extends Controller{
public function Oss(){
ob_end_clean();
error_log("sadasdasdsadsasadasasdasdasd");
header('Content-Type: application/json');
$handllerObj = new CallbackHandler(file_get_contents("php://input"));
$handllerObj -> ossHandler(Request::instance()->header('Authorization'),Request::instance()->header('x-oss-pub-key-url'));
@@ -81,4 +82,11 @@ class Callback extends Controller{
$handllerObj -> s3Handler($callbackKey);
}
public function Remote(){
ob_end_clean();
header('Content-Type: application/json');
$handllerObj = new CallbackHandler(file_get_contents("php://input"));
$handllerObj -> remoteHandler(Request::instance()->header('Authorization'));
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ class Explore extends Controller{
$list = Db::name('shares')
->where('type',"public")
->where('origin_name',"like","%".$keyWords."%")
->order('id DESC')
->order('share_time DESC')
->paginate(10);
$listData = $list->all();
foreach ($listData as $key => $value) {
+11 -1
View File
@@ -38,6 +38,16 @@ class Home extends Controller{
]);
}
public function Download(){
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
return view('download', [
'options' => Option::getValues(['basic','group_sell']),
'userInfo' => $userInfo,
'groupData' => $groupData,
]);
}
public function Album(){
$userInfo = $this->userObj->getInfo();
$list = Db::name("files")->where("upload_user",$this->userObj->uid)
@@ -60,7 +70,7 @@ class Home extends Controller{
$listData = $list->all();
$pageNow = input("?get.page")?input("get.page"):1;
if($pageNow>$pageCount){
$this->error('页面不存在',404,Option::getValues(['basic','group_sell']));
$this->error('您当前没有上传任何图片',404,Option::getValues(['basic','group_sell']));
}
return view('album', [
'options' => Option::getValues(['basic','group_sell']),
+4
View File
@@ -13,6 +13,10 @@ class Index extends Controller{
public function index(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if($this->userObj->loginStatus){
$this->redirect(url('/Home','',''));
exit();
}
$userInfo = $this->userObj->getInfo();
return view('index', [
'options' => Option::getValues(['basic']),
+2 -2
View File
@@ -19,7 +19,7 @@ class Member extends Controller{
* @return [type] [description]
*/
public function index(){
echo "hello";
echo "Pong";
}
/**
@@ -39,7 +39,7 @@ class Member extends Controller{
}
public function ForgetPwd(){
if(input('?post.regEmail') && input('?post.captchaCode') && !empty(input('post.captchaCode')) && !empty(input('post.regEmail'))){
if(input('?post.regEmail') && !empty(input('post.regEmail'))){
$findAction = User::findPwd(input('post.regEmail'),input('post.captchaCode'));
if ($findAction[0]){
return json(['code' => '200','message' => $findAction[1]]);
+2 -2
View File
@@ -35,7 +35,7 @@ class Profile extends Controller{
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('id DESC')
->order('share_time DESC')
->paginate(10);
break;
case 'hot':
@@ -49,7 +49,7 @@ class Profile extends Controller{
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('id DESC')
->order('share_time DESC')
->paginate(10);
break;
}
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\Task;
use \app\index\model\Option;
class Queue extends Controller{
public function __construct(\think\Request $request = null){
$token = Option::getValue("task_queue_token");
if($token==""){
abort(403);
}
if(Request::instance()->header("Authorization") !="Bearer ".$token){
abort(403);
}
}
public function index(){
}
public function basicInfo(){
return json_encode([
"basePath" => ROOT_PATH,
]);
}
public function getList(){
$size = input("get.num");
$tasks = Db::name("task")->where("status","todo")->limit($size)->select();
$taskID = array_column($tasks,"id");
Db::name("task")->where("id","in",$taskID)->update(["status"=>"processing"]);
if(empty($tasks)){
return "none";
}else{
return json_encode($tasks);
}
}
public function getPolicy(){
$id = input("get.id");
$policy = Db::name("policy")->where("id",$id)->find();
if(empty(($policy))){
abort(404);
}else{
return json($policy);
}
}
public function setSuccess(){
$id = input("get.id");
$task = new Task($id);
$task->taskModel = Db::name("task")
->where("id",$id)
//->where("status","processing")
->find();
if(empty($task->taskModel)){
return json(["error"=>true,"msg"=>"未找到任务"]);
}
return json($task->setSuccess());
}
public function setError(){
$id = input("get.id");
$task = new Task($id);
$task->taskModel = Db::name("task")
->where("id",$id)
//->where("status","processing")
->find();
if(empty($task->taskModel)){
return json(["error"=>true,"msg"=>"未找到任务"]);
}
return json($task->Error());
}
}
@@ -0,0 +1,179 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use \app\index\model\Aria2;
use think\Session;
class RemoteDownload extends Controller{
public $userObj;
public function _initialize(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if(!$this->userObj->loginStatus){
echo "Bad request";
exit();
}
}
private function checkPerimission($permissionId){
$permissionData = $this->userObj->groupData["aria2"];
if(explode(",",$permissionData)[$permissionId] != "1"){
return false;
}
return true;
}
private function insertRecord($aria2,$url,$path){
Db::name("download")->insert([
"pid" => $aria2->pid,
"path_id" => $aria2->pathId,
"owner" => $this->userObj->uid,
"save_dir" => $path,
"status" => "ready",
"msg" => "",
"info"=>"",
"source" =>$url,
"file_index" => 0,
"is_single" => 1,
"total_size" => 0,
]);
}
public function addUrl(){
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$this->checkPerimission(0) || ($policyData["policy_type"] != "local" && $policyData["policy_type"] != "onedrive")){
return json(["result"=>['success'=>false,'error'=>"您当前的无用户无法执行此操作"]]);
}
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$downloadStart = $aria2->addUrl(input("post.url"));
if($aria2->reqStatus){
$this->insertRecord($aria2,input("post.url"),input("post.path"));
return json(["result"=>['success'=>true,'error'=>null]]);
}else{
return json(["result"=>['success'=>false,'error'=>$aria2->reqMsg]]);
}
}
public function AddTorrent(){
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$this->checkPerimission(0) || $policyData["policy_type"] != "local"){
return json(['error'=>1,'message'=>'您当前的无用户无法执行此操作']);
}
$downloadingLength = Db::name("download")
->where("owner",$this->userObj->uid)
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
->sum("total_size");
if(!\app\index\model\FileManage::sotrageCheck($this->userObj->uid,$downloadingLength)){
return json(["result"=>['success'=>false,'error'=>"容量不足"]]);
}
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$torrentObj = new \app\index\model\FileManage(input("post.id"),$this->userObj->uid,true);
$downloadStart = $aria2->addTorrent($torrentObj->signTmpUrl());
if($aria2->reqStatus){
$this->insertRecord($aria2,input("post.id"),input("post.savePath"));
return json(["result"=>['success'=>true,'error'=>null]]);
}else{
return json(["result"=>['success'=>false,'error'=>$aria2->reqMsg]]);
}
}
public function FlushStatus(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
if(!input("?post.id")){
return json(['error'=>1,'message'=>"信息不完整"]);
}
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$aria2->flushStatus(input("post.id"),$this->userObj->uid,$policyData)){
return json(['error'=>1,'message'=>$aria2->reqMsg]);
}
}
public function FlushUser(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$toBeFlushed = Db::name("download")
->where("owner",$this->userObj->uid)
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
//取消的
->select();
foreach ($toBeFlushed as $key => $value) {
$aria2->flushStatus($value["id"],$this->userObj->uid,$this->userObj->getPolicy());
}
}
public function Cancel(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$downloadItem = Db::name("download")->where("owner",$this->userObj->uid)->where("id",input("post.id"))->find();
if(empty($downloadItem)){
return json(['error'=>1,'message'=>"未找到下载记录"]);
}
if($aria2->Remove($downloadItem["pid"],"")){
return json(['error'=>0,'message'=>"下载已取消"]);
}else{
return json(['error'=>1,'message'=>"取消失败"]);
}
}
public function ListDownloading(){
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","in",["active","ready","waiting"])->order('id desc')->select();
foreach ($downloadItems as $key => $value) {
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$downloadItems[$key]["fileName"] = basename($connectInfo["dir"]);
$downloadItems[$key]["completedLength"] = $connectInfo["completedLength"];
$downloadItems[$key]["totalLength"] = $connectInfo["totalLength"];
$downloadItems[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$downloadItems[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$downloadItems[$key]["fileName"] = $value["source"];
}
$downloadItems[$key]["completedLength"] = 0;
$downloadItems[$key]["totalLength"] = 0;
$downloadItems[$key]["downloadSpeed"] = 0;
}
}
return json($downloadItems);
}
public function ListFinished(){
$page = input("get.page");
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","not in",["active","ready","waiting"])->order('id desc')->page($page.',10')->select();
foreach ($downloadItems as $key => $value) {
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$downloadItems[$key]["fileName"] = basename($connectInfo["dir"]);
$downloadItems[$key]["completedLength"] = $connectInfo["completedLength"];
$downloadItems[$key]["totalLength"] = $connectInfo["totalLength"];
$downloadItems[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$downloadItems[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$downloadItems[$key]["fileName"] = $value["source"];
}
$downloadItems[$key]["completedLength"] = 0;
$downloadItems[$key]["totalLength"] = 0;
$downloadItems[$key]["downloadSpeed"] = 0;
}
}
return json($downloadItems);
}
}
+16 -1
View File
@@ -132,6 +132,21 @@ class Share extends Controller{
return $shareObj->listPic($shareId,$filePath);
}
public function Thumb(){
$shareId = input('get.shareKey');
$filePath = input('get.path');
if(input("get.isImg") != "true"){
return "";
}
$shareObj = new ShareHandler($shareId,false);
$Redirect = $shareObj->getThumb($this->userObj,$filePath);
if($Redirect[0]){
$this->redirect($Redirect[1],302);
}else{
$this->error($Redirect[1],403,$this->siteOptions);
}
}
public function Delete(){
$shareId = input('post.id');
$shareObj = new ShareHandler($shareId,false);
@@ -163,7 +178,7 @@ class Share extends Controller{
}
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$list = Db::name('shares')->where('owner',$this->userObj->uid)->paginate(10);
$list = Db::name('shares')->where('owner',$this->userObj->uid)->order('share_time DESC')->paginate(30);
$listData = $list->all();
foreach ($listData as $key => $value) {
if($value["source_type"]=="file"){
+2 -2
View File
@@ -7,7 +7,7 @@ use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use \app\index\model\Directory;
use \app\index\model\Object;
use \app\index\model\Objects;
use \app\index\model\DavAuth;
use \app\index\model\BasicCallBack;
@@ -28,7 +28,7 @@ class WebDav extends Controller{
$publicDir = new Directory($this->uid."/");
$server = new DAV\Server($publicDir);
$server->setBaseUri('/WebDav/Api/uid/'.$this->uid);
$server->setBaseUri('/WebDav/Api/uid/'.$this->uid."/");
$lockBackend = new DAV\Locks\Backend\File(ROOT_PATH.'public/locks');
$lockPlugin = new DAV\Locks\Plugin($lockBackend);
$server->addPlugin($lockPlugin);
+129 -6
View File
@@ -5,6 +5,7 @@ use think\Model;
use think\Db;
use \app\index\model\Mail;
use \app\index\model\FileManage;
use \Krizalys\Onedrive\Client;
class AdminHandler extends Model{
@@ -14,11 +15,30 @@ class AdminHandler extends Model{
public $pageNow;
public $pageTotal;
public $dataTotal;
public $dbVerInfo;
public function __construct($options){
$this->siteOptions = $options;
}
public function checkDbVersion(){
$versionInfo = json_decode(@file_get_contents(ROOT_PATH. "application/version.json"),true);
$dbVerNow = Option::getValue("database_version");
if(!isset($versionInfo["db_version"]) || $dbVerNow < (int)$versionInfo["db_version"]){
$this->dbVerInfo = array('now' => $dbVerNow, 'require' => $versionInfo["db_version"]);
return true;
}
return false;
}
public function checkCron(){
$cronRecord = Db::name("corn")->where("name","delete_unseful_chunks")->find();
if($cronRecord["last_excute"]+$cronRecord["interval_s"]+"1800"<time()){
return false;
}
return true;
}
public function getStatics(){
$statics["fileNum"] = Db::name('files')->count();
$statics["privateShareNum"] = Db::name('shares')->where("type","private")->count();
@@ -93,6 +113,14 @@ class AdminHandler extends Model{
return $this->saveOptions($options);
}
public function saveAria2Setting($options){
return $this->saveOptions($options);
}
public function saveTaskOption($options){
return $this->saveOptions($options);
}
public function saveMailTemplate($options){
return $this->saveOptions($options);
}
@@ -102,6 +130,7 @@ class AdminHandler extends Model{
unset($options["sizeTimes"]);
$options["grade_policy"] = 0;
$options["policy_list"] = $options["policy_name"];
$options["aria2"] = $options["aria2"] ? "1,1,1" : "0,0,0";
try {
Db::name("groups")->insert($options);
} catch (Exception $e) {
@@ -130,7 +159,7 @@ class AdminHandler extends Model{
} catch (Exception $e) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
return ["error"=>200,"msg"=>"设置已保存","id"=>Db::name('policy')->getLastInsID()];
}
public function editPolicy($options){
@@ -156,6 +185,7 @@ class AdminHandler extends Model{
unset($options["id"]);
$options["max_storage"] = $options["max_storage"]*$options["sizeTimes"];
unset($options["sizeTimes"]);
$options["aria2"] = $options["aria2"] ? "1,1,1" : "0,0,0";
try {
Db::name("groups")->where("id",$groupId)->update($options);
} catch (Exception $e) {
@@ -336,8 +366,55 @@ class AdminHandler extends Model{
return $userData;
}
public function listDownloads(){
$pageSize = 10;
$this->pageData = Db::name("download")
->order("id desc")
->paginate($pageSize);
$this->dataTotal = Db::name("download")
->order("id desc")
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$userCache=[];
$userCacheList=[];
foreach ($this->listData as $key => $value) {
if(in_array($value["owner"], $userCacheList)){
$this->listData[$key]["user"] = $userCache[$value["owner"]];
}else{
$this->listData[$key]["user"] = Db::name("users")->where("id",$value["owner"])->find();
array_push($userCacheList,$value["owner"]);
$userCache[$value["owner"]] = $this->listData[$key]["user"];
}
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$this->listData[$key]["fileName"] = basename($connectInfo["dir"]);
$this->listData[$key]["completedLength"] = $connectInfo["completedLength"];
$this->listData[$key]["totalLength"] = $connectInfo["totalLength"];
$this->listData[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$this->listData[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$this->listData[$key]["fileName"] = $value["source"];
}
$this->listData[$key]["completedLength"] = 0;
$this->listData[$key]["totalLength"] = 0;
$this->listData[$key]["downloadSpeed"] = 0;
}
}
$this->pageNow = input("?get.page")?input("get.page"):1;
}
public function getTasks(){
$taskData = Db::name("task")
->order("id DESC")
->paginate(10);
return $taskData;
}
public function listFile(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodFile')) ? "id DESC" : cookie('orderMethodFile');
$this->pageData = Db::name("files")
->where(function ($query) {
@@ -392,7 +469,7 @@ class AdminHandler extends Model{
}
public function listUser(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodUser')) ? "id DESC" : cookie('orderMethodUser');
$this->pageData = Db::name("users")
->where(function ($query) {
@@ -461,8 +538,8 @@ class AdminHandler extends Model{
}
public function listShare(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodShare')) ? "id DESC" : cookie('orderMethodShare');
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodShare')) ? "share_time DESC" : cookie('orderMethodShare');
$this->pageData = Db::name("shares")
->where(function ($query) {
if(!empty(cookie('shareSearch'))){
@@ -506,7 +583,7 @@ class AdminHandler extends Model{
}
public function listPolicy(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$pageSize =!cookie('?pageSize') ? 10 : cookie('pageSize');
$this->pageData = Db::name("policy")
->where(function ($query) {
if(!empty(cookie('policySearch'))){
@@ -652,6 +729,52 @@ class AdminHandler extends Model{
}
return ["error"=>0,"msg"=>"设置已保存"];
}
public function updateOnedriveToken($policyId){
$policyData = Db::name("policy")->where("id",$policyId)->find();
if(empty($policyData)){
throw new \think\Exception("Policy not found");
}
$onedrive = new Client([
'client_id' => $policyData["bucketname"],
]);
$url = $onedrive->getLogInUrl([
'offline_access',
'files.readwrite.all',
], Option::getValue("siteURL")."Admin/oneDriveCalllback");
echo "<script>location.href='".$url."'</script>正在跳转至Onedrive账号授权页面,如果没有跳转,请<a href='$url'>点击这里</a>。";
Db::name("policy")->where("id",$policyId)->update([
"sk" => json_encode($onedrive->getState()),
]);
\think\Session::set('onedrive.pid',$policyId);
}
public function oneDriveCalllback($code){
if(input("?get.error")){
throw new \think\Exception(input("get.error_description"));
}
$policyId = \think\Session::get('onedrive.pid');
$policyData = Db::name("policy")->where("id",$policyId)->find();
$onedrive = new Client([
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
// Obtain the token using the code received by the OneDrive API.
$onedrive->obtainAccessToken($policyData["ak"], $_GET['code']);
// Persist the OneDrive client' state for next API requests.
Db::name("policy")->where("id",$policyId)->update([
"sk" => json_encode($onedrive->getState()),
]);
echo "<script>location.href='/Admin/PolicyList?page=1'</script>";
}
}
?>
+459
View File
@@ -0,0 +1,459 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
class Aria2 extends Model{
private $authToken;
private $apiUrl;
private $savePath;
private $saveOptions;
public $reqStatus;
public $reqMsg;
public $pathId;
public $pid;
private $uid;
private $policy;
public function __construct($options){
$this->authToken = $options["aria2_token"];
$this->apiUrl = rtrim($options["aria2_rpcurl"],"/")."/";
$this->saveOptions = json_decode($options["aria2_options"],true);
$this->savePath = rtrim(rtrim($options["aria2_tmppath"],"/"),"\\").DS;
}
/**
* 新建普通URL下载任务
*
* @param string $url
* @return void
*/
public function addUrl($url){
$this->pathId = uniqid();
$reqFileds = [
"params" => ["token:".$this->authToken,
[$url],["dir" => $this->savePath.$this->pathId],
],
"jsonrpc" => "2.0",
"id" => $this->pathId,
"method" => "aria2.addUri"
];
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
$this->reqStatus = 1;
$this->pid = $respondData["result"];
}else{
$this->reqStatus = 0;
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
}
}
/**
* 新建种子下载任务
*
* @param string $torrentUrl 种子URL
* @return void
*/
public function addTorrent($torrentUrl){
$this->pathId = uniqid();
$reqFileds = [
"params" => ["token:".$this->authToken,
[$torrentUrl],["dir" => $this->savePath.$this->pathId],
],
"jsonrpc" => "2.0",
"id" => $this->pathId,
"method" => "aria2.addUri"
];
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
$this->reqStatus = 1;
$this->pid = $respondData["result"];
}else{
$this->reqStatus = 0;
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
}
}
/**
* 刷新下载状态
*
* @param int $id 任务ID
* @param int $uid 用户ID
* @param array $policy 上传策略
* @return void
*/
public function flushStatus($id,$uid,$policy){
$this->uid = $uid;
if(empty($policy)){
$user = Db::name("users")->where("id",$uid)->find();
$group = Db::name("groups")->where("id",$user["user_group"])->find();
$policy = Db::name("policy")->where("id",$group["policy_name"])->find();
}
$this->policy = $policy;
$downloadInfo = Db::name("download")->where("id",$id)->find();
if(empty($downloadInfo)){
$this->reqStatus = 0;
$this->reqMsg = "未找到下载记录";
return false;
}
if(in_array($downloadInfo["status"], ["error","complete"])){
$this->reqStatus = 1;
return true;
}
if($uid != $downloadInfo["owner"]){
$this->reqStatus = 0;
$this->reqMsg = "无权操作";
return false;
}
$reqFileds = [
"params" => ["token:".$this->authToken,$downloadInfo["pid"]],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.tellStatus"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
if($this->storageCheck($respondData["result"],$downloadInfo)){
if($downloadInfo["is_single"] && count($respondData["result"]["files"]) >1){
$this->updateToMuiltpe($respondData["result"],$downloadInfo);
return false;
}
if(isset($respondData["result"]["followedBy"])){
Db::name("download")->where("id",$id)
->update([
"pid" => $respondData["result"]["followedBy"][0],
]);
return false;
}
Db::name("download")->where("id",$id)
->update([
"status" => $respondData["result"]["status"],
"last_update" => date("Y-m-d h:i:s"),
"info" => json_encode([
"completedLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"],
"totalLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
"dir" => $respondData["result"]["files"][$downloadInfo["file_index"]]["path"],
"downloadSpeed" => $respondData["result"]["downloadSpeed"],
"errorMessage" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
]),
"msg" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
"total_size" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
]);
switch ($respondData["result"]["status"]) {
case 'complete':
$this->setComplete($respondData["result"],$downloadInfo);
break;
case 'removed':
$this->setCanceled($respondData["result"],$downloadInfo);
break;
default:
# code...
break;
}
if(($respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"] == $respondData["result"]["files"][$downloadInfo["file_index"]]["length"] && ($respondData["result"]["files"][$downloadInfo["file_index"]]["length"] !=0 )) && $respondData["result"]["status"]=="active"){
$this->setComplete($respondData["result"],$downloadInfo,$downloadInfo["file_index"]);
Db::name("download")->where("id",$id)
->update([
"status" => "complete",
]);
}
}else{
$this->reqStatus = 0;
$this->reqMsg = "空间容量不足";
$this->setError($respondData["result"],$downloadInfo,"空间容量不足");
return false;
}
}else{
$this->reqStatus = 0;
$this->reqMsg = $respondData["error"]["message"];
$this->setError($respondData,$downloadInfo,$respondData["error"]["message"],"error",true);
return false;
}
return true;
}
/**
* 取消任务
*
* @param array $quenInfo 任务信息(aria2
* @param array $sqlData 任务信息(数据库)
* @return void
*/
private function setCanceled($quenInfo,$sqlData){
@self::remove_directory($this->savePath.$sqlData["path_id"]);
if(!is_dir($this->savePath.$sqlData["path_id"])){
Db::name("download")->where("id",$sqlData["id"])->update([
"status" => "canceled",
]);
}
}
/**
* 移除整个目录
*
* @param string $dir
* @return void
*/
static function remove_directory($dir){
if($handle=opendir("$dir")){
while(false!==($item=readdir($handle))){
if($item!="."&&$item!=".."){
if(is_dir("$dir/$item")){
self::remove_directory("$dir/$item");
}else{
unlink("$dir/$item");
}
}
}
closedir($handle);
rmdir($dir);
}
}
/**
* 将单文件任务升级至多文件任务
*
* @param array $quenInfo
* @param array $sqlData
* @return void
*/
private function updateToMuiltpe($quenInfo,$sqlData){
foreach ($quenInfo["files"] as $key => $value) {
Db::name("download")->insert([
"pid" => $sqlData["pid"],
"path_id" => $sqlData["path_id"],
"owner" => $sqlData["owner"],
"save_dir" => $sqlData["save_dir"],
"status" => "ready",
"msg" => "",
"info"=>"",
"source" =>$sqlData["source"],
"file_index" => $key,
"is_single" => 0,
"total_size" => 0,
]);
}
Db::name("download")->where("id",$sqlData["id"])->delete();
}
/**
* 下载完成后续处理
*
* @param array $quenInfo
* @param array $sqlData
* @param int $fileIndex
* @return void
*/
private function setComplete($quenInfo,$sqlData,$fileIndex=null){
if($this->policy["policy_type"] != "local" && $this->policy["policy_type"] != "onedrive"){
$this->setError($quenInfo,$sqlData,"您当前的上传策略无法使用离线下载");
return false;
}
if($fileIndex==null){
$this->forceRemove($sqlData["pid"]);
}
$suffixTmp = explode('.', $quenInfo["dir"]);
$fileSuffix = array_pop($suffixTmp);
$uploadHandller = new UploadHandler($this->policy["id"],$this->uid);
$allowedSuffix = explode(',', $uploadHandller->getAllowedExt(json_decode($this->policy["filetype"],true)));
$sufficCheck = !in_array($fileSuffix,$allowedSuffix);
if(empty($uploadHandller->getAllowedExt(json_decode($this->policy["filetype"],true)))){
$sufficCheck = false;
}
if($sufficCheck){
//取消任务
$this->setError($quenInfo,$sqlData,"文件类型不被允许");
return false;
}
if($this->policy['autoname']){
$fileName = $uploadHandller->getObjName($this->policy['namerule'],"local",basename($quenInfo["files"][$sqlData["file_index"]]["path"]));
}else{
$fileName = basename($quenInfo["files"][$sqlData["file_index"]]["path"]);
}
$generatePath = $uploadHandller->getDirName($this->policy['dirrule']);
if($this->policy["policy_type"] == "onedrive"){
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
$task = new Task();
$task->taskName = "Upload RemoteDownload File " . $quenInfo["files"][$sqlData["file_index"]]["path"] . " to Onedrive";
$task->taskType = $quenInfo["files"][$sqlData["file_index"]]["length"]<=4*1024*1024 ? "UploadRegularRemoteDownloadFileToOnedrive" :"UploadLargeRemoteDownloadFileToOnedrive";
@list($width, $height, $type, $attr) = getimagesize($quenInfo["files"][$sqlData["file_index"]]["path"]);
$picInfo = empty($width)?"":$width.",".$height;
$task->taskContent = json_encode([
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
"originPath" => $quenInfo["files"][$sqlData["file_index"]]["path"],
"objname" => $fileName,
"savePath" => $generatePath,
"fsize" => (int)$quenInfo["files"][$sqlData["file_index"]]["length"],
"picInfo" => $picInfo,
"policyId" => $this->policy["id"],
]);
$task->userId = $this->uid;
$task->saveTask();
}else{
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
is_dir(dirname($savePath))? :mkdir(dirname($savePath),0777,true);
rename($quenInfo["files"][$sqlData["file_index"]]["path"],$savePath);
@unlink(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
$jsonData = array(
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
"objname" => $generatePath.DS.$fileName,
"fsize" => $quenInfo["files"][$sqlData["file_index"]]["length"],
);
@list($width, $height, $type, $attr) = getimagesize($savePath);
$picInfo = empty($width)?" ":$width.",".$height;
$addAction = FileManage::addFile($jsonData,$this->policy,$this->uid,$picInfo);
if(!$addAction[0]){
//取消任务
$this->setError($quenInfo,$sqlData,$addAction[1]);
return false;
}
}
FileManage::storageCheckOut($this->uid,$quenInfo["files"][$sqlData["file_index"]]["length"]);
}
/**
* 设置任务为失败状态
*
* @param array $quenInfo
* @param array $sqlData
* @param string $msg 失败消息
* @param string $status 状态
* @param boolean $delete 是否删除下载文件
* @return void
*/
private function setError($quenInfo,$sqlData,$msg,$status="error",$delete=true){
$this->Remove($sqlData["pid"],$sqlData);
$this->removeDownloadResult($sqlData["pid"],$sqlData);
if($delete){
if(isset($quenInfo["files"][$sqlData["file_index"]]["path"]) && file_exists($quenInfo["files"][$sqlData["file_index"]]["path"])){
@unlink($quenInfo["files"][$sqlData["file_index"]]["path"]);
@self::remove_directory(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
}
}
Db::name("download")->where("id",$sqlData["id"])->update([
"msg" => $msg,
"status" => $status,
]);
}
/**
* 移除任务
*
* @param int $gid
* @param array $sqlData
* @return void
*/
public function Remove($gid,$sqlData){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.remove"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
/**
* 删除下载结果
*
* @param int $gid
* @param array $sqlData
* @return void
*/
public function removeDownloadResult($gid,$sqlData){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.removeDownloadResult"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
/**
* 强制移除任务
*
* @param int $gid
* @return void
*/
public function forceRemove($gid){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.forceRemove"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
/**
* 检查容量
*
* @param array $quenInfo
* @param array $sqlData
* @return void
*/
private function storageCheck($quenInfo,$sqlData){
if(!FileManage::sotrageCheck($this->uid,$quenInfo["totalLength"])){
return false;
}
if(!FileManage::sotrageCheck($this->uid,$quenInfo["completedLength"])){
return false;
}
return true;
}
/**
* 发送请求
*
* @param string $data
* @return array
*/
private function sendReq($data){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $this->apiUrl."jsonrpc");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$tmpInfo = curl_exec($curl);
if (curl_errno($curl)) {
$this->reqStatus = 0;
$this->reqMsg = "请求失败,".curl_error($curl);
}
curl_close($curl);
return json_decode($tmpInfo,true);
}
}
?>
+3 -3
View File
@@ -4,7 +4,7 @@ namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \app\index\model\FileManage;
use \app\index\model\LocalAdapter;
class Avatar extends Model{
@@ -90,7 +90,7 @@ class Avatar extends Model{
$filePath = ROOT_PATH . 'public/avatars/' . $this->fileName.$siezSuffix;
if(file_exists($filePath)){
ob_end_clean();
header('Content-Type: '.FileManage::getMimetype($filePath));
header('Content-Type: '.LocalAdapter::getMimetype($filePath));
$fileObj = fopen($filePath,"r");
while(!feof($fileObj)){
echo fread($fileObj,2097152);
@@ -131,7 +131,7 @@ class Avatar extends Model{
}
ob_end_clean();
$filePath = ROOT_PATH . 'static/img/default.png' .$siezSuffix;
header('Content-Type: '.FileManage::getMimetype($filePath));
header('Content-Type: '.LocalAdapter::getMimetype($filePath));
$fileObj = fopen($filePath,"r");
while(!feof($fileObj)){
echo fread($fileObj,2097152);
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
class B2 extends Model{
public $policy;
private $ak;
public function __construct($policy){
$this->policy = $policy;
$akInfo = explode(":",$this->policy["ak"]);
$this->ak = $akInfo[0];
if(time()-$akInfo[1]>=86000){
$this->updateAuth();
}
}
private function updateAuth(){
$credentials = base64_encode($this->ak . ":" . $this->policy["sk"]);
$url = "https://api.backblazeb2.com/b2api/v1/b2_authorize_account";
$session = curl_init($url);
$headers = array();
$headers[] = "Accept: application/json";
$headers[] = "Authorization: Basic " . $credentials;
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_HTTPGET, true);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
$server_output = curl_exec($session);
curl_close ($session);
$authInfo = json_decode($server_output,true);
Db::name("policy")->where("id",$this->policy["id"])
->update([
"ak" => $this->ak . ":" . time(),
"op_name" => $authInfo["apiUrl"],
"url" => $authInfo["downloadUrl"],
"op_pwd" => $authInfo["authorizationToken"],
]);
$this->policy = Db::name("policy")->where("id",$this->policy["id"])->find();
$this->updateUploadAuth();
}
private function updateUploadAuth(){
$session = curl_init($this->policy["op_name"] . "/b2api/v1/b2_get_upload_url");
$data = array("bucketId" => $this->policy["bucketname"]);
$post_fields = json_encode($data);
curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields);
$headers = array();
$headers[] = "Authorization: " . $this->policy["op_pwd"];
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($session);
curl_close ($session);
$authInfo = json_decode($server_output,true);
Db::name("policy")->where("id",$this->policy["id"])
->update([
"server" => $authInfo["uploadUrl"] . "|" . $authInfo["authorizationToken"],
]);
$this->policy = Db::name("policy")->where("id",$this->policy["id"])->find();
die("");
}
}
?>
@@ -20,6 +20,28 @@ class CallbackHandler extends Model{
$this->CallbackData = $data;
}
public function remoteHandler($header){
$jsonData = json_decode(base64_decode($this->CallbackData),true);
$CallbackSqlData = Db::name('callback')->where('callback_key',$jsonData['callbackkey'])->find();
$this->policyData = Db::name('policy')->where('id',$CallbackSqlData['pid'])->find();
if(!$this->IsRemoteCallback($header)){
$this->setError("Undelegated Request");
}
if($this->policyData == null){
$this->setError("CallbackKey Not Exist.");
}
if(!FileManage::sotrageCheck($CallbackSqlData["uid"],$jsonData["fsize"])){
$this->setError("空间容量不足",true);
}
$picInfo = $jsonData["picinfo"];
$addAction = FileManage::addFile($jsonData,$this->policyData,$CallbackSqlData["uid"],$picInfo);
if(!$addAction[0]){
$this->setError($addAction[1],true);
}
FileManage::storageCheckOut($CallbackSqlData["uid"],$jsonData["fsize"]);
$this->setSuccess($jsonData['fname']);
}
public function qiniuHandler($header){
$jsonData = json_decode($this->CallbackData,true);
$CallbackSqlData = Db::name('callback')->where('callback_key',$jsonData['callbackkey'])->find();
@@ -172,6 +194,11 @@ class CallbackHandler extends Model{
}
}
private function IsRemoteCallback($header){
$signKey = hash_hmac("sha256",$this->CallbackData,$this->policyData["sk"]);
return ($signKey == $header);
}
public function IsOssCallback($auth,$pubKey){
if (empty($auth) || empty($pubKey)){
header("http/1.1 403 Forbidden");
+53
View File
@@ -7,6 +7,8 @@ use \think\Session;
use \app\index\model\FileManage;
use \app\index\model\Option;
use \app\index\model\Mail;
use \app\index\model\Aria2;
use think\Exception;
class CronHandler extends Model{
@@ -40,6 +42,16 @@ class CronHandler extends Model{
$this->deleteCallbackData($value["interval_s"]);
}
break;
case 'flush_aria2':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->flushAria2($value["interval_s"]);
}
break;
case 'flush_onedrive_token':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->flushOnedriveToken($value["interval_s"]);
}
break;
default:
# code...
break;
@@ -69,5 +81,46 @@ class CronHandler extends Model{
$this->setComplete("delete_callback_data");
}
public function flushAria2($interval){
echo("flushingAria2Status...");
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$toBeFlushed = Db::name("download")
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
->select();
foreach ($toBeFlushed as $key => $value) {
$aria2->flushStatus($value["id"],$value["owner"],null);
}
echo("Complete<br>");
$this->setComplete("flush_aria2");
}
public function flushOnedriveToken($interval){
echo("flushOnedriveToken...");
$toBeFlushedPolicy = Db::name("policy")->where("policy_type","onedrive")->select();
foreach ($toBeFlushedPolicy as $key => $value) {
$onedrive = new \Krizalys\Onedrive\Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $value["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($value["sk"]),
]);
try{
$onedrive->renewAccessToken($value["ak"]);
}catch(\Exception $e){
}
Db::name("policy")->where("id",$value["id"])->update([
"sk" => json_encode($onedrive->getState()),
]);
}
echo("Complete<br>");
$this->setComplete("flush_onedrive_token");
}
}
?>
+11 -5
View File
@@ -30,6 +30,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
function createFile($name, $data = NULL){
$name = str_replace(" ","",$name);
$userData = Db::name("users")->where("id",$this->uid)->find();
$groupData = Db::name("groups")->where("id",$userData["user_group"])->find();
$policyData = Db::name("policy")->where("id",$groupData["policy_name"])->find();
@@ -47,7 +48,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
$fileSize = fstat($data)["size"];
if(empty($fileSize)){
$fileSize = 0;
$fileSize = -1;
}
if($fileSize>$policyData["max_size"]){
throw new DAV\Exception\InsufficientStorage('File is to large');
@@ -66,6 +67,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
mkdir($savePath,0777,true);
}
file_put_contents($savePath."/".$fileName, $data);
if($fileSize<=0){
$fileSize = filesize($savePath."/".$fileName);
}
$jsonData = array(
"path" => str_replace("/",",",ltrim($this->myPath,"/")),
"fname" => $name,
@@ -120,7 +124,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
function getChildFile($name){
$path = $this->uid.rtrim($this->myPath,"/") . '/' . $name["orign_name"];
return new Object($path);
return new Objects($path);
}
function getChildDir($name){
@@ -134,6 +138,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
function getChild($name) {
$name = str_replace(" ","",$name);
if(!$this->childExists($name)){
throw new DAV\Exception\NotFound('File with name ' . $name . ' could not be located');
}
@@ -142,12 +147,13 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
$returnObj = new Directory($path);
return $returnObj;
}else{
return new Object($path);
return new Objects($path);
}
}
function childExists($name) {
$fileObj = new Object($this->uid.rtrim($this->myPath,"/") . '/' . $name);
$name = str_replace(" ","",$name);
$fileObj = new Objects($this->uid.rtrim($this->myPath,"/") . '/' . $name);
if($this->findDir(rtrim($this->myPath,"/") . '/' . $name) || $fileObj->isExist){
return true;
}
@@ -162,7 +168,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
$dirName = end($explode);
$rootPath = rtrim($path,"/".$dirName);
$rootPath = empty($rootPath) ? "/" : $rootPath;
$dirData = Db::name('folders')->where('owner',$this->uid)->where('position',dirname($path) == "\\" ?"/":dirname($path))->where("folder_name",basename($path))->find();
$dirData = Db::name('folders')->where('owner',$this->uid)->where('position',dirname($path) == "\\" ?"/":dirname($path))->where("folder_name",getDirName($path))->find();
if(empty($dirData)){
return false;
}
File diff suppressed because it is too large Load Diff
+392
View File
@@ -0,0 +1,392 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
/**
* 本地策略文件管理适配器
*/
class LocalAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取文本文件内容
*
* @return string 内容
*/
public function getFileContent(){
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
$fileObj = fopen($filePath,"r");
$fileContent = fread($fileObj,filesize($filePath)+1);
return $fileContent;
}
/**
* 保存可编辑文件
*
* @param string $content 要保存的文件内容
* @return void
*/
public function saveContent($content){
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
file_put_contents($filePath, "");
file_put_contents($filePath, $content);
}
/**
* 输出预览文件
*
* @param boolean $isAdmin 是否为管理员请求
* @return mixed 文件数据
*/
public function Preview($isAdmin = false){
$speedLimit = Db::name('groups')->where('id',$this->userModel["user_group"])->find();
$rangeTransfer = $speedLimit["range_transfer"];
$speedLimit = $speedLimit["speed"];
$sendFileOptions = Option::getValues(["download"]);
if($sendFileOptions["sendfile"] == "1" && !empty($sendFileOptions)){
$this->sendFile($speedLimit,$rangeTransfer,false,$sendFileOptions["header"]);
}else{
if($isAdmin){
$speedLimit="";
}
if($speedLimit == "0"){
exit();
}else if(empty($speedLimit)){
header("Cache-Control: max-age=10800");
$this->outputWithoutLimit(false,$rangeTransfer);
exit();
}else if((int)$speedLimit > 0){
header("Cache-Control: max-age=10800");
$this->outputWithLimit($speedLimit);
}
}
}
/**
* 使用Sendfile模式发送文件数据
*
* @param int $speed 下载限速
* @param boolean $range 是否支持断点续传
* @param boolean $download 是否为下载请求
* @param string $header Sendfile Header
* @return void
*/
private function sendFile($speed,$range,$download=false,$header="X-Sendfile"){
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
$realPath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
if($header == "X-Accel-Redirect"){
$filePath = '/public/uploads/' . $this->fileModel["pre_name"];
}
if($download){
$filePath = str_replace("\\","/",$filePath);
if($header == "X-Accel-Redirect"){
ob_flush();
flush();
echo "s";
}
//保证如下顺序,否则最终浏览器中得到的content-type为'text/html'
//1,写入 X-Sendfile 头信息
$pathToFile = str_replace('%2F', '/', rawurlencode($filePath));
header($header.": ".$pathToFile);
//2,写入Content-Type头信息
$mime_type = self::getMimetypeOnly($realPath);
header('Content-Type: '.$mime_type);
//3,写入正确的附件文件名头信息
$orign_fname = $this->fileModel["orign_name"];
$ua = $_SERVER["HTTP_USER_AGENT"]; // 处理不同浏览器的兼容性
if (preg_match("/Firefox/", $ua)) {
$encoded_filename = rawurlencode($orign_fname);
header("Content-Disposition: attachment; filename*=\"utf8''" . $encoded_filename . '"');
} else if (preg_match("/MSIE/", $ua) || preg_match("/Edge/", $ua) || preg_match("/rv:/", $ua)) {
$encoded_filename = rawurlencode($orign_fname);
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else {
// for Chrome,Safari etc.
header('Content-Disposition: attachment;filename="'. $orign_fname .'";filename*=utf-8'."''". $orign_fname);
}
exit;
}else{
$filePath = str_replace("\\","/",$filePath);
header('Content-Type: '.self::getMimetype($realPath));
if($header == "X-Accel-Redirect"){
ob_flush();
flush();
echo "s";
}
header($header.": ".str_replace('%2F', '/', rawurlencode($filePath)));
ob_flush();
flush();
}
}
/**
* 无限速发送文件数据
*
* @param boolean $download 是否为下载
* @param boolean $reload 是否支持断点续传
* @return void
*/
public function outputWithoutLimit($download = false,$reload = false){
ignore_user_abort(false);
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
set_time_limit(0);
session_write_close();
$file_size = filesize($filePath);
$ranges = $this->getRange($file_size);
if($reload == 1 && $ranges!=null){
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges:bytes');
header(sprintf('content-length:%u',$ranges['end']-$ranges['start']));
header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end']-1, $file_size));
}
if($download){
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($filePath));
$encoded_fname = rawurlencode($this->fileModel["orign_name"]);
header('Content-Disposition: attachment;filename="'.$encoded_fname.'";filename*=utf-8'."''".$encoded_fname);
ob_flush();
flush();
}
if(file_exists($filePath)){
if(!$download){
header('Content-Type: '.self::getMimetype($filePath));
ob_flush();
flush();
}
$fileObj = fopen($filePath,"rb");
if($reload == 1){
fseek($fileObj, sprintf('%u', $ranges['start']));
}
while(!feof($fileObj)){
echo fread($fileObj,10240);
ob_flush();
flush();
}
fclose($fileObj);
}
}
/**
* 有限速发送文件数据
*
* @param int $speed 最大速度
* @param boolean $download 是否为下载请求
* @return void
*/
public function outputWithLimit($speed,$download = false){
ignore_user_abort(false);
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
set_time_limit(0);
session_write_close();
if($download){
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($filePath));
$encoded_fname = rawurlencode($this->fileModel["orign_name"]);
header('Content-Disposition: attachment;filename="'.$encoded_fname.'";filename*=utf-8'."''".$encoded_fname);
ob_flush();
flush();
}else{
header('Content-Type: '.self::getMimetype($filePath));
ob_flush();
flush();
}
if(file_exists($filePath)){
$fileObj = fopen($filePath,"r");
while (!feof($fileObj)){
echo fread($fileObj,round($speed*1024));
ob_flush();
flush();
sleep(1);
}
fclose($fileObj);
}
}
/**
* 获取文件MIME Type
*
* @param string $path 文件路径
* @return void
*/
static function getMimetype($path){
//FILEINFO_MIME will output something like "image/jpeg; charset=binary"
$finfoObj = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfoObj, $path);
finfo_close($finfoObj);
return $mimetype;
}
/**
* 获取文件MIME Type
*
* @param string $path 文件路径
* @return void
*/
static function getMimetypeOnly($path){
//FILEINFO_MIME_TYPE will output something like "image/jpeg"
$finfoObj = finfo_open(FILEINFO_MIME_TYPE);
$mimetype = finfo_file($finfoObj, $path);
finfo_close($finfoObj);
return $mimetype;
}
/**
* 获取断点续传时HTTP_RANGE头
*
* @param int $file_size 文件大小
* @return void
*/
private function getRange($file_size){
if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){
$range = $_SERVER['HTTP_RANGE'];
$range = preg_replace('/[\s|,].*/', '', $range);
$range = explode('-', substr($range, 6));
if(count($range)<2){
$range[1] = $file_size;
}
$range = array_combine(array('start','end'), $range);
if(empty($range['start'])){
$range['start'] = 0;
}
if(empty($range['end'])){
$range['end'] = $file_size;
}
return $range;
}
return null;
}
/**
* 生成/返回 文件缩略图
*
* @return array 重定向信息
*/
public function getThumb(){
$picInfo = explode(",",$this->fileModel["pic_info"]);
$picInfo = self::getThumbSize($picInfo[0],$picInfo[1]);
if(file_exists(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb")){
self::outputThumb(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb");
return [0,0];
}
$thumbImg = new Thumb(ROOT_PATH . "public/uploads/".$this->fileModel["pre_name"]);
$thumbImg->thumb($picInfo[1], $picInfo[0]);
if(!is_dir(dirname(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]))){
mkdir(dirname(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]),0777,true);
}
$thumbImg->out(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb");
self::outputThumb(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb");
return [0,0];
}
/**
* 计算缩略图大小
*
* @param int $width 原始宽
* @param int $height 原始高
* @return array
*/
static function getThumbSize($width,$height){
$rate = $width/$height;
$maxWidth = 90;
$maxHeight = 39;
$changeWidth = 39*$rate;
$changeHeight = 90/$rate;
if($changeWidth>=$maxWidth){
return [(int)$changeHeight,90];
}
return [39,(int)$changeWidth];
}
/**
* 输出缩略图
*
* @param string $path 缩略图文件路径
* @return void
*/
static function outputThumb($path){
ob_end_clean();
if(!input("get.cache")=="no"){
header("Cache-Control: max-age=10800");
}
header('Content-Type: '.self::getMimetype($path));
$fileObj = fopen($path,"r");
echo fread($fileObj,filesize($path));
fclose($file);
}
/**
* 处理下载请求
*
* @param boolean $isAdmin 是否为管理员请求
* @return void
*/
public function Download($isAdmin=false){
$speedLimit = Db::name('groups')->where('id',$this->userModel["user_group"])->find();
$rangeTransfer = $speedLimit["range_transfer"];
$speedLimit = $speedLimit["speed"];
$sendFileOptions = Option::getValues(["download"]);
if($sendFileOptions["sendfile"] == "1"){
$this->sendFile($speedLimit,$rangeTransfer,true,$sendFileOptions["header"]);
}else{
if($isAdmin){
$speedLimit = "";
}
if($speedLimit == "0"){
exit();
}else if(empty($speedLimit)){
$this->outputWithoutLimit(true,$rangeTransfer);
exit();
}else if((int)$speedLimit > 0){
$this->outputWithLimit($speedLimit,true);
}
}
}
/**
* 删除指定本地文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
$fileListTemp = array_column($fileList, 'pre_name');
foreach ($fileListTemp as $key => $value) {
@unlink(ROOT_PATH . 'public/uploads/'.$value);
if(file_exists(ROOT_PATH . 'public/thumb/'.$value."_thumb")){
@unlink(ROOT_PATH . 'public/thumb/'.$value."_thumb");
}
}
}
/**
* 签名临时直链,用于Office365预览
*
* @return array
*/
public function signTmpUrl(){
$options = Option::getValues(["oss","basic"]);
$timeOut = $options["timeout"];
$delayTime = time()+$timeOut;
$key=$this->fileModel["id"].":".$delayTime.":".md5($this->userModel["user_pass"].$this->fileModel["id"].$delayTime.config("salt"));
return [1,$options['siteURL']."Callback/TmpPreview/key/".$key];
}
}
?>
+4 -1
View File
@@ -45,13 +45,16 @@ class Mail extends Model{
$mail->Username =$this->smtpUser;
$mail->Password = $this->smtpPass;
$mail->From = $this->fromAdress;
$mail->SMTPDebug = 1;
$mail->Debugoutput = function($str, $level) {
$this->errorMsg .= $str;
};
$mail->isHTML(true);
$mail->addAddress($to,$name);
$mail->Subject = $title;
$mail->Body = $content;
$status = $mail->send();
if(!$status){
$this->errorMsg = $mail->ErrorInfo;
return false;
}
return true;
@@ -6,7 +6,7 @@ use think\Db;
use Sabre\DAV;
use \app\index\model\FileManage;
class Object extends DAV\File{
class Objects extends DAV\File{
private $myPath;
public $id;
+140
View File
@@ -0,0 +1,140 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \Krizalys\Onedrive\Client;
use \app\index\model\Option;
/**
* Onedrive策略文件管理适配器
*/
class OnedriveAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
private $clinet;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
$this->clinet = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $this->policyModel["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($this->policyModel["sk"]),
]);
}
/**
* 获取文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
$file = new \Krizalys\Onedrive\File($this->clinet,"/me/drive/root:/".$this->fileModel["pre_name"].":");
return $file->fetchContent();
}
/**
* 签名预览URL
*
* @return void
*/
public function Preview($base=null,$name=null){
$preview = json_decode(json_encode($this->clinet->apiGet("/me/drive/root:/".rawurlencode($this->fileModel["pre_name"]).":")),true);
return [1,$preview["@microsoft.graph.downloadUrl"]];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$this->clinet->createFile(rawurldecode($this->fileModel["pre_name"]),"/me/drive/root:/",$content);
}
/**
* 计算缩略图大小
*
* @param int $width 原始宽
* @param int $height 原始高
* @return array
*/
static function getThumbSize($width,$height){
$rate = $width/$height;
$maxWidth = 90;
$maxHeight = 39;
$changeWidth = 39*$rate;
$changeHeight = 90/$rate;
if($changeWidth>=$maxWidth){
return [(int)$changeHeight,90];
}
return [39,(int)$changeWidth];
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
$picInfo = explode(",",$this->fileModel["pic_info"]);
$thumbSize = self::getThumbSize($picInfo[0],$picInfo[1]);
$thumb = json_decode(json_encode($this->clinet->apiGet("/me/drive/root:/".rawurlencode($this->fileModel["pre_name"]).":/thumbnails")),true);
return [1,$thumb["value"][0]["small"]["url"]];
}
/**
* 删除某一策略下的指定upyun文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
$clinet = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
foreach (array_column($fileList, 'pre_name') as $key => $value) {
$clinet->deleteObject("/me/drive/root:/".rawurlencode($value).":");
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
$preview = json_decode(json_encode($this->clinet->apiGet("/me/drive/root:/".rawurlencode($this->fileModel["pre_name"]).":")),true);
return [1,$preview["@microsoft.graph.downloadUrl"]];
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+199
View File
@@ -0,0 +1,199 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use OSS\OssClient;
use OSS\Core\OssException;
use \app\index\model\Option;
/**
* 阿里云OSS策略文件管理适配器
*/
class OssAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取OSS策略文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名OSS预览URL
*
* @return void
*/
public function Preview(){
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
return[true,$fileUrl];
}else{
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = $this->policyModel["url"];
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return [false,0];
}
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
try{
$signedUrl = $ossClient->signUrl($this->policyModel["bucketname"], $this->fileModel["pre_name"], Option::getValue("timeout"));
} catch(OssException $e) {
return [false,0];
}
return[true,$signedUrl];
}
}
/**
* 保存OSS文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = "http".ltrim(ltrim($this->policyModel["server"],"https"),"http");
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
die('{ "result": { "success": false, "error": "鉴权失败" } }');
}
try{
$ossClient->putObject($this->policyModel["bucketname"], $this->fileModel["pre_name"], $content);
} catch(OssException $e) {
die('{ "result": { "success": false, "error": "编辑失败" } }');
}
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?x-oss-process=image/resize,m_lfit,h_39,w_90";
return[true,$fileUrl];
}else{
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = $this->policyModel["url"];
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return [false,0];
}
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
try{
$signedUrl = $ossClient->signUrl($this->policyModel["bucketname"], $this->fileModel["pre_name"], Option::getValue("timeout"),'GET', array("x-oss-process" => 'image/resize,m_lfit,h_39,w_90'));
} catch(OssException $e) {
return [false,0];
}
return[true,$signedUrl];
}
}
/**
* 删除某一策略下的指定OSS文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
$accessKeyId = $policyData["ak"];
$accessKeySecret = $policyData["sk"];
$endpoint = "http".ltrim(ltrim($policyData["server"],"https"),"http");
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return false;
}
try{
$ossClient->deleteObjects($policyData["bucketname"], array_column($fileList, 'pre_name'));
} catch(OssException $e) {
return false;
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
if(!$this->policyModel['bucket_private']){
return[true,"/File/OssDownload?url=".urlencode($this->policyModel["url"].$this->fileModel["pre_name"])."&name=".urlencode($this->fileModel["orign_name"])];
}else{
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = $this->policyModel["url"];
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return [false,0];
}
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
try{
$signedUrl = $ossClient->signUrl($this->policyModel["bucketname"], $this->fileModel["pre_name"], Option::getValue("timeout"),'GET', array("response-content-disposition" => 'attachment; filename='.$this->fileModel["orign_name"]));
} catch(OssException $e) {
return [false,0];
}
return[true,$signedUrl];
}
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteOssFile($fname,$policy){
$accessKeyId = $policy["ak"];
$accessKeySecret = $policy["sk"];
$endpoint = "http".ltrim(ltrim($policy["server"],"https"),"http");
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return false;
}
try{
$ossClient->deleteObject($policy["bucketname"], $fname);
} catch(OssException $e) {
return false;
}
return true;
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+149
View File
@@ -0,0 +1,149 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
require_once 'extend/Qiniu/functions.php';
use Qiniu\Auth;
use \app\index\model\Option;
/**
* 七牛策略文件管理适配器
*/
class QiniuAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名七牛文件预览URL
*
* @param string $thumb 缩略图参数
* @return void
*/
public function Preview($thumb=null){
if($thumb===true || $thumb===false){
$thumb =null;
}
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"].$thumb;
return[true,$fileUrl];
}else{
$auth = new Auth($this->policyModel["ak"], $this->policyModel["sk"]);
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"].$thumb;
$signedUrl = $auth->privateDownloadUrl($baseUrl);
return[true,$signedUrl];
}
}
/**
* 保存七牛文件内容
*
* @param string $content 文件内容
* @return bool
*/
public function saveContent($content){
$auth = new Auth($this->policyModel["ak"], $this->policyModel["sk"]);
$expires = 3600;
$keyToOverwrite = $this->fileModel["pre_name"];
$upToken = $auth->uploadToken($this->policyModel["bucketname"], $keyToOverwrite, $expires, null, true);
$uploadMgr = new \Qiniu\Storage\UploadManager();
list($ret, $err) = $uploadMgr->put($upToken, $keyToOverwrite, $content);
if ($err !== null) {
die('{ "result": { "success": false, "error": "编辑失败" } }');
} else {
return true;
}
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
return $this->Preview("?imageView2/2/w/90/h/39");
}
/**
* 删除某一策略下的指定七牛文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function deleteFile($fileList,$policyData){
$auth = new Auth($policyData["ak"], $policyData["sk"]);
$config = new \Qiniu\Config();
$bucketManager = new \Qiniu\Storage\BucketManager($auth);
$fileListTemp = array_column($fileList, 'pre_name');
$ops = $bucketManager->buildBatchDelete($policyData["bucketname"], $fileListTemp);
list($ret, $err) = $bucketManager->batch($ops);
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?attname=".urlencode($this->fileModel["orign_name"]);
return[true,$fileUrl];
}else{
$auth = new Auth($this->policyModel["ak"], $this->policyModel["sk"]);
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?attname=".urlencode($this->fileModel["orign_name"]);
$signedUrl = $auth->privateDownloadUrl($baseUrl);
return[true,$signedUrl];
}
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return void
*/
static function deleteSingle($fname,$policy){
$auth = new Auth($policy["ak"], $policy["sk"]);
$config = new \Qiniu\Config();
$bucketManager = new \Qiniu\Storage\BucketManager($auth);
$err = $bucketManager->delete($policy["bucketname"], $fname);
if ($err) {
return false;
}else{
return true;
}
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
class Remote extends Model{
public $sk;
private $policy;
private $serverOutput;
private $httpCode;
public function __construct($policy){
$this->policy = $policy;
}
public function remove($fileList){
$signKey = $this->sign($fileList,"DELETE");
$this->send("manager.php",$signKey,"DELETE",base64_encode(json_encode($fileList)));
}
public function preview($fname){
return $this->signUrl($this->policy["url"]."object.php?action=preview&name=".urlencode($fname)."&expires=".(time()+(int)Option::getValue("timeout")));
}
public function clean(){
return $this->signUrl($this->policy["url"]."object.php?action=clean&expires=".(time()+(int)Option::getValue("timeout")));
}
public function download($fname,$attnanme){
return $this->signUrl($this->policy["url"]."object.php?action=download&name=".urlencode($fname)."&attaname=".urlencode($attnanme)."&expires=".(time()+(int)Option::getValue("timeout")));
}
public function thumb($fname,$picInfo){
return $this->signUrl($this->policy["url"]."object.php?action=thumb&name=".urlencode($fname)."&expires=".(time()+(int)Option::getValue("timeout"))."&w=".$picInfo[0]."&h=".$picInfo[1]);
}
public function signUrl($url){
$signKey = hash_hmac("sha256",$url,"GET".$this->policy["sk"]);
return $url."&auth=".$signKey;
}
public function updateContent($fname,$content){
$object = ["fname"=>$fname,"content"=>$content];
$signKey = $this->sign($object,"UPDATE");
$this->send("manager.php",$signKey,"UPDATE",base64_encode(json_encode($object)));
}
public function send($target,$auth,$action,$object){
$session = curl_init($this->policy["server"].$target);
$postData = array(
"action" => $action,
"auth" => $auth,
"object" => $object,
);
curl_setopt($session, CURLOPT_POST, 1);
curl_setopt($session, CURLOPT_POSTFIELDS, $postData);
curl_setopt($session, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false);
$this->serverOutput = curl_exec($session);
$this->httpCode = curl_getinfo($session,CURLINFO_HTTP_CODE);
echo $this->serverOutput;
}
public function sign($content,$method = null){
return hash_hmac("sha256",base64_encode(json_encode($content)),$method.$this->policy["sk"]);
}
}
?>
+97
View File
@@ -0,0 +1,97 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
/**
* 远程策略文件管理适配器
*/
class RemoteAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名文件预览URL
*
* @return void
*/
public function Preview(){
$remote = new Remote($this->policyModel);
return [1,$remote->preview($this->fileModel["pre_name"])];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return bool
*/
public function saveContent($content){
$remote = new Remote($this->policyModel);
$remote->updateContent($this->fileModel["pre_name"],$content);
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
$remote = new Remote($this->policyModel);
return [1,$remote->thumb($this->fileModel["pre_name"],explode(",",$this->fileModel["pic_info"]))];
}
/**
* 删除某一策略下的指定文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function deleteFile($fileList,$policyData){
$remoteObj = new Remote($policyData);
$remoteObj->remove(array_column($fileList, 'pre_name'));
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
$remote = new Remote($this->policyModel);
return [1,$remote->download($this->fileModel["pre_name"],$this->fileModel["orign_name"])];
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use Upyun\Upyun;
use Upyun\Config;
use \app\index\model\Option;
/**
* AWS S3策略文件管理适配器
*/
class S3Adapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取又拍云策略文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名S3预览URL
*
* @return void
*/
public function Preview($base=null,$name=null){
if($base===true || $base ===false){
$base = null;
}
$timeOut = Option::getValue("timeout");
return [1,\S3\S3::aws_s3_link($this->policyModel["ak"], $this->policyModel["sk"],$this->policyModel["bucketname"],"/".$this->fileModel["pre_name"],3600,$this->policyModel["op_name"])];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$s3 = new \S3\S3($this->policyModel["ak"], $this->policyModel["sk"],false,$this->policyModel["op_pwd"]);
$s3->setSignatureVersion('v4');
$s3->putObjectString($content, $this->policyModel["bucketname"], $this->fileModel["pre_name"]);
}
/**
* 删除某一策略下的指定文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
foreach (array_column($fileList, 'pre_name') as $key => $value) {
self::deleteS3File($value,$policyData);
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
$timeOut = Option::getValue("timeout");
return [1,\S3\S3::aws_s3_link($this->policyModel["ak"], $this->policyModel["sk"],$this->policyModel["bucketname"],"/".$this->fileModel["pre_name"],3600,$this->policyModel["op_name"],array(),false)];
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteS3File($fname,$policy){
$s3 = new \S3\S3($policy["ak"], $policy["sk"],false,$policy["op_pwd"]);
$s3->setSignatureVersion('v4');
return $s3->deleteObject($policy["bucketname"],$fname);
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+14
View File
@@ -81,6 +81,20 @@ class ShareHandler extends Model{
}
}
public function getThumb($user,$path,$folder=false){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
if($folder){
$fileObj = new FileManage($path,$this->shareData["owner"]);
}else{
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
}
return $fileObj->getThumb();
}
public function checkSession($user){
if($this->lockStatus){
return [false,"会话过期,请刷新页面"];
+412
View File
@@ -0,0 +1,412 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \app\index\model\FileManage;
use think\console\Input;
use think\console\Output;
use \Krizalys\Onedrive\Client;
use Sabre\DAV\Mock\File;
class Task extends Model{
public $taskModel;
public $taskName;
public $taskType;
public $taskContent;
public $input;
public $output;
public $userId;
public $status = "success";
public $errorMsg;
public $policyModel;
public function __construct($id=null){
if($id!==null){
}
}
/**
* 保存任务至数据库
*
* @return void
*/
public function saveTask(){
Db::name("task")->insert([
"task_name" => $this->taskName,
"attr" => $this->taskContent,
"type" => $this->taskType,
"status" => "todo",
"uid" => $this->userId,
]);
}
/**
* 开始执行任务
*
* @return void
*/
public function Doit(){
switch ($this->taskModel["type"]){
case "uploadSingleToOnedrive":
$this->uploadSingleToOnedrive();
break;
case "UploadRegularRemoteDownloadFileToOnedrive":
$this->uploadSingleToOnedrive();
break;
case "uploadChunksToOnedrive":
$this->uploadChunksToOnedrive();
break;
case "UploadLargeRemoteDownloadFileToOnedrive":
$this->uploadUnchunkedFile();
break;
default:
$this->output->writeln("Unknown task type (".$this->taskModel["type"].")");
break;
}
}
/**
* 上传未分片的大文件至Onedrive
*
* @return void
*/
private function uploadUnchunkedFile(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
//创建分片上传Session,获取上传URL
try{
$uploadUrl = $onedrive->apiPost("/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]).":/createUploadSession",[])->uploadUrl;
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
//创建分片上传Session,获取上传URL
try{
$uploadUrl = $onedrive->apiPost("/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]).":/createUploadSession",[])->uploadUrl;
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
//每次4MB上传文件
if(!$file = @fopen($this->taskContent["originPath"],"r")){
$this->status="error";
$this->errorMsg = "File not exist.";
$this->cleanTmpChunk();
return;
}
$offset = 0;
$totalSize = filesize($this->taskContent["originPath"]);
while (1) {
//移动文件指针
fseek($file, $offset);
$chunksize = (($offset+4*1024*1024)>$totalSize)?($totalSize-$offset):1024*4*1024;
$headers = [];
$headers[] = "Content-Length: ".$chunksize;
$headers[] = "Content-Range: bytes ".$offset."-".($offset+$chunksize-1)."/".$this->taskContent["fsize"];
//发送单个分片数据
try{
$onedrive->sendFileChunk($uploadUrl,$headers,fread($file,$chunksize));
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
$this->output->writeln("[Info] Chunk Uploaded. Offset:".$offset);
$offset+=$chunksize;
if($offset+1 >=$totalSize){
break;
}
}
fclose($file);
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]),$onedrive);
$this->cleanTmpChunk();
return;
}
$this->cleanTmpChunk();
}
/**
* 上传已分片的大文件至Onedrive
*
* @return void
*/
private function uploadChunksToOnedrive(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
//创建分片上传Session,获取上传URL
try{
$uploadUrl = $onedrive->apiPost("/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]).":/createUploadSession",[])->uploadUrl;
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
//逐个上传文件分片
$offset = 0;
foreach ($this->taskContent["chunks"] as $key => $value) {
$chunkPath = ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk";
if(!$file = @fopen($chunkPath,"r")){
$this->status="error";
$this->errorMsg = "File chunk not exist.";
$this->cleanTmpChunk();
return;
}
$headers = [];
$chunksize = filesize($chunkPath);
$headers[] = "Content-Length: ".$chunksize;
$headers[] = "Content-Range: bytes ".$offset."-".($offset+$chunksize-1)."/".$this->taskContent["fsize"];
//发送单个分片数据
try{
$onedrive->sendFileChunk($uploadUrl,$headers,$file);
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
$this->output->writeln("[Info] Chunk Uploaded. Offset:".$offset);
$offset += $chunksize;
fclose($file);
}
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]),$onedrive);
$this->cleanTmpChunk();
return;
}
$this->cleanTmpChunk();
}
/**
* 上传单文件(<=4mb)至Onedrive
*
* @return void
*/
private function uploadSingleToOnedrive(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
$filePath = $this->taskModel["type"] == "UploadRegularRemoteDownloadFileToOnedrive"?$this->taskContent["originPath"]:ROOT_PATH . 'public/uploads/'.$this->taskContent["savePath"] . "/" . $this->taskContent["objname"];
if($file = @fopen($filePath,"r")){
try{
$onedrive->createFile(rawurlencode($this->taskContent["objname"]),"/me/drive/root:/".$this->taskContent["savePath"],$file);
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpFile();
return;
}
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".$this->taskContent["savePath"]."/".rawurlencode($this->taskContent["objname"]),$onedrive);
$this->cleanTmpFile();
return;
}
fclose($file);
$this->cleanTmpFile();
}else{
$this->status = "error";
$this->errorMsg = "Failed to open file [".$filePath."]";
}
}
/**
* 删除本地临时文件
*
* @return bool 是否成功
*/
private function cleanTmpFile(){
if($this->taskModel["type"] == "UploadRegularRemoteDownloadFileToOnedrive"){
return @unlink($this->taskContent["originPath"]);
}else{
return @unlink(ROOT_PATH . 'public/uploads/'.$this->taskContent["savePath"] . "/" . $this->taskContent["objname"]);
}
}
/**
* 删除本地临时分片
*
* @return void
*/
private function cleanTmpChunk(){
if($this->taskModel["type"] == "UploadLargeRemoteDownloadFileToOnedrive"){
@unlink($this->taskContent["originPath"]);
}else{
foreach ($this->taskContent["chunks"] as $key => $value) {
@unlink( ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
}
}
}
/**
* 设置为出错状态并清理远程文件
*
* @param string $msg 错误消息
* @param bool $delete 是否删除文件
* @param string $path 文件路径
* @param mixed $adapter 远程操作适配器
* @return void
*/
private function setError($msg,$delete,$path,$adapter){
$this->status="error";
$this->errorMsg = $msg;
if($delete){
switch($this->taskModel["type"]){
case "uploadSingleToOnedrive":
$adapter->deleteObject($path);
break;
case "uploadChunksToOnedrive":
$adapter->deleteObject($path);
break;
default:
break;
}
}
FileManage::storageGiveBack($this->taskModel["uid"],$this->taskContent["fsize"]);
}
public function setSuccess(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".$this->taskContent["savePath"]."/".rawurlencode($this->taskContent["objname"]),$onedrive);
$this->cleanTmpFile();
Db::name("task")->where("id",$this->taskModel["id"])->update(["status"=>"error|".$this->errorMsg]);
return ["error"=>true,"msg"=>$this->errorMsg];
}
$this->cleanTmpFile();
if($this->taskModel["type"] == "UploadLargeRemoteDownloadFileToOnedrive" || $this->taskModel["type"] == "uploadChunksToOnedrive"){
$this->cleanTmpChunk();
}
Db::name("task")->where("id",$this->taskModel["id"])->update(["status"=>"complete"]);
return ["error"=>false,"msg"=>null];
}
public function Error(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
$this->setError("",false,"/me/drive/root:/".$this->taskContent["savePath"]."/".rawurlencode($this->taskContent["objname"]),$onedrive);
$this->cleanTmpFile();
if($this->taskModel["type"] == "UploadLargeRemoteDownloadFileToOnedrive" || $this->taskModel["type"] == "uploadChunksToOnedrive"){
$this->cleanTmpChunk();
}
Db::name("task")->where("id",$this->taskModel["id"])->update(["status"=>"error"]);
}
}
?>
+7 -6
View File
@@ -20,15 +20,16 @@ class TwoFactor extends Model{
public function qrcodeRender(){
ob_end_clean();
header("content-type: image/png");
$this->secretKey = $this->ga->createSecret();
$qrCode = new QrCode();
session("two_factor_enable",$this->secretKey);
$qrCode->setText(urldecode($this->ga->getQRCodeGoogleUrl(Option::getValue("siteName"), $this->secretKey)))
->setPadding(5)
->render();
$qrCode = new QrCode(urldecode(str_replace("https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=","",$this->ga->getQRCodeGoogleUrl(Option::getValue("siteName"), $this->secretKey))));
$qrCode->setSize(165);
$qrCode->setMargin(0);
header('Content-Type: '.$qrCode->getContentType());
echo $qrCode->writeString();
}
public function confirmCode($key,$code){
$this->secretKey = $key;
if(empty($code)){
@@ -50,4 +51,4 @@ class TwoFactor extends Model{
}
}
?>
?>
+132 -12
View File
@@ -8,6 +8,7 @@ use think\Db;
use Qiniu\Auth;
use \app\index\model\Option;
use \app\index\model\FileManage;
use \app\index\model\Task;
class UploadHandler extends Model{
@@ -75,13 +76,25 @@ class UploadHandler extends Model{
return 0;
}
/**
* 组合分片并生成最终文件
*
* @param array $ctx 文件片校验码
* @param string $fname 最终文件名
* @param string $path 储存目录
* @return void
*/
public function generateFile($ctx,$fname,$path){
$ctxTmp = explode(",",$ctx);
$chunks = Db::name('chunks')->where([
'ctx' => ["in",$ctxTmp],
])->order('id asc')->select();
$file = $this->combineChunks($chunks);
$this->filterCheck($file,$fname);
$file = null;
if($this->policyContent["policy_type"] != "onedrive"){
$file = $this->combineChunks($chunks);
}
$this->filterCheck($file,$fname,$chunks);
$suffixTmp = explode('.', $fname);
$fileSuffix = array_pop($suffixTmp);
if($this->policyContent['autoname']){
@@ -95,28 +108,67 @@ class UploadHandler extends Model{
if(file_exists($savePath.DS.$fileName)){
$this->setError("文件重名",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}
if(!@rename(ROOT_PATH . 'public/uploads/chunks/'.$file,$savePath.DS.$fileName)){
$this->setError("文件创建失败",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}else{
if($this->policyContent["policy_type"] == "onedrive"){
if($path == "ROOTDIR"){
$path = "";
}
$jsonData = array(
$task = new Task();
$task->taskName = "Upload Big File " . $fname . " to Onedrive";
$task->taskType = "uploadChunksToOnedrive";
$task->taskContent = json_encode([
"path" => $path,
"fname" => $fname,
"objname" => $generatePath."/".$fileName,
"objname" => $fileName,
"savePath" => $generatePath,
"fsize" => $this->fileSizeTmp,
);
$addAction = FileManage::addFile($jsonData,$this->policyContent,$this->userId);
"picInfo" => "",
"chunks" => $chunks,
"policyId" => $this->policyContent['id']
]);
$task->userId = $this->userId;
$task->saveTask();
echo json_encode(array("key" => $fname));
}else{
if(!@rename(ROOT_PATH . 'public/uploads/chunks/'.$file,$savePath.DS.$fileName)){
$this->setError("文件创建失败",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}else{
if($path == "ROOTDIR"){
$path = "";
}
$jsonData = array(
"path" => $path,
"fname" => $fname,
"objname" => $generatePath."/".$fileName,
"fsize" => $this->fileSizeTmp,
);
@list($width, $height, $type, $attr) = getimagesize($savePath.DS.$fileName);
$picInfo = empty($width)?" ":$width.",".$height;
$addAction = FileManage::addFile($jsonData,$this->policyContent,$this->userId,$picInfo);
if(!$addAction[0]){
$this->setError($addAction[1],true,$fileName,$savePath);
}
echo json_encode(array("key" => $fname));
echo json_encode(array("key" => $fname));
}
}
}
public function filterCheck($file,$fname){
$fileSize = filesize(ROOT_PATH . 'public/uploads/chunks/'.$file);
protected function countTotalChunkSize($chunks){
$size = 0;
foreach ($chunks as $key => $value) {
$size += @filesize(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
}
return $size;
}
public function filterCheck($file,$fname,$chunks){
if($file !=null){
$fileSize = filesize(ROOT_PATH . 'public/uploads/chunks/'.$file);
}else{
$fileSize = $this->countTotalChunkSize($chunks);
}
$suffixTmp = explode('.', $fname);
$fileSuffix = array_pop($suffixTmp);
$allowedSuffix = explode(',', self::getAllowedExt(json_decode($this->policyContent["filetype"],true)));
@@ -131,6 +183,12 @@ class UploadHandler extends Model{
$this->fileSizeTmp = $fileSize;
}
/**
* 组合文件分片
*
* @param array $fname 文件分片数据库记录
* @return void
*/
public function combineChunks($fname){
$fileName = "file_".self::getRandomKey(8);
$fileObj=fopen (ROOT_PATH . 'public/uploads/chunks/'.$fileName,"a+");
@@ -181,12 +239,39 @@ class UploadHandler extends Model{
);
@list($width, $height, $type, $attr) = getimagesize(rtrim($savePath, DS).DS.$Uploadinfo->getSaveName());
$picInfo = empty($width)?" ":$width.",".$height;
//处理Onedrive等非直传
if($this->policyContent['policy_type'] == "onedrive"){
$task = new Task();
$task->taskName = "Upload" . $info["name"] . " to Onedrive";
$task->taskType = "uploadSingleToOnedrive";
$task->taskContent = json_encode([
"path" => $info["path"],
"fname" => $info["name"],
"objname" => $Uploadinfo->getSaveName(),
"savePath" => $generatePath,
"fsize" => $Uploadinfo->getSize(),
"picInfo" => $picInfo,
"policyId" => $this->policyContent['id']
]);
$task->userId = $this->userId;
$task->saveTask();
echo json_encode(array("key" => $info["name"]));
FileManage::storageCheckOut($this->userId,$jsonData["fsize"],$Uploadinfo->getInfo('size'));
return;
}
//向数据库中添加文件记录
$addAction = FileManage::addFile($jsonData,$this->policyContent,$this->userId,$picInfo);
if(!$addAction[0]){
$tmpFileName = $Uploadinfo->getSaveName();
unset($Uploadinfo);
$this->setError($addAction[1],true,$tmpFileName,$savePath);
}
//扣除容量
FileManage::storageCheckOut($this->userId,$jsonData["fsize"],$Uploadinfo->getInfo('size'));
echo json_encode(array("key" => $info["name"]));
}else{
@@ -219,6 +304,9 @@ class UploadHandler extends Model{
case 'local':
return $this->getLocalToken();
break;
case 'onedrive':
return 'nazGTT91tboaLWBC549$:tHSsNyTBxoV4HDfELJeKH1EUmEY=:eyJjYWxsYmFja0JvZHkiOiJ7XCJwYXRoXCI6XCJcIn0iLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb25cL2pzb24iLCJzY29wZSI6ImMxNjMyMTc3LTQ4NGEtNGU1OS1hZDBhLWUwNDc4ZjZhY2NjZSIsImRlYWRsaW5lIjoxNTM2ODMxOTEwfQ==';
break;
case 'oss':
return $this->getOssToken();
break;
@@ -228,6 +316,9 @@ class UploadHandler extends Model{
case 's3':
return $this->getS3Token();
break;
case 'remote':
return $this->getRemoteToken();
break;
default:
# code...
break;
@@ -295,6 +386,35 @@ class UploadHandler extends Model{
return $token;
}
private function getRemoteToken(){
$callbackKey = $this->getRandomKey();
$sqlData = [
'callback_key' => $callbackKey,
'pid' => $this->policyId,
'uid' => $this->userId
];
Db::name('callback')->insert($sqlData);
$policy = array(
'callbackUrl' =>Option::getValue("siteURL").'Callback/Remote',
'callbackKey' => $callbackKey,
'callbackBodyType' => 'application/json',
'fsizeLimit' => (int)$this->policyContent['max_size'],
'uid' => $this->userId,
);
$dirName = $this->getObjName($this->policyContent['dirrule']);
if($this->policyContent["autoname"]){
$policy = array_merge($policy,array("saveKey" => $dirName.(empty($dirName)?"":"/").$this->getObjName($this->policyContent['namerule'])));
}else{
$policy = array_merge($policy,array("saveKey" => $dirName.(empty($dirName)?"":"/")."$(fname)"));
}
if(!empty($this->policyContent['mimetype'])){
$policy = array_merge($policy,array("mimeLimit" => $this->policyContent['mimetype']));
}
$signingKey = hash_hmac("sha256",json_encode($policy),"UPLOAD".$this->policyContent['sk']);
$token = $signingKey. ":" .base64_encode(json_encode($policy));
return $token;
}
static function upyunSign($key, $secret, $method, $uri, $date, $policy=null, $md5=null){
$elems = array();
foreach (array($method, $uri, $date, $policy, $md5) as $v){
+162
View File
@@ -0,0 +1,162 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use Upyun\Upyun;
use Upyun\Config;
use \app\index\model\Option;
/**
* 又拍云策略文件管理适配器
*/
class UpyunAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取又拍云策略文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名又拍云预览URL
*
* @return void
*/
public function Preview($base=null,$name=null){
if($base===true || $base===false){
$base =null;
}
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?auth=0";
if(!empty($base)){
$fileUrl = $base;
}
return[true,$fileUrl];
}else{
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
if(!empty($base)){
$baseUrl = $base;
}
$etime = time() + Option::getValue("timeout");
$key = $this->policyModel["sk"];
$path = "/".$this->fileModel["pre_name"];
if(!empty($name)){
$path = "/".$name;
}
$sign = substr(md5($key.'&'.$etime.'&'.$path), 12, 8).$etime;
$signedUrl = $baseUrl."?_upt=".$sign;
return[true,$signedUrl];
}
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$bucketConfig = new Config($this->policyModel["bucketname"], $this->policyModel["op_name"], $this->policyModel["op_pwd"]);
$client = new Upyun($bucketConfig);
if(empty($content)){
$content = " ";
}
$res=$client->write($this->fileModel["pre_name"],$content);
}
/**
* 计算缩略图大小
*
* @param int $width 原始宽
* @param int $height 原始高
* @return array
*/
static function getThumbSize($width,$height){
$rate = $width/$height;
$maxWidth = 90;
$maxHeight = 39;
$changeWidth = 39*$rate;
$changeHeight = 90/$rate;
if($changeWidth>=$maxWidth){
return [(int)$changeHeight,90];
}
return [39,(int)$changeWidth];
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
$picInfo = explode(",",$this->fileModel["pic_info"]);
$thumbSize = self::getThumbSize($picInfo[0],$picInfo[1]);
$baseUrl =$this->policyModel["url"].$this->fileModel["pre_name"]."!/fwfh/90x39";
return [1,$this->Preview($baseUrl,$this->fileModel["pre_name"]."!/fwfh/90x39")[1]];
}
/**
* 删除某一策略下的指定upyun文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
foreach (array_column($fileList, 'pre_name') as $key => $value) {
self::deleteUpyunFile($value,$policyData);
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
return [true,$this->Preview()[1]."&_upd=".urlencode($this->fileModel["orign_name"])];
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteUpyunFile($fname,$policy){
$bucketConfig = new Config($policy["bucketname"], $policy["op_name"], $policy["op_pwd"]);
$client = new Upyun($bucketConfig);
$res=$client->delete($fname,true);
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+11 -11
View File
@@ -87,7 +87,7 @@ class User extends Model{
* @return [type] [description]
*/
static function register($userEmail,$userPass,$captchaCode){
if(Option::getValue("login_captcha")=="1"){
if(Option::getValue("reg_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
@@ -193,7 +193,7 @@ class User extends Model{
}
static function findPwd($email,$captchaCode){
if(Option::getValue("login_captcha")=="1"){
if(Option::getValue("forget_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
@@ -308,23 +308,23 @@ class User extends Model{
public function getMemory($notEcho = false){
$usedMemory = $this->userSQLData["used_storage"];
$groupStorage = (int)$this->groupData["max_storage"];
$packetStorage = (int)Db::name('storage_pack')
$groupStorage = $this->groupData["max_storage"];
$packetStorage = Db::name('storage_pack')
->where('uid',$this->uid)
->where('dlay_time',">",time())
->sum('pack_size');
$returnData["used"] = self::countSize((int)$usedMemory);
$returnData["used"] = self::countSize($usedMemory);
$returnData["total"] = self::countSize($groupStorage+$packetStorage);
$returnData["rate"] = floor((int)$usedMemory/($groupStorage+$packetStorage)*100);
$returnData["basic"] = self::countSize((int)$groupStorage);
$returnData["pack"] = self::countSize((int)$packetStorage);
$returnData["rate"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["basic"] = self::countSize($groupStorage);
$returnData["pack"] = self::countSize($packetStorage);
if($usedMemory > $groupStorage){
$returnData["r1"] = floor((int)$usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r1"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = 0;
$returnData["r3"] = 100-$returnData["r1"];
}else{
$returnData["r1"] = floor((int)$usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = floor(((int)$groupStorage-$usedMemory)/($groupStorage+$packetStorage)*100);;
$returnData["r1"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = floor(($groupStorage-$usedMemory)/($groupStorage+$packetStorage)*100);;
$returnData["r3"] = 100-$returnData["r1"]-$returnData["r2"];
}
if($notEcho){
@@ -113,6 +113,18 @@
<div class="col-md-4 option-des"> 是否允许用户使用WebDAV协议同步文件。目前此功能仅支持本地上传方案</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">离线下载</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="aria2" value="1" id="webdav1" >
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="aria2" id="webdav0" value="0" checked>
<label class="" for="webdav0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许用户使用离线下载。此功能仅支持本地存储策略,开启前需要到离线下载管理页面设置Aria2接口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">用户组标志色</label>
+23 -3
View File
@@ -67,11 +67,31 @@
<img class="card-img-top" src="/static/img/s3.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Amazon S3</h5>
<p class="card-text"><a href="https://aws.amazon.com/cn/s3/?nc1=h_ls" target="_blank">Amazon S3</a>专为从任意位置存储和检索任意数量的数据而构建的对象存储</p>
<p class="card-text"><a href="https://aws.amazon.com/cn/s3/?nc1=h_ls" target="_blank">Amazon S3</a>专为数据而构建的对象存储</p>
<a href="/Admin/PolicyAddS3" class="btn btn-primary">添加</a>
</div>
</div>
</div>
<div class="col-md-3 mt-2 md-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/remote.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">远程服务器</h5>
<p class="card-text">你可以将文件存放在不同于主程序的服务器上。<a href="https://github.com/HFO4/Cloudreve/wiki/%E8%BF%9C%E7%A8%8B%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%90%AD%E5%BB%BA">配置说明</a></p>
<a href="/Admin/PolicyAddRemote" class="btn btn-primary">添加</a>
</div>
</div>
</div>
<div class="col-md-3 mt-2 md-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/onedrive.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Onedrive <span class="badge badge-warning">中转</span></h5>
<p class="card-text">支持 Onedrive Bussiness 和个人版。<a href="https://github.com/HFO4/Cloudreve/wiki/Onedrive%E5%AF%B9%E6%8E%A5%E8%AF%B4%E6%98%8E">配置说明</a></p>
<a href="/Admin/PolicyAddOnedrive" class="btn btn-primary">添加</a>
</div>
</div>
</div>
</div>
</div>
@@ -430,7 +450,7 @@
<label for="url" class="col-form-label col-form-label-sm">空间域名</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="url" required>
<input type="text" class="form-control" name="url" value="http(s)://" required>
</div>
<div class="col-md-4 option-des"> OSS空间绑定的域名,用于文件下载、访问,结尾要加"/"(推荐配合阿里云CDN使用,此处填写CDN域名)</div>
</div>
@@ -440,7 +460,7 @@
<label for="server" class="col-form-label col-form-label-sm">上传域名</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="server" required>
<input type="text" class="form-control" name="server" value="http(s)://" required>
</div>
<div class="col-md-4 option-des"> OSS空间的域名,一般与上一项保持一致。(如果上一项填写的是CDN域名,则此处与上一项不应相同)</div>
</div>
@@ -0,0 +1,155 @@
{extend name="header_admin" /}
{block name="title"}添加上传策略- {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item">
<a href="/Admin/PolicyAdd">上传策略</a>
</li>
<li class="breadcrumb-item active">添加</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>添加上传策略</h2>
<br>
<div class="alert alert-warning" role="alert">
<strong>添加Onedrive上传策略前请确保阅读完下面内容!</strong>
<ul>
<li>Onedrive策略无法直传,上传文件时,Cloudreve会先将文件暂存在服务器,之后再由任务队列上传至Onedrive;</li>
<li>使用本策略前,请先配置并运行<a href="https://github.com/HFO4/Cloudreve/wiki/Onedrive%E5%AF%B9%E6%8E%A5%E8%AF%B4%E6%98%8E">Cloudreve任务队列</a>,否则正常无法使用;</li>
<li>用户上传文件后不能立马看到所上传的文件,需要等待任务队列处理完毕;</li>
<li>请确保已经正确配置好Cloudreve的Cron定时任务,以便用于刷新AccessToken信息。</li>
</ul>
</div>
<div class="card" id="s3" >
<div class="card-header">
添加Onedrive上传策略
</div>
<div class="card-body">
<form id="onedrivePolicy">
<input type="text" class="form-control" name="policy_type" value="onedrive" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="bucketname" class="col-form-label col-form-label-sm">应用 IDClient ID </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" required></div>
<div class="col-md-4 option-des"><a href="https://apps.dev.microsoft.com/?referrer=https%3a%2f%2fdeveloper.microsoft.com%2fzh-CN%2fgraph%2fquick-start#/quickstart/graphIO?publicClientSupport=false&appName=Cloudreve&redirectUrl={:urlencode($options.siteURL)}Admin%2FoneDriveCalllback&allowImplicitFlow=false&ru=https:%2F%2Fdeveloper.microsoft.com%2Fzh-CN%2Fgraph%2Fquick-start%3FappID%3D_appId_%26appName%3D_appName_%26redirectUrl%3D{$options.siteURL}Admin/oneDriveCalllback%26platform%3Doption-php" target="_blank">点击这里</a>获取下一步要用到的应用机密和此处的应用ID</div>
</div>
<input type="text" class="form-control" name="sk" value="0" style="display: none" required>
<input type="text" name="bucket_private" value="1" style="display: none">
<input type="text" name="origin_link" value="0" style="display: none">
<input type="text" name="url" value="0" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">应用机密(密码/公钥)</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="ak" required>
</div>
<div class="col-md-4 option-des"> 同上一项一起获取</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="10" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" selected>MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" >
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{uid}_{randomkey8}_{originname}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
Onedrive根目录
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用目录变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="saveQiniu">保存并继续</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
<br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/add_policy.js"></script>
{/block}
@@ -0,0 +1,170 @@
{extend name="header_admin" /}
{block name="title"}添加上传策略- {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item">
<a href="/Admin/PolicyAdd">上传策略</a>
</li>
<li class="breadcrumb-item active">添加</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>添加上传策略</h2>
<br>
<div class="card" id="s3" >
<div class="card-header">
添加远程上传策略
</div>
<div class="card-body">
<form id="qiniuPolicy">
<input type="text" class="form-control" name="policy_type" value="remote" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="ak" class="col-form-label col-form-label-sm">AccessToken </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="sk" required></div>
<div class="col-md-4 option-des"> 默认随机生成,请与远程服务端配置文件中保持一致</div>
</div>
<input type="text" class="form-control" name="ak" value="0" style="display: none" required>
<input type="text" name="bucket_private" value="1" style="display: none">
<input type="text" name="origin_link" value="0" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">服务端URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="server" required>
</div>
<div class="col-md-4 option-des"> 结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">下载根URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="url" required>
</div>
<div class="col-md-4 option-des"> 一般与上一步保持一致,结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="10" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" selected>MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" >
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{uid}_{randomkey8}_{originname}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
/public/uploads/
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用目录变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="saveQiniu">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
<br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/add_policy.js"></script>
<script type="text/javascript">
function randomString(len) {
  len = len || 32;
  var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  var maxPos = $chars.length;
  var pwd = '';
  for (i = 0; i < len; i++) {
    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return pwd;
}
$("[name='sk']").val(randomString(32));
</script>
{/block}
@@ -11,7 +11,14 @@
<li class="breadcrumb-item active">数据概况</li>
</ol>
<!-- Icon Cards-->
{eq name="isCronOk" value="true"}
{else/}
<div class="alert alert-danger" role="alert">
<strong>警告!Cron定时任务疑似未执行</strong><p>检测到<a href="/Admin/Cron">定时任务</a>未正常执行,请检查您的定时任务配置。您可以通过Crontab或者其他网站监控服务,让 “您的域名/Cron” 此地址每隔一段时间被访问一次。</p>
</div>
{/eq}
<div class="row">
<div class="col-xl-3 col-sm-6 mb-3">
<div class="card text-white bg-primary o-hidden h-100">
<div class="card-body">
@@ -48,7 +48,7 @@
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">站点关键词</label>
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">站点描述</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="siteDes" value="{$options.siteDes}"></div>
<div class="col-md-4 option-des"> 位于首页meta标签内的站点描述</div>
+122
View File
@@ -0,0 +1,122 @@
{extend name="header_admin" /}
{block name="title"}离线下载 - {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item active">离线下载</li>
<li class="breadcrumb-item active">配置</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>离线下载</h2>
<br>
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#options"><i class="fa fa-cog" aria-hidden="true"></i> Aria2配置</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#list"><i class="fa fa-list" aria-hidden="true"></i> 任务列表</a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" >
<div class="tab-pane fade show active" id="options" role="tabpanel" aria-labelledby="pills-home-tab">
<form id="aria2Options">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">RPC Server</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_rpcurl" value="{$optionsForSet.aria2_rpcurl}" spellcheck="false"></div>
<div class="col-md-4 option-des"> aria2的RPC服务器地址,请在aria2的配置文件中启用RPC服务。例如:http://127.0.0.1:6800/</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">RPC Token</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_token" value="{$optionsForSet.aria2_token}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 在配置文件中设置的RPC服务的Token</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">下载临时目录</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_tmppath" value="{$optionsForSet.aria2_tmppath}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 下载文件数据的临时存放目录,请确保PHP对该目录拥有读写权限</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="aria2_options" class="col-form-label col-form-label-sm">其他下载参数</label>
</div>
<div class="col-md-4"> <textarea class="form-control" name="aria2_options" spellcheck="false">{$optionsForSet.aria2_options}</textarea></div>
<div class="col-md-4 option-des"> aria2启动下载的其他附带参数,请以json格式书写。你可也可以将这些设置写在aria2配置文件里。可用参数请查阅<a href="https://aria2.github.io/manual/en/html/aria2c.html#options" target="_blank">官方文档</a></div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="button" class="btn btn-primary" id="saveAria2">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
<div class="tab-pane fade" id="list" role="tabpanel" aria-labelledby="pills-profile-tab">
<table class="table">
<thead>
<tr>
<th scope="col" class="textCenter">#</th>
<th scope="col" width="50%">文件名</th>
<th scope="col" class="textCenter">创建者</th>
<th scope="col" class="textCenter">文件大小</th>
<th scope="col" class="textCenter">状态</th>
<th scope="col" class="textCenter">进度</th>
<th scope="col" class="textCenter">操作</th>
</tr>
</thead>
<tbody id="listContent">
{volist name='list' id='download'}
<tr id="i-{$download.id}" data-pid="{$download.pid}">
<th scope="row" class="textCenter">{$download.id}</th>
<td>{:$originList[$key]['fileName']}</td>
<td class="textCenter">{:$originList[$key]['user']["user_nick"]}</td>
<td class="textCenter">{:countSize($download.total_size)}</td>
<td class="textCenter">{$download.status}</td>
<td class="textCenter">{eq name="$originList[$key]['totalLength']" value="0"}-{else/}{:floor($originList[$key]['completedLength']/$originList[$key]['totalLength']*10000)/100}%{/eq}</td>
<td class="textCenter">{eq name="download.status" value="active"}<a href="javascript:" onclick="cancel('{$download.id}')">取消任务</a>{else/} - {/eq}</td>
</tr>
{/volist}
</tbody>
</table>
{$list->render()}
</div>
<div class="tab-pane fade" id="tools" role="tabpanel" aria-labelledby="pills-profile-tab">
ddd
</div>
</div>
</div>
</div><br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/summernote.min.js"></script>
<script src="/static/js/admin/summernote-zh-CN.min.js"></script>
<script src="/static/js/admin/aria2.js"></script>
{/block}
+18 -1
View File
@@ -114,6 +114,18 @@
<div class="col-md-4 option-des"> 是否允许用户使用WebDAV协议同步文件。目前此功能仅支持本地上传方案</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">离线下载</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="aria2" value="1" id="webdav1" >
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="aria2" id="webdav0" value="0">
<label class="" for="webdav0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许用户使用离线下载。此功能仅支持本地存储策略,开启前需要到离线下载管理页面设置Aria2接口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">用户组标志色</label>
@@ -162,10 +174,15 @@
<script src="/static/js/admin/add_group.js"></script>
<script type="text/javascript">
$("option[value='{$group.policy_name}']").attr("selected",true);
$("option[value='{$group.max_storage}']").attr("selected",true);
$("option[value='{:countSize($group.max_storage,true)[1]}']").attr("selected",true);
$("input[name='range_transfer'][value='{$group.range_transfer}']").attr("checked",true);
$("input[name='allow_share'][value='{$group.allow_share}']").attr("checked",true);
$("input[name='color'][value='{$group.color}']").attr("checked",true);
$("input[name='webdav'][value='{$group.webdav}']").attr("checked",true);
if('{$group.aria2}'=="1,1,1"){
$("input[name='aria2'][value='1']").attr("checked",true);
}else{
$("input[name='aria2'][value='0']").attr("checked",true);
}
</script>
{/block}
+247 -1
View File
@@ -684,7 +684,7 @@
{case value="s3"}
<div class="card" id="s3" >
<div class="card-header">
添加S3上传策略
编辑S3上传策略
</div>
<div class="card-body">
<form id="Policy">
@@ -804,6 +804,252 @@
</div>
{/case}
{case value="remote"}
<div class="card" id="remote" >
<div class="card-header">
编辑远程上传策略
</div>
<div class="card-body">
<form id="Policy">
<input type="text" value="{$policy.id}" name="id" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" value="{$policy.policy_name}" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="ak" class="col-form-label col-form-label-sm">AccessToken </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="sk" value="{$policy.sk}" required></div>
<div class="col-md-4 option-des"> 默认随机生成,请与远程服务端配置文件中保持一致</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">服务端URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="server" value="{$policy.server}" required>
</div>
<div class="col-md-4 option-des"> 结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">下载根URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="url" value="{$policy.url}" required>
</div>
<div class="col-md-4 option-des"> 一般与上一步保持一致,结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="{:countSize($policy.max_size,true)[0]}" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn" data-value="{:countSize($policy.max_size,true)[1]}">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" >MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
{eq name="$policy.autoname" value="1"}
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
{else/}
<input class="" type="radio" name="autoname" id="autoname1" value="1" >
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" checked>
<label class="" for="autoname2" >关闭</label>
{/eq}
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" style=" {eq name="$policy.autoname" value="0"}display:none{/eq}">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{$policy.namerule}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTable">变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
/public/uploads/
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{$policy.dirrule}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTableFolder">目录变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="savePolicy">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
{/case}
{case value="onedrive"}
<div class="alert alert-warning" role="alert">
<strong>添加Onedrive上传策略前请确保阅读完下面内容!</strong>
<ul>
<li>Onedrive策略无法直传,上传文件时,Cloudreve会先将文件暂存在服务器,之后再由任务队列上传至Onedrive;</li>
<li>使用本策略前,请先配置并运行<a href="https://github.com/HFO4/Cloudreve/wiki/Onedrive%E5%AF%B9%E6%8E%A5%E8%AF%B4%E6%98%8E">Cloudreve任务队列</a>,否则正常无法使用;</li>
<li>用户上传文件后不能立马看到所上传的文件,需要等待任务队列处理完毕;</li>
<li>请确保已经正确配置好Cloudreve的Cron定时任务,以便用于刷新AccessToken信息。</li>
</ul>
</div>
<div class="card" id="s3" >
<div class="card-header">
添加Onedrive上传策略
</div>
<div class="card-body">
<form id="Policy">
<input type="text" value="{$policy.id}" name="id" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" value="{$policy.policy_name}" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="bucketname" class="col-form-label col-form-label-sm">应用 IDClient ID </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" value="{$policy.bucketname}" required></div>
<div class="col-md-4 option-des"><a href="https://apps.dev.microsoft.com/?referrer=https%3a%2f%2fdeveloper.microsoft.com%2fzh-CN%2fgraph%2fquick-start#/quickstart/graphIO?publicClientSupport=false&appName=Cloudreve&redirectUrl={:urlencode($options.siteURL)}Admin%2FoneDriveCalllback&allowImplicitFlow=false&ru=https:%2F%2Fdeveloper.microsoft.com%2Fzh-CN%2Fgraph%2Fquick-start%3FappID%3D_appId_%26appName%3D_appName_%26redirectUrl%3D{$options.siteURL}Admin/oneDriveCalllback%26platform%3Doption-php" target="_blank">点击这里</a>获取下一步要用到的应用机密和此处的应用ID</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">应用机密(密码/公钥)</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="ak" value="{$policy.ak}" required>
</div>
<div class="col-md-4 option-des"> 同上一项一起获取</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="{:countSize($policy.max_size,true)[0]}" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn" data-value="{:countSize($policy.max_size,true)[1]}">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" >MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
{eq name="$policy.autoname" value="1"}
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
{else/}
<input class="" type="radio" name="autoname" id="autoname1" value="1" >
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" checked>
<label class="" for="autoname2" >关闭</label>
{/eq}
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" style=" {eq name="$policy.autoname" value="0"}display:none{/eq}">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{$policy.namerule}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTable">变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
Onedrive根目录/
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{$policy.dirrule}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTableFolder">目录变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="saveQiniu">保存</button> <a class="btn btn-danger" href="/Admin/UpdateOnedriveToken?id={$policy.id}" id="saveQiniu">重新授权账号(请先保存)</a></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
{/case}
{/switch}
<br>
</div>
@@ -103,7 +103,7 @@
</div>
</form>
</div>
<div class="tab-pane fade" id="test" role="tabpanel" aria-labelledby="pills-profile-tab">
<form id="testMail">
<div class="row form-setting">
@@ -145,7 +145,7 @@
<div class="tab-pane fade" id="template" role="tabpanel" aria-labelledby="pills-profile-tab">
<form id="mailTemplate">
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
@@ -66,6 +66,37 @@
<div class="col-md-4 option-des"> 用于展示用户头像的Gravatar服务器地址</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="gravatar_server" class="col-form-label col-form-label-sm">文件数据发送模式</label>
</div>
<div class="col-md-4">
<select class="form-control" name="sendfile">
<option value="0">传统</option>
<option value="1" {eq name="optionsForSet.sendfile" value="1"}selected{/eq}>X-Sendfile</option>
</select>
</div>
<div class="col-md-4 option-des"> 推荐使用X-Sendfile以获得高效的文件传输,启用前请确保服务器安装X-Sendfile模块并在站点配置文件中启用。</div>
</div>
<div class="row form-setting" style="{eq name="optionsForSet.sendfile" value="0"}display:none{/eq}" id="sendfile_header">
<div class="col-md-1 form-label ">
<label for="gravatar_server" class="col-form-label col-form-label-sm">Sendfile Header</label>
</div>
<div class="col-md-4">
<select class="form-control" name="header">
<option value="X-Sendfile" {eq name="optionsForSet.header" value="X-Sendfile"}selected{/eq}>X-Sendfile [Apache,Lighttpd v1.5,Cherokee]</option>
<option value="X-LIGHTTPD-send-file" {eq name="optionsForSet.header" value="X-LIGHTTPD-send-file"}selected{/eq}>X-LIGHTTPD-send-file [Lighttpd v1.4]</option>
<option value="X-Accel-Redirect" {eq name="optionsForSet.header" value="X-Accel-Redirect"}selected{/eq}>X-Accel-Redirect [Nginx,Cherokee]</option>
</select>
</div>
<div class="col-md-4 option-des"> 请根据你所使用的服务器软件选择合适的header</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
@@ -42,6 +42,12 @@
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="s3">S3</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="remote">远程</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="onedrive">Onedrive</a>
</li>
</ul>
</div>
</div>
@@ -78,6 +84,8 @@
{case value="oss"}OSS{/case}
{case value="upyun"}又拍云{/case}
{case value="s3"}Amazon S3{/case}
{case value="remote"}远程{/case}
{case value="onedrive"}Onedrive{/case}
{default /}其他
{/switch}
</td>
+99
View File
@@ -0,0 +1,99 @@
{extend name="header_admin" /}
{block name="title"}任务队列 - {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item active">任务队列</li>
<li class="breadcrumb-item active">配置</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>任务队列</h2>
<br>
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#options"><i class="fa fa-cog" aria-hidden="true"></i> 配置</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#list"><i class="fa fa-list" aria-hidden="true"></i> 任务列表</a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" >
<div class="tab-pane fade show active" id="options" role="tabpanel" aria-labelledby="pills-home-tab">
<form id="taskOptions">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">Token</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="task_queue_token" id="task_queue_token" value="{$taskOption}" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 任务队列鉴权Token,应该与任务队列配置文件conf.yaml中的token保持一致,留空表示禁止任务队列请求</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="button" class="btn btn-outline-secondary" id="generateToken">随机生成Token</button> <button type="button" class="btn btn-primary" id="saveTask">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
<div class="tab-pane fade" id="list" role="tabpanel" aria-labelledby="pills-profile-tab">
<table class="table">
<thead>
<tr>
<th scope="col" class="textCenter">#</th>
<th scope="col" width="50%">任务名</th>
<th scope="col" class="textCenter">状态</th>
<th scope="col" class="textCenter">创建日期</th>
<th scope="col" class="textCenter">创建者UID</th>
</tr>
</thead>
<tbody id="listContent">
{volist name='task' id='t'}
<tr id="i-{$t.id}" >
<th scope="row" class="textCenter">{$t.id}</th>
<td>{$t.task_name}</td>
<td class="textCenter">{$t.status}</td>
<td class="textCenter">{$t.addtime}</td>
<td class="textCenter">{$t.uid}</td>
</tr>
{/volist}
</tbody>
</table>
{$task->render()}
</div>
<div class="tab-pane fade" id="tools" role="tabpanel" aria-labelledby="pills-profile-tab">
ddd
</div>
</div>
</div>
</div><br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/summernote.min.js"></script>
<script src="/static/js/admin/summernote-zh-CN.min.js"></script>
<script src="/static/js/admin/task.js"></script>
{/block}
+1
View File
@@ -6,6 +6,7 @@
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="keywords" content="{$options.siteKeywords}"/>
<meta name="description" content="{$options.siteDes}"/>
<meta name="theme-color" content="#4e64d9"/>
<title>{$options.siteName} - {$options.siteTitle}</title>
<!-- Font Awesome -->
+9
View File
@@ -76,6 +76,12 @@
<span class="nav-link-text">分享</span>
</a>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="离线下载">
<a class="nav-link" href="/Admin/RemoteDownload">
<i class="fa fa-fw fa-cloud-download"></i>
<span class="nav-link-text">离线下载</span>
</a>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="用户">
<a class="nav-link" href="/Admin/Users" data-parent="#user">
<i class="fa fa-fw fa-user"></i>
@@ -116,6 +122,9 @@
<span class="nav-link-text">其他</span>
</a>
<ul class="sidenav-second-level collapse" id="else">
<li>
<a href="/Admin/Queue">任务队列</a>
</li>
<li>
<a href="/Admin/Cron">定时任务</a>
</li>
+2 -11
View File
@@ -1,27 +1,18 @@
<!doctype html>
<html lang="zh-cn" data-ng-app="FileManagerApp">
<head>
<!--
* Angular FileManager v1.5.1 (https://github.com/joni2back/angular-filemanager)
* Jonas Sciangula Street <joni2back@gmail.com>
* Licensed under MIT (https://github.com/joni2back/angular-filemanager/blob/master/LICENSE)
-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>{block name="title"}{/block}</title>
<!-- third party -->
<meta name="theme-color" content="#4e64d9"/>
<title>{block name="title"}{/block}</title>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/material.css" />
<script src="/static/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<!-- /third party -->
<!-- Comment if you need to use raw source code -->
<link href="/static/css/angular-filemanager.min.css" rel="stylesheet">
<link href="/static/css/toastr.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<!-- /Comment if you need to use raw source code -->
<script type="text/javascript">
</script>
{block name="content"}{/block}
@@ -2,6 +2,7 @@
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<meta name="theme-color" content="#4e64d9"/>
<title>{block name="title"}{/block}</title>
<!-- third party -->
<script src="/static/js/jquery.min.js"></script>
+81
View File
@@ -0,0 +1,81 @@
{extend name="header_home" /}
{block name="title"}离线下载管理- {$options.siteName}{/block}
{block name="content"}
<script src="/static/js/remoteDownload.js"></script>
<style type="text/css">
.col-md-3{
padding-right: 15px;
padding-left: 15px;
}
</style>
</head>
<body >
<div id="container">
{include file="navbar_home" /}
<div class="col-md-10 quota_content">
<h1>离线下载管理</h1>
<br>
<div class="fix_side">
<div class="fix">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">正在进行中 <i class="fa fa-refresh" aria-hidden="true" title="刷新数据" onclick="refresh()"></i></div>
<div class="panel-body centerTable" id="loadStatus">
更新状态数据中...
</div>
<div class="table-responsive" style="display: none">
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">下载速度</th>
<th class="centerTable">进度</th>
<th class="centerTable">操作</th>
</tr>
</thead>
<tbody id="itemContent">
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">已完成 </i></div>
<div class="table-responsive" >
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">状态</th>
</tr>
</thead>
<tbody id="completeItemContent">
</tbody>
</table>
</div>
<div class="panel-body centerTable" id="loadButton">
<button class="btn btn-primary" id="loadFinished">点击加载已完成列表</button>
</div>
</div>
</div>
</div>
<br>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
{/block}
+81
View File
@@ -0,0 +1,81 @@
{extend name="header_home" /}
{block name="title"}离线下载管理- {$options.siteName}{/block}
{block name="content"}
<script src="/static/js/remoteDownload.js"></script>
<style type="text/css">
.col-md-3{
padding-right: 15px;
padding-left: 15px;
}
</style>
</head>
<body >
<div id="container">
{include file="navbar_home" /}
<div class="col-md-10 quota_content">
<h1>离线下载管理</h1>
<br>
<div class="fix_side">
<div class="fix">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">正在进行中 <i class="fa fa-refresh" aria-hidden="true" title="刷新数据" onclick="refresh()"></i></div>
<div class="panel-body centerTable" id="loadStatus">
更新状态数据中...
</div>
<div class="table-responsive" style="display: none">
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">下载速度</th>
<th class="centerTable">进度</th>
<th class="centerTable">操作</th>
</tr>
</thead>
<tbody id="itemContent">
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">已完成 </i></div>
<div class="table-responsive" >
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">状态</th>
</tr>
</thead>
<tbody id="completeItemContent">
</tbody>
</table>
</div>
<div class="panel-body centerTable" id="loadButton">
<button class="btn btn-primary" id="loadFinished">点击加载已完成列表</button>
</div>
</div>
</div>
</div>
<br>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
{/block}
+5 -14
View File
@@ -1,34 +1,23 @@
<!doctype html>
<html lang="en" data-ng-app="FileManagerApp">
<html lang="zh_cn" data-ng-app="FileManagerApp">
<head>
<!--
* Angular FileManager v1.5.1 (https://github.com/joni2back/angular-filemanager)
* Jonas Sciangula Street <joni2back@gmail.com>
* Licensed under MIT (https://github.com/joni2back/angular-filemanager/blob/master/LICENSE)
-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<meta name="theme-color" content="#4e64d9"/>
<title>我的文件 - {$options.siteName}</title>
<!-- third party -->
<script src="/static/js/angular.min.js"></script>
<script src="/static/js/angular-translate.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/material.css" />
<script src="/static/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<!-- /third party -->
<!-- Comment if you need to use raw source code -->
<link href="/static/css/angular-filemanager.min.css" rel="stylesheet">
<link href="/static/css/toastr.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<script src="/static/js/angular-filemanager.min.js"></script>
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<!-- /Comment if you need to use raw source code -->
<script type="text/javascript">
uploadConfig={
saveType : "{$policyData.policy_type}",
@@ -39,6 +28,8 @@
allowSource : "{$policyData.origin_link}",
upUrl : "{$policyData.server}",
allowShare:"{$groupData.allow_share}",
allowRemoteDownload:"{:explode(",",$groupData.aria2)[0]}",
allowTorrentDownload:"{:explode(",",$groupData.aria2)[1]}",
};
</script>
<script src="/static/js/home.js"></script>
@@ -106,4 +97,4 @@ upload_load=0;
</script>
{$options.js_code}
</html>
</html>
+3 -2
View File
@@ -3,7 +3,7 @@
<!--Video-->
<div class="intro-video view" id="home">
<img src="static/img/output.jpg">
<div class="hm-gradient">
@@ -16,7 +16,8 @@
<hr class="hr-light">
<h4 class="subtext-header mt-2 mb-2">只需十秒钟,注册后即可开始使用免费高速的云存储服务</h4>
</div>
<a href="/Login" class="btn btn-rounded btn-light-green-2" ><i class="fa fa-paper-plane-o"></i> 立即注册</a>
<a href="/Login" class="btn btn-rounded btn-light-green-2" ><i class="fa fa-paper-plane-o"></i> 注册</a>
<a href="/Home" class="btn btn-rounded btn-light-green-2" ><i class="fa fa-sign-in"></i> 登录</a>
</div>
</div>
</div>
+3 -3
View File
@@ -57,7 +57,7 @@
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="loginButton">登录</button>
</form> <button class="btn btn-raised btn-primary waves-light" id="loginButton">登录</button>
<div class="link-group">
<a href="javascript:void" class="noWave" id="create" data-change="true">创建一个账户</a><br>
<a href="javascript:void" class="noWave" id="forgetSwitch2" data-change="true">忘记密码?</a>
@@ -105,7 +105,7 @@
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="regButton">注册</button> <div class="link-group">
</form> <button class="btn btn-raised btn-primary waves-light" id="regButton">注册</button> <div class="link-group">
<a href="javascript:void" class="noWave" id="loginSwitch3" data-change="true">已有账号?立即登录</a><br>
<a href="javascript:void" class="noWave" id="forgetSwitch" data-change="true">忘记密码?</a>
</div>
@@ -163,7 +163,7 @@
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="findMyFuckingPwd">找回密码</button> <div class="link-group">
</form> <button class="btn btn-raised btn-primary waves-light" id="findMyFuckingPwd">找回密码</button> <div class="link-group">
<a href="javascript:void" class="noWave" id="loginSwitch2" data-change="true">返回登录</a><br>
</div>
</div>
+21 -4
View File
@@ -2,7 +2,7 @@
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<a class="navbar-brand" href="/">
<a class="navbar-brand waves-light" href="/">
</a>
@@ -19,14 +19,26 @@
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle avatar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userInfo.uid}/s" class="img-circle avatar-s"> {$userInfo.userNick} <span class="caret"></span></a>
<a href="#" class="dropdown-toggle avatar-a waves-light" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userInfo.uid}/s" class="img-circle avatar-s"> {$userInfo.userNick} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/Profile/{$userInfo.uid}">个人主页</a></li>
<li><a href="/Member/Setting">设置</a></li>
<li role="separator" class="divider"></li>
{eq name="$userInfo.groupId" value="1"}
<li><a href="/Admin">管理面板</a></li>
{/eq}
<li role="separator" class="divider"></li>
<li><a href="/Member/LogOut">退出登录</a></li>
</ul>
</li>
<li class="mobile-addition">
<a href="/Share/My" role="button" aria-haspopup="true" ><i class="fa fa-share-alt" aria-hidden="true"></i> 我的分享</a>
</li>
<li class="mobile-addition">
<a href="/Explore/Search" role="button" aria-haspopup="true" ><i class="fa fa-search" aria-hidden="true"></i> 搜索分享</a>
</li>
<li class="mobile-addition">
<a href="/Home/Album" role="button" aria-haspopup="true" ><i class="fa fa-picture-o" aria-hidden="true"></i> 图片集</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
@@ -51,7 +63,7 @@
<div id="container">
<button class="btn btn-raised btn-info btn-lg upload_button" id="pickfiles" >
<button class="btn btn-raised btn-info btn-lg upload_button waves-light" id="pickfiles" >
<i class="glyphicon glyphicon-plus"></i>
<span id="up_text"></span>
</button>
@@ -117,6 +129,11 @@
<a href="/Home/Album" class="list-group-item">
<i class="fa fa-picture-o" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 图片集
</a>
{eq name=':(explode(",",$groupData.aria2)[0] OR explode(",",$groupData.aria2)[1])' value="1"}
<a href="/Home/Download" class="list-group-item">
<i class="fa fa-cloud-download" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 离线下载
</a>
{/eq}
</div>
<div clss="usage" style=" visibility: visible;
position: absolute;
+3 -3
View File
@@ -1,6 +1,6 @@
<div class="navbar-header">
<div >
<a class="navbar-brand" href="/">
<a class="navbar-brand waves-light" href="/">
</a>
</div>
@@ -19,7 +19,7 @@
<ul class="nav navbar-nav navbar-right">
{eq name="$loginStatus" value="1"}
<li class="dropdown">
<a href="#" class="dropdown-toggle avatar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userData.id}/s" class="img-circle avatar-s"> {$userData.user_nick} <span class="caret"></span></a>
<a href="#" class="dropdown-toggle avatar-a waves-light" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userData.id}/s" class="img-circle avatar-s"> {$userData.user_nick} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/Home">我的文件</a></li>
<li><a href="/Profile/{$userData.id}">个人主页</a></li>
@@ -30,7 +30,7 @@
</li>
{else/}
<li >
<a href="/Login" class="dropdown-toggle" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-user mr-1"></i> 登录/注册 </a>
<a href="/Login" class="dropdown-toggle waves-light" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-user mr-1"></i> 登录/注册 </a>
</li>
{/eq}
+2 -2
View File
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" data-ng-app="FileManagerApp">
<html lang="zh_cn" data-ng-app="FileManagerApp">
<head>
<!--
* Angular FileManager v1.5.1 (https://github.com/joni2back/angular-filemanager)
@@ -80,4 +80,4 @@ upload_load=0;
</script>
{$options.js_code}
</html>
</html>
+3 -28
View File
@@ -70,34 +70,9 @@
</div>
</div>
</div>
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userInfo /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main-h">
<div class="">
<div class="jumbotron">
<div class="card_botom">
<div class="row bottom-width">
<div class="manager"><angular-filemanager></angular-filemanager>
</div>
</div>
</div>
</div></div>
</div>
</div>
<angular-filemanager></angular-filemanager>
</body>
<script type="text/javascript">
shareInfo={
+97 -103
View File
@@ -1,37 +1,35 @@
{extend name="header_public" /}
{block name="title"}{$fileData.orign_name} - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/share.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<link rel="stylesheet" href="/static/css/share.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<div class="modal fade" id="previewModal" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" onclick="audioPause()" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only ng-binding">关闭</span>
</button>
<h4 class="modal-title">视频预览</h4>
</div>
<div class="modal-body">
<div class="text-center previewContent">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" onclick="audioPause()" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only ng-binding">关闭</span>
</button>
<h4 class="modal-title">视频预览</h4>
</div>
<div class="modal-body">
<div class="text-center previewContent">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
@@ -47,7 +45,6 @@
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
@@ -55,14 +52,14 @@
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
@@ -71,84 +68,81 @@
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
</div>
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
<div class="container main" >
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="file-sign col-xs-2">
<i class="fa fa-file-image-o" aria-hidden="true"></i>
</div>
<div class="file_title col-xs-10">
</div>
<div class="container main" >
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="file-sign col-xs-2">
<i class="fa fa-file-image-o" aria-hidden="true"></i>
</div>
<div class="file_title col-xs-10">
<div class="file_title_inside str_wrap">
{$fileData.orign_name|htmlspecialchars=ENT_NOQUOTES}&nbsp;&nbsp;
{$fileData.orign_name|htmlspecialchars=ENT_NOQUOTES}&nbsp;&nbsp;
</div>
<div class="file_info"><span id="size"></span>&nbsp;&nbsp;<span><i class="fa fa-cloud-download" aria-hidden="true"></i> <span id="down_num"></span></span>&nbsp;&nbsp;<span><i class="fa fa-eye" aria-hidden="true"></i> <span id="view_num"></span></span></div>
</div>
</div>
</div>
<div class="card-middle">
<button class="btn btn-info btn-fab" id="previewButton"><i class="material-icons fa fa-eye"></i><div class="ripple-container"></div></button>
<button class="btn btn-primary btn-fab" id="download"><i class="material-icons fa fa-download"></i><div class="ripple-container"></div></button>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<div class="avatar ">
<img src="/Member/Avatar/{$userInfo.id}/s" class="img-circle animated rotateIn">
<div class="nick">
<span class="nickname"><a class="notWave" href="/Profile/{$userInfo.id}">{$userInfo.user_nick}</a></span>
<br>
<span class="share_time">分享于<span id="share_time"></span></span>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-3"></div>
</div>
<div class="card-middle">
<button class="btn btn-info btn-fab" id="previewButton"><i class="material-icons fa fa-eye"></i><div class="ripple-container"></div></button>
<button class="btn btn-primary btn-fab" id="download"><i class="material-icons fa fa-download"></i><div class="ripple-container"></div></button>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<div class="avatar ">
<img src="/Member/Avatar/{$userInfo.id}/s" class="img-circle animated rotateIn">
<div class="nick">
<span class="nickname"><a class="notWave" href="/Profile/{$userInfo.id}">{$userInfo.user_nick}</a></span>
<br>
<span class="share_time">分享于<span id="share_time"></span></span>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-3"></div>
</div>
</body>
<script src="/static/js/jquery.liMarquee.js"></script>
<script type="text/javascript">
shareInfo={
fileSize : "{$fileData.size}",
shareDate : "{$shareData.share_time}",
ownerUid:"{$userInfo.id}",
ownerNick:"{$userInfo.user_nick}",
downloadNum:"{$shareData.download_num}",
ViewNum:"{$shareData.view_num}",
shareId:"{$shareData.share_key}",
fileName:"{$fileData.orign_name}",
picSize:"{$fileData.pic_info}",
{eq name="$loginStatus" value="1"}
allowPreview:true,
{else/}
allowPreview:{$allowPreview},
{/eq}
};
</script>
<script src="/static/js/share_single.js"> </script>
{$options.js_code}
{/block}
</div>
</body>
<script src="/static/js/jquery.liMarquee.js"></script>
<script type="text/javascript">
shareInfo={
fileSize : "{$fileData.size}",
shareDate : "{$shareData.share_time}",
ownerUid:"{$userInfo.id}",
ownerNick:"{$userInfo.user_nick}",
downloadNum:"{$shareData.download_num}",
ViewNum:"{$shareData.view_num}",
shareId:"{$shareData.share_key}",
fileName:"{$fileData.orign_name}",
picSize:"{$fileData.pic_info}",
{eq name="$loginStatus" value="1"}
allowPreview:true,
{else/}
allowPreview:{$allowPreview},
{/eq}
};
</script>
<script src="/static/js/share_single.js"> </script>
{$options.js_code}
{/block}
+1 -1
View File
@@ -15,7 +15,7 @@ Route::rule([
's/:key'=>'index/Share/index',
'Share/Download/:key'=>'index/Share/Download',
'Share/Preview/:key'=>'index/Share/Preview',
'Share/List/:key'=>'index/Share/List',
'Share/ListFile/:key'=>'index/Share/ListFile',
'Login'=>'index/Member/LoginForm',
'Member/emailActivate/:key'=>'index/Member/emailActivate',
'Member/resetPwd/:key'=>'index/Member/resetPwd',
+1 -1
View File
@@ -1 +1 @@
{"type":"","version":"1.0 Beta","version_id":1}
{"type":"","version":"1.1.1","version_id":6,"db_version":4}
+32 -20
View File
@@ -4,35 +4,47 @@
"keywords": ["php", "cloud", "ThinkPHP", "file-sharing"],
"homepage": "https://cloudreve.org",
"type": "project",
"license": "GPL-3.0-only",
"license": "GPL-3.0-only",
"authors": [
{
"name": "Aaron",
"email": "abslant@foxmail.com",
"homepage": "https://aoaoao.me",
"role": "Developer"
},
{
"name": "Qnner",
"email": "admin@qnner.com",
"homepage": "http://me.qnner.com",
"role": "Developer"
},
{
"name": "dune",
"email": "rainaysann@gmail.com",
"homepage": "https://github.com/rainays",
"role": "Developer"
}
{
"name": "Aaron",
"email": "abslant@foxmail.com",
"homepage": "https://aoaoao.me",
"role": "Developer"
},
{
"name": "Qnner",
"email": "admin@qnner.com",
"homepage": "http://me.qnner.com",
"role": "Developer"
},
{
"name": "dune",
"email": "rainaysann@gmail.com",
"homepage": "https://github.com/rainays",
"role": "Developer"
}
],
"require": {
"ext-curl":"*",
"ext-fileinfo":"*",
"ext-gd":"*",
"topthink/think-captcha":"1.*",
"endroid/qrcode": "1.*",
"aliyuncs/oss-sdk-php": "~2.0",
"sabre/dav":"~3.2.0",
"upyun/sdk": "^3.3"
},
"autoload": {
"psr-0": {
"CloudreveInstaller\\Installer" : ""
}
},
"scripts": {
"post-install-cmd": [
"CloudreveInstaller\\Installer::startInstall"
],
"post-create-project-cmd" : [
"CloudreveInstaller\\Installer::startInstall"
]
}
}
+2
View File
@@ -0,0 +1,2 @@
zip -r -q -o pack.zip ./
curl -F "token=$TOKEN" -F "commit=$TRAVIS_COMMIT" -F "filename=@pack.zip" -H "Expect:" http://cloudreve.org/deploy.php
File diff suppressed because it is too large Load Diff
+298
View File
@@ -0,0 +1,298 @@
<?php
namespace Krizalys\Onedrive;
/**
* @class DriveItem
*
* A DriveItem instance is an entity that may be stored in a OneDrive account.
* There are two types of drive items: file or a folder, each of which being a
* subclass of the DriveItem class.
*
* Note that DriveItem instances are only "proxy" to actual OneDrive drive items
* (eg. destroying a DriveItem instance will not delete the actual OneDrive
* drive item it is referencing to).
*/
abstract class DriveItem
{
/**
* @var Client The owning Client instance.
*/
protected $_client;
/**
* @var string The unique ID assigned by OneDrive to this drive item.
*/
protected $_id;
/**
* @var string The unique ID assigned by OneDrive to the parent folder of
* this drive item.
*/
private $_parentId;
/**
* @var string The name of this drive item.
*/
private $_name;
/**
* @var string The description of this drive item.
*/
private $_description;
/**
* @var int The size of this drive item, in bytes.
*/
private $_size;
/**
* @var string The source link of this drive item.
*/
private $_source;
/**
* @var int The creation time, in seconds since UNIX epoch.
*/
private $_createdTime;
/**
* @var int The last modification time, in seconds since UNIX epoch.
*/
private $_updatedTime;
/**
* Constructor.
*
* @param Client $client The Client instance owning this DriveItem
* instance.
* @param null|string $id The unique ID of the OneDrive drive item
* referenced by this DriveItem instance.
* @param array|object $options An array/object with one or more of the
* following keys/properties:
* - 'parent_id' (string) The unique ID of
* the parent OneDrive folder of this drive
* item.
* - 'name' (string) The name of this drive
* item.
* - 'description' (string) The description of
* this drive item. May be empty.
* - 'size' (int) The size of this drive item,
* in bytes.
* - 'source' (string) The source link of this
* drive item.
* - 'created_time' (string) The creation time,
* as a RFC date/time.
* - 'updated_time' (string) The last
* modification time, as a RFC date/time.
*/
public function __construct(Client $client, $id, $options = [])
{
$options = (object) $options;
$this->_client = $client;
$this->_id = null !== $id ? (string) $id : null;
$this->_parentId = property_exists($options, 'parent_id') ?
(string) $options->parent_id : null;
$this->_name = property_exists($options, 'name') ?
(string) $options->name : null;
$this->_description = property_exists($options, 'description') ?
(string) $options->description : null;
$this->_size = property_exists($options, 'size') ?
(int) $options->size : null;
$this->_source = property_exists($options, 'source') ?
(string) $options->source : null;
$this->_createdTime = property_exists($options, 'created_time') ?
strtotime($options->created_time) : null;
$this->_updatedTime = property_exists($options, 'updated_time') ?
strtotime($options->updated_time) : null;
}
/**
* Determines whether the OneDrive drive item referenced by this DriveItem
* instance is a folder.
*
* @return bool true if the OneDrive drive item referenced by this DriveItem
* instance is a folder, false otherwise.
*/
public function isFolder()
{
return false;
}
/**
* Fetches the properties of the OneDrive drive item referenced by this
* DriveItem instance. Some properties are cached for faster subsequent
* access.
*
* @return array The properties of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function fetchProperties()
{
$result = $this->_client->fetchProperties($this->_id);
$this->_parentId = '' != $result->parent_id ?
(string) $result->parent_id : null;
$this->_name = $result->name;
$this->_description = '' != $result->description ?
(string) $result->description : null;
$this->_size = (int) $result->size;
/** @todo Handle volatile existence (eg. present only for files). */
$this->_source = (string) $result->source;
$this->_createdTime = strtotime($result->created_time);
$this->_updatedTime = strtotime($result->updated_time);
return $result;
}
/**
* Gets the unique ID of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return string The unique ID of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function getId()
{
return $this->_id;
}
/**
* Gets the unique ID of the parent folder of the OneDrive drive item
* referenced by this DriveItem instance.
*
* @return string The unique ID of the OneDrive folder containing the drive
* item referenced by this DriveItem instance.
*/
public function getParentId()
{
if (null === $this->_parentId) {
$this->fetchProperties();
}
return $this->_parentId;
}
/**
* Gets the name of the OneDrive drive item referenced by this DriveItem
* instance.
*
* @return string The name of the OneDrive drive item referenced by this
* DriveItem instance.
*/
public function getName()
{
if (null === $this->_name) {
$this->fetchProperties();
}
return $this->_name;
}
/**
* Gets the description of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return string The description of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function getDescription()
{
if (null === $this->_description) {
$this->fetchProperties();
}
return $this->_description;
}
/**
* Gets the size of the OneDrive drive item referenced by this DriveItem
* instance.
*
* @return int The size of the OneDrive drive item referenced by this
* DriveItem instance.
*/
public function getSize()
{
if (null === $this->_size) {
$this->fetchProperties();
}
return $this->_size;
}
/**
* Gets the source link of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return string The source link of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function getSource()
{
if (null === $this->_source) {
$this->fetchProperties();
}
return $this->_source;
}
/**
* Gets the creation time of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return int The creation time of the drive item referenced by this
* DriveItem instance, in seconds since UNIX epoch.
*/
public function getCreatedTime()
{
if (null === $this->_createdTime) {
$this->fetchProperties();
}
return $this->_createdTime;
}
/**
* Gets the last modification time of the OneDrive drive item referenced by
* this DriveItem instance.
*
* @return int The last modification time of the drive item referenced by
* this DriveItem instance, in seconds since UNIX epoch.
*/
public function getUpdatedTime()
{
if (null === $this->_updatedTime) {
$this->fetchProperties();
}
return $this->_updatedTime;
}
/**
* Moves the OneDrive drive item referenced by this DriveItem instance into
* another OneDrive folder.
*
* @param null|string $destinationId The unique ID of the OneDrive folder
* into which to move the OneDrive drive
* item referenced by this DriveItem
* instance, or null to move it to the
* OneDrive root folder. Default: null.
*/
public function move($destinationId = null)
{
$this->_client->moveObject($this->_id, $destinationId);
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace Krizalys\Onedrive;
/**
* @class File
*
* A File instance is a DriveItem instance referencing a OneDrive file. It may
* have content but may not contain other OneDrive drive items.
*/
class File extends DriveItem
{
/**
* Constructor.
*
* @param Client $client The Client instance owning this DriveItem
* instance.
* @param null|string $id The unique ID of the OneDrive drive item
* referenced by this DriveItem instance.
* @param array|object $options An array/object with one or more of the
* following keys/properties:
* - 'parent_id' (string) The unique ID of the
* parent OneDrive folder of this drive item.
* - 'name' (string) The name of this drive
* item.
* - 'description' (string) The description of
* this drive item. May be empty.
* - 'size' (int) The size of this drive item,
* in bytes.
* - 'created_time' (string) The creation time,
* as a RFC date/time.
* - 'updated_time' (string) The last
* modification time, as a RFC date/time.
*/
public function __construct(Client $client, $id, $options = [])
{
parent::__construct($client, $id, $options);
}
/**
* Fetches the content of the OneDrive file referenced by this File
* instance.
*
* @param array $options Extra cURL options to apply.
*
* @return string The content of the OneDrive file referenced by this File
* instance.
*
* @todo Should somewhat return the content-type as well; this information
* is not disclosed by OneDrive.
*/
public function fetchContent($options = [])
{
return $this->_client->apiGet($this->_id . '/content', $options);
}
/**
* Copies the OneDrive file referenced by this File instance into another
* OneDrive folder.
*
* @param null|string $destinationId The unique ID of the OneDrive folder
* into which to copy the OneDrive file
* referenced by this File instance, or
* null to copy it in the OneDrive root
* folder. Default: null.
*/
public function copy($destinationId = null)
{
$this->_client->copyFile($this->_id, $destinationId);
}
}
+143
View File
@@ -0,0 +1,143 @@
<?php
namespace Krizalys\Onedrive;
/**
* @class Folder
*
* A Folder instance is a DriveItem instance referencing to a OneDrive folder.
* It may contain other OneDrive drive items but may not have content.
*/
class Folder extends DriveItem
{
/**
* Determines whether the OneDrive drive item referenced by this DriveItem
* instance is a folder.
*
* @return bool true if the OneDrive drive item referenced by this DriveItem
* instance is a folder, false otherwise.
*/
public function isFolder()
{
return true;
}
/**
* Constructor.
*
* @param Client $client The Client instance owning this DriveItem
* instance.
* @param null|string $id The unique ID of the OneDrive drive item
* referenced by this DriveItem instance, or
* null to reference the OneDrive root folder.
* Default: null.
* @param array|object $options Options to pass to the DriveItem
* constructor.
*/
public function __construct(Client $client, $id = null, $options = [])
{
parent::__construct($client, $id, $options);
}
/**
* Gets the drive items in the OneDrive folder referenced by this Folder
* instance.
*
* @return array The drive items in the OneDrive folder referenced by this
* Folder instance, as DriveItem instances.
*
* @deprecated Use Folder::fetchChildObjects() instead.
*/
public function fetchObjects()
{
/** @todo Log deprecation notice. */
return $this->fetchChildObjects();
}
/**
* Gets the child drive items in the OneDrive folder referenced by this
* Folder instance.
*
* @return array The drive items in the OneDrive folder referenced by this
* Folder instance, as DriveItem instances.
*/
public function fetchChildObjects()
{
return $this->_client->fetchObjects($this->_id);
}
/**
* Gets the descendant drive items under the OneDrive folder referenced by
* this Folder instance.
*
* @return array The files in the OneDrive folder referenced by this Folder
* instance, as DriveItem instances.
*/
public function fetchDescendantObjects()
{
$driveItems = [];
foreach ($this->fetchChildObjects() as $driveItem) {
if ($driveItem->isFolder()) {
$driveItems = array_merge(
$driveItem->fetchDescendantObjects(),
$driveItems
);
} else {
array_push($driveItems, $driveItem);
}
}
return $driveItems;
}
/**
* Creates a folder in the OneDrive folder referenced by this Folder
* instance.
*
* @param string $name The name of the OneDrive folder to be
* created.
* @param null|string $description The description of the OneDrive folder
* to be created, or null to create it
* without a description. Default: null.
*
* @return Folder The folder created, as a Folder instance.
*/
public function createFolder($name, $description = null)
{
return $this->_client->createFolder($name, $this->_id, $description);
}
/**
* Creates a file in the OneDrive folder referenced by this Folder instance.
*
* @param string $name The name of the OneDrive file to be
* created.
* @param string|resource $content The content of the OneDrive file to be
* created, as a string or handle to an
* already opened file. In the latter case,
* the responsibility to close the handle is
* is left to the calling function. Default:
* ''.
* @param array $options The options.
*
* @return File The file created, as a File instance.
*
* @throws \Exception Thrown on I/O errors.
*/
public function createFile($name, $content = '', array $options = [])
{
$client = $this->_client;
$options = array_merge([
'name_conflict_behavior' => $client->getNameConflictBehavior(),
'stream_back_end' => $client->getStreamBackEnd(),
], $options);
return $this->_client->createFile(
$name, $this->_id,
$content,
$options
);
}
}
@@ -0,0 +1,17 @@
<?php
namespace Krizalys\Onedrive;
class NameConflictBehavior
{
// Fail behavior: fail the operation if the drive item exists.
const FAIL = 1;
// Rename behavior: rename the drive item if it already exists. The drive
// item is renamed as "<original name> 1", incrementing the trailing number
// until an available file name is discovered.
const RENAME = 2;
// Replace behavior: replace the drive item if it already exists.
const REPLACE = 3;
}
@@ -0,0 +1,41 @@
<?php
namespace Krizalys\Onedrive;
class NameConflictBehaviorParameterizer
{
/**
* Parameterizes a given name conflict behavior.
*
* @param array $params The parameters.
* @param int $nameConflictBehavior The name conflict behavior.
*
* @return array
*
* @throws \Exception Thrown if the name conflict behavior given is not
* supported.
*/
public function parameterize(array $params, $nameConflictBehavior)
{
switch ($nameConflictBehavior) {
case NameConflictBehavior::FAIL:
$params['overwrite'] = 'false';
break;
case NameConflictBehavior::RENAME:
$params['overwrite'] = 'ChooseNewName';
break;
case NameConflictBehavior::REPLACE:
$params['overwrite'] = 'true';
break;
default:
throw new \Exception(
"Unsupported name conflict behavior: $nameConflictBehavior"
);
}
return $params;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Krizalys\Onedrive;
class StreamBackEnd
{
// Memory-backed stream.
const MEMORY = 1;
// Temporary file-backed stream. A temporary file is actually used if the
// stream contents exceeds 2 MiB.
const TEMP = 2;
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Krizalys\Onedrive;
class StreamOpener
{
private static $uris = [
StreamBackEnd::MEMORY => 'php://memory',
StreamBackEnd::TEMP => 'php://temp',
];
/**
* Opens a stream given a stream back end.
*
* @param int $streamBackEnd The stream back end.
*
* @return bool|resource The open stream.
*
* @throws \Exception Thrown if the stream back end given is not supported.
*/
public function open($streamBackEnd)
{
if (!array_key_exists($streamBackEnd, self::$uris)) {
throw new \Exception("Unsupported stream back end: $streamBackEnd");
}
$uri = self::$uris[$streamBackEnd];
return fopen($uri, 'rw+b');
}
}
+54 -12
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2 -1
View File
@@ -1,2 +1,3 @@
*
!.gitignore
!.gitignore
!chunks
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
File diff suppressed because one or more lines are too long
+1042 -1
View File
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -1 +1,4 @@
.header-panel{background-color:#3f51b5;height:250px;position:relative;z-index:3}.header-panel div{position:relative;height:100%}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.navbar{margin-bottom:0}.main{position:relative;top:-180px;z-index:4;overflow:hidden}.jumbotron{padding:0!important;transition:all .2s linear;background-color:rgba(255,255,255,0)!important}.jumbotron:hover{box-shadow:1px 4px 8px 0 rgba(0,0,0,.2),1px 3px 6px 0 rgba(0,0,0,.19)!important;transition:box-shadow .2s}.l-block{background:#fff;border-radius:2px;max-width:350px;width:100%;vertical-align:middle;position:relative;display:none;text-align:left}.palette-Teal.bg{background-color:#009688}.card_top{border-top-left-radius:2px;border-top-right-radius:2px}.card_botom{background-color:#f7f7f7;border-bottom-left-radius:2px;border-bottom-right-radius:2px}.card-top-row{padding:25px 30px;color:#fff}.top-color{background-color:#3f51b5;margin-right:0;margin-left:0;border-top-left-radius:2px;border-top-right-radius:2px}.bottom-width{margin-right:0;margin-left:0;padding:25px;padding-top:5px}.login-icon{font-size:40px}.login-text{margin-top:10px;margin-left:-5px}.form-group{padding-bottom:0}.noWave{display:initial}.link-group{margin-left:5px;line-height:25px}[alt=captcha]{width:150px}
.header-panel{background-color:#3f51b5;height:250px;position:relative;z-index:3}.header-panel div{position:relative;height:100%}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.navbar{margin-bottom:0}.main{position:relative;top:-180px;z-index:4;overflow:hidden}.jumbotron{padding:0!important;transition:all .2s linear;background-color:rgba(255,255,255,0)!important}.jumbotron:hover{box-shadow:1px 4px 8px 0 rgba(0,0,0,.2),1px 3px 6px 0 rgba(0,0,0,.19)!important;transition:box-shadow .2s}.l-block{background:#fff;border-radius:2px;max-width:350px;width:100%;vertical-align:middle;position:relative;display:none;text-align:left}.palette-Teal.bg{background-color:#009688}.card_top{border-top-left-radius:2px;border-top-right-radius:2px}.card_botom{background-color:#f7f7f7;border-bottom-left-radius:2px;border-bottom-right-radius:2px}.card-top-row{padding:25px 30px;color:#fff}.top-color{background-color:#3f51b5;margin-right:0;margin-left:0;border-top-left-radius:2px;border-top-right-radius:2px}.bottom-width{margin-right:0;margin-left:0;padding:25px;padding-top:5px}.login-icon{font-size:40px}.login-text{margin-top:10px;margin-left:-5px}.form-group{padding-bottom:0}.noWave{display:initial}.link-group{margin-left:5px;line-height:25px}[alt=captcha]{width:150px}
.form-group.is-focused label, .form-group.is-focused label.control-label {
color: #3F51B5;
}
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -1 +1,4 @@
.intro-page .navbar:not(.top-nav-collapse){background:0 0!important;box-shadow:none}.full-height,.full-height body,.full-height header,.full-height header .view{height:100%}.intro-page .navbar:not(.top-nav-collapse){background:0 0!important;box-shadow:none}.creative-lp .navbar{background-color:#6d7275}.grey-skin .navbar{background-color:#7d8488}.navbar{font-weight:300}.navbar.navbar-dark .navbar-brand{color:#fff}.navbar .navbar-brand{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start;overflow:visible}.full-height,.full-height body,.full-height header,.full-height header .view{height:100%}header .view{position:relative;width:100%;height:auto;background-repeat:no-repeat;background-position:center center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-o-background-size:cover}header .view{position:relative;width:100%;height:auto;background-repeat:no-repeat;background-position:center center;background-size:cover}.view{overflow:hidden;position:relative;cursor:default}.creative-lp .hm-gradient .full-bg-img{background:-moz-linear-gradient(45deg,rgba(153,122,226,.6),rgba(3,3,4,.6) 100%);background:-webkit-linear-gradient(45deg,rgba(153,122,226,.6),rgba(3,3,4,.6) 100%);background:linear-gradient(to 45deg,rgba(153,122,226,.6),rgba(3,3,4,.6) 100%)}.full-bg-img{height:100%;width:100%;position:absolute;overflow:hidden;top:0;left:0}.card .card-circle,.card-overlay,.flex-center,.section.team-section .avatar,.testimonial-carousel .testimonial .avatar{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:100%}.white-text{color:#FFF!important}.brand-name{font-size:4.5rem}.font-bold{font-weight:500}.mb-2{margin-bottom:1.5rem!important}.hr-streak hr.hr-light,.intro hr.hr-light,.intro-video hr.hr-light{margin-left:31%;margin-right:31%;background-color:#fff;height:1px}.hr-streak hr,.intro hr,.intro-video hr{margin:1.5rem 0 1.5rem 0}.hr-light{background-color:#fff;height:.5px}.subtext-header{line-height:1.5}.btn-light-green-2{background:#00bfa5}.btn-rounded{-webkit-border-radius:10em;-moz-border-radius:10em;-ms-border-radius:10em;-o-border-radius:10em;border-radius:10em!important}.btn{border-radius:2px;border:0;transition:.2s ease-out;color:#fff!important;margin:6px;white-space:normal!important;word-wrap:break-word;text-transform:uppercase}.btn{font-size:.8rem;padding:.85rem 2.13rem}.badge,.btn,.btn-floating,.card,.card-cascade-2 .card-block,.card-cascade.wider .card-block,.card-wrapper .back,.card-wrapper .front,.cascading-media .view,.chip,.dropdown-content,.dropdown-menu,.jumbotron,.list-group,.modal-dialog.cascading-modal .modal-c-tabs .nav-tabs,.modal-dialog.modal-notify .modal-header,.navbar,.navbar .nav-item .dropdown-menu.dropdown a:active,.navbar .nav-item .dropdown-menu.dropdown a:focus,.navbar .nav-item .dropdown-menu.dropdown a:hover,.pager li a,.pagination .active .page-link,.popover,.pricing-card .price .version,.social-list,.top-nav-collapse,.z-depth-1{box-shadow:0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12)}.btn,.btn-floating{transition:.5s}
.intro-page .navbar:not(.top-nav-collapse){background:0 0!important;box-shadow:none}.full-height,.full-height body,.full-height header,.full-height header .view{height:100%}.intro-page .navbar:not(.top-nav-collapse){background:0 0!important;box-shadow:none}.creative-lp .navbar{background-color:#6d7275}.grey-skin .navbar{background-color:#7d8488}.navbar{font-weight:300}.navbar.navbar-dark .navbar-brand{color:#fff}.navbar .navbar-brand{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start;overflow:visible}.full-height,.full-height body,.full-height header,.full-height header .view{height:100%}header .view{position:relative;width:100%;height:auto;background-repeat:no-repeat;background-position:center center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-o-background-size:cover}header .view{position:relative;width:100%;height:auto;background-repeat:no-repeat;background-position:center center;background-size:cover}.view{overflow:hidden;position:relative;cursor:default}.creative-lp .hm-gradient .full-bg-img{background:-moz-linear-gradient(45deg,rgba(153,122,226,.6),rgba(3,3,4,.6) 100%);background:-webkit-linear-gradient(45deg,rgba(153,122,226,.6),rgba(3,3,4,.6) 100%);background:linear-gradient(to 45deg,rgba(153,122,226,.6),rgba(3,3,4,.6) 100%)}.full-bg-img{height:100%;width:100%;position:absolute;overflow:hidden;top:0;left:0}.card .card-circle,.card-overlay,.flex-center,.section.team-section .avatar,.testimonial-carousel .testimonial .avatar{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:100%}.white-text{color:#FFF!important}.brand-name{font-size:4.5rem}.font-bold{font-weight:500}.mb-2{margin-bottom:1.5rem!important}.hr-streak hr.hr-light,.intro hr.hr-light,.intro-video hr.hr-light{margin-left:31%;margin-right:31%;background-color:#fff;height:1px}.hr-streak hr,.intro hr,.intro-video hr{margin:1.5rem 0 1.5rem 0}.hr-light{background-color:#fff;height:.5px}.subtext-header{line-height:1.5}.btn-light-green-2{background:#00bfa5}.btn-rounded{-webkit-border-radius:10em;-moz-border-radius:10em;-ms-border-radius:10em;-o-border-radius:10em;border-radius:10em!important}.btn{border-radius:2px;border:0;transition:.2s ease-out;color:#fff!important;margin:6px;white-space:normal!important;word-wrap:break-word;text-transform:uppercase}.btn{font-size:.8rem;padding:.85rem 2.13rem}.badge,.btn,.btn-floating,.card,.card-cascade-2 .card-block,.card-cascade.wider .card-block,.card-wrapper .back,.card-wrapper .front,.cascading-media .view,.chip,.dropdown-content,.dropdown-menu,.jumbotron,.list-group,.modal-dialog.cascading-modal .modal-c-tabs .nav-tabs,.modal-dialog.modal-notify .modal-header,.navbar,.navbar .nav-item .dropdown-menu.dropdown a:active,.navbar .nav-item .dropdown-menu.dropdown a:focus,.navbar .nav-item .dropdown-menu.dropdown a:hover,.pager li a,.pagination .active .page-link,.popover,.pricing-card .price .version,.social-list,.top-nav-collapse,.z-depth-1{box-shadow:0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12)}.btn,.btn-floating{transition:.5s}
.intro-video{
background-image: url(/static/img/output.jpg);
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
video{z-index:999}.file_name{white-space:nowrap}.iconset{overflow:auto;overflow-x:hidden;overflow-y:visible}.navbar .navbar-form .form-control,.navbar .navbar-form .form-group .form-control{padding:2px;margin:10px;color:#000}.form-control::-webkit-input-placeholder{color:red}.icon-black{color:#535353}.notWave{display:initial!important}.header-panel{background-color:#3f51b5;height:250px;position:relative;z-index:3}.header-panel div{position:relative;height:100%}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.navbar{margin-bottom:0}.main-h{position:relative;top:-135px;z-index:999}.jumbotron{padding:0!important;transition:all .2s linear;background-color:rgba(255,255,255,0)!important}.jumbotron:hover{box-shadow:1px 4px 8px 0 rgba(0,0,0,.2),1px 3px 6px 0 rgba(0,0,0,.19)!important;transition:box-shadow .2s}.btn-primary:not(.btn-raised):not(.btn-link):focus,.btn-primary:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):hover{background-color:#0ca99a;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.btn-info:not(.btn-raised):not(.btn-link):focus,.btn-info:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):hover{background-color:#3cc2ff;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.img-circle{width:50px;display:inline-block}.bottom-width{padding:17px;padding-top:0;min-height:480px;padding-bottom:0}.btn{outline:0!important}.img-circle{width:30px}.avatar{margin-top:8px;color:#9b9b9b;text-align:right;padding-right:10px}.noWave{display:initial!important}.item-icon{margin-bottom:7px}.modal-backdrop{z-index:100}input{padding:7px 10px;width:100%;height:50px;outline:0;transition:opacity box-shadow .5s;box-shadow:0 1px 2px 0 #787878;border:0}input:focus{transition:opacity box-shadow .5s;box-shadow:0 1px 14px}h1{color:#fffb5;margin-bottom:50px}.cent{text-align:center}.table-responsive{background-color:#fff}.table{margin-bottom:0}
video{z-index:999}.file_name{white-space:nowrap}.iconset{overflow:auto;overflow-x:hidden;overflow-y:visible}.navbar .navbar-form .form-control,.navbar .navbar-form .form-group .form-control{padding:2px;margin:10px;color:#000}.form-control::-webkit-input-placeholder{color:red}.icon-black{color:#535353}.notWave{display:initial!important}.header-panel{background-color:#3f51b5;height:250px;position:relative;z-index:3}.header-panel div{position:relative;height:100%}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.navbar{margin-bottom:0}.main-h{position:relative;top:-135px;z-index:999}.jumbotron{padding:0!important;transition:all .2s linear;background-color:rgba(255,255,255,0)!important}.jumbotron:hover{box-shadow:1px 4px 8px 0 rgba(0,0,0,.2),1px 3px 6px 0 rgba(0,0,0,.19)!important;transition:box-shadow .2s}.btn-primary:not(.btn-raised):not(.btn-link):focus,.btn-primary:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):hover{background-color:#0ca99a;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.btn-info:not(.btn-raised):not(.btn-link):focus,.btn-info:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):hover{background-color:#3cc2ff;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.img-circle{width:50px;display:inline-block}.bottom-width{padding:17px;padding-top:0;min-height:480px;padding-bottom:0}.btn{outline:0!important}.img-circle{width:30px}.avatar{margin-top:8px;color:#9b9b9b;text-align:right;padding-right:10px}.noWave{display:initial!important}.item-icon{margin-bottom:7px}.modal-backdrop{z-index:100}input{padding:7px 10px;width:100%;height:50px;outline:0;transition:opacity box-shadow .5s;box-shadow:0 1px 2px 0 #787878;border:0}input:focus{transition:opacity box-shadow .5s;box-shadow:0 1px 14px}h1{color:#dee0f2;margin-bottom:50px}.cent{text-align:center}.table-responsive{background-color:#fff}.table{margin-bottom:0}
+1050 -1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

+2
View File
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More