Compare commits

..
237 Commits
Author SHA1 Message Date
HFO4 7a530f5d58 Feat: Color option in dashboard 2019-03-17 21:49:35 +08:00
HFO4 88fac85ca5 Fix: markdown page js file import order 2019-03-17 17:06:01 +08:00
HFO4 9f658e86ec Fix: css name 2019-03-17 16:45:25 +08:00
HFO4 ebee2b09d9 Remove: extra description 2019-03-17 16:35:25 +08:00
HFO4 836cb1a9c7 Remove: Unused import 2019-03-17 16:32:59 +08:00
HFO4 3c7aedc195 Fix: default theme options 2019-03-17 14:48:11 +08:00
HFO4 a11f81966d Feat: Composer install script with fetching static file 2019-03-17 11:43:11 +08:00
HFO4 2c2e8b6052 Modify: Disable cmd install 2019-03-17 11:00:46 +08:00
HFO4 3b7e9298b5 Merge branch 'master' of https://github.com/HFO4/Cloudreve 2019-03-17 10:49:54 +08:00
HFO4 b341d3bc2c Modify: static file in .gitignore 2019-03-17 10:49:27 +08:00
HFO4 fd03047ed2 Delete: static file 2019-03-17 10:48:58 +08:00
HFO4 f15499d274 Fix: Installer missing static file 2019-03-17 09:38:18 +08:00
AaronLiuandGitHub 889b65163b Update README.md 2019-03-16 16:11:07 +08:00
AaronLiuandGitHub 1c05b9a0d3 Update README.md 2019-03-16 16:04:26 +08:00
AaronLiuandGitHub 00a2b452c4 Update README.md 2019-03-16 15:57:09 +08:00
AaronLiuandGitHub 721dad4abe Update README.md 2019-03-16 15:55:08 +08:00
HFO4 ebb95b31c2 Fix: Qiniu thumb error 2019-03-16 15:39:49 +08:00
HFO4 d854913cd2 Fix: Broken context menu in FireFox 2019-03-16 15:23:57 +08:00
HFO4 94c3dd7420 放宽邮箱验证规则 / 七牛缩略图尺寸调整 2019-03-16 14:55:09 +08:00
AaronLiuandGitHub 9d8646c6f6 Merge pull request #143 from HFO4/dev
Fix: broken getUpToken in qiniu policy
2019-03-16 14:22:12 +08:00
HFO4 560e455618 Fix: broken getUpToken in qiniu policy 2019-03-16 14:19:35 +08:00
AaronLiuandGitHub 51f8be8681 Merge pull request #142 from HFO4/dev
Fix: array to string error
2019-03-16 14:13:34 +08:00
AaronLiuandGitHub df8581e238 Delete Download.html 2019-03-16 14:12:51 +08:00
HFO4 bf2f5e621f Fix: array to string error 2019-03-16 14:11:46 +08:00
AaronLiuandGitHub a4439ba210 Merge pull request #141 from HFO4/dev
2.0.0-Alpha1
2019-03-16 14:04:14 +08:00
HFO4 822d9b4bba Add: gziped frontend resources 2019-03-16 14:02:18 +08:00
HFO4 18bcd93d97 Feat: Use official s3 SDK / support minio 2019-03-16 13:29:23 +08:00
HFO4 76ea49890d Feat: Login / Reg / Find pwd form 2019-03-16 10:41:12 +08:00
HFO4 74d0afb441 Feat: 自定义分片大小 2019-03-14 19:37:07 +08:00
HFO4 b2d5547199 Fix: 一些情况下离线下载失败后临时文件无法删除 2019-03-14 19:03:35 +08:00
HFO4 ef636bc2d1 Feat: My share 2019-03-14 11:29:09 +08:00
HFO4 3a4912083c Feat: User profile 2019-03-14 10:00:53 +08:00
HFO4 737665c62c Feat: Change prefer theme color 2019-03-13 19:38:23 +08:00
HFO4 df42ac0e96 Feat: User setting 2019-03-11 21:46:05 +08:00
HFO4 4880972d77 Feat: Referer check 2019-03-11 18:11:19 +08:00
HFO4 84e2ec0f19 Feat: Theme config 2019-03-11 16:21:17 +08:00
HFO4 53c79bfa65 Feat: Single file share 2019-03-11 15:04:35 +08:00
HFO4 41d1294ebf Feat: Folder share 2019-03-10 19:07:01 +08:00
HFO4 d84b00f35d Feat: markdown editor 2019-03-10 13:14:32 +08:00
HFO4 da4f152438 Feat: Video preview 2019-03-10 11:09:20 +08:00
HFO4 3d82929a3b Feat: Remember path 2019-03-09 19:27:25 +08:00
HFO4 2e2357e794 Feat: file type filter &Fix: cannot create torrent remote download in onedrive policy 2019-03-08 21:27:17 +08:00
HFO4 c94e17a4da Fix: img preview wont require height and width anymore 2019-03-07 17:20:18 +08:00
HFO4 c741d79677 Feat: Search file api 2019-03-07 15:29:22 +08:00
HFO4 e0b4b42ec6 Fix: file rename validate 2019-03-06 09:42:21 +08:00
HFO4 65dcedb3ca Fix: blocking while get uptoken 2019-03-05 19:28:55 +08:00
HFO4 422f9d8d6a Feat:initial test 2019-03-03 20:26:25 +08:00
AaronLiuandGitHub 1fc4956a3f Merge pull request #135 from HFO4/revert-104-patch-2
Revert "order 字段名添加分隔符"
2019-02-16 18:15:26 +08:00
AaronLiuandGitHub 564578aefb Revert "order 字段名添加分隔符" 2019-02-16 18:14:38 +08:00
AaronLiuandGitHub 12f98dd0f5 Update README.md 2019-02-16 10:05:29 +08:00
AaronLiuandGitHub c33b6d587f Merge pull request #125 from Yuanuo/patch-04
修复重命名文件时验证失败的问题
2019-01-21 10:18:41 +08:00
Yuanuo 277f882e8d 修复重命名文件时验证失败的问题
文件重命名时,文件后缀名之前的‘.’会导致验证失败,后台做校验时应该先去除后缀名部分。
2019-01-17 14:05:18 +08:00
AaronLiuandGitHub 68399a5877 fix: route checking 2018-12-22 12:23:52 +08:00
AaronLiuandGitHub 6b4f999bc4 fix: route checking 2018-12-22 12:19:56 +08:00
AaronLiuandGitHub 3514ff437c Update README.md 2018-12-19 19:08:36 +08:00
AaronLiuandGitHub 8c344b456d Merge pull request #104 from Instrye/patch-2
order 字段名添加分隔符
2018-11-02 13:34:16 +08:00
InstryeandGitHub ff4fa3f9b2 order 字段名添加分隔符
order方法中的字段名 rank 不会自动添加 `` ,导致在某些MySQL版本中会报错
2018-11-02 11:10:25 +08:00
HFO4 d9fa51a615 fix: move file while forget to change the parent folder ID 2018-10-20 19:21:57 +08:00
HFO4 6dd49fce09 Fix #100 2018-10-20 18:32:00 +08:00
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
HFO4 f46684495c 删除googleauthenticator 2018-02-04 14:55:03 +08:00
HFO4 884949dad4 语法错误 2018-02-04 14:40:51 +08:00
HFO4 3f30ec3c7e 取消dev要求 2018-02-04 14:33:02 +08:00
HFO4 93abecd2cb 更新README 2018-02-04 14:23:53 +08:00
AaronLiuandGitHub 9395228a66 Update composer.json 2018-02-04 14:08:42 +08:00
AaronLiuandGitHub 79c78a90f4 Update composer.json 2018-02-04 14:05:47 +08:00
AaronLiuandGitHub dcd392e70f Update composer.json 2018-02-04 13:56:50 +08:00
AaronLiuandGitHub 935f74fa52 Update composer.json 2018-02-04 13:37:10 +08:00
AaronLiuandGitHub 64f6cff7ce Update composer.json 2018-02-04 13:26:52 +08:00
AaronLiuandGitHub 76c5d9ad8a Update composer.json 2018-02-04 13:24:43 +08:00
AaronLiuandGitHub 3c90b83d11 Update composer.json 2018-02-04 13:24:13 +08:00
AaronLiuandGitHub 224c2a66c6 Update composer.json 2018-02-04 13:06:38 +08:00
AaronLiuandGitHub 7d6e026280 Update composer.json 2018-02-04 13:05:15 +08:00
AaronLiuandGitHub e9201d85f7 Update README.md 2018-02-04 13:02:21 +08:00
HFO4 3b9040028d Init 2018-02-04 12:58:07 +08:00
AaronLiuandGitHub a7691a1d1d Initial commit 2018-02-04 12:56:38 +08:00
1046 changed files with 151463 additions and 52376 deletions
-1
View File
@@ -1 +0,0 @@
custom: ["https://cloudreve.org/buy.php"]
-38
View File
@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
-61
View File
@@ -1,61 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 360
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 30
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- pinned
- security
- "[Status] Maybe Later"
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: true
# Label to use when marking as stale
staleLabel: wontfix
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
# closeComment: >
# Your comment here.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed
-55
View File
@@ -1,55 +0,0 @@
name: Build
on: workflow_dispatch
jobs:
build:
name: Build
runs-on: ubuntu-18.04
steps:
- name: Set up Go 1.17
uses: actions/setup-go@v2
with:
go-version: "1.17"
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
clean: false
submodules: "recursive"
- run: |
git fetch --prune --unshallow --tags
- name: Get dependencies and build
run: |
sudo apt-get update
sudo apt-get -y install gcc-mingw-w64-x86-64
sudo apt-get -y install gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
sudo apt-get -y install gcc-aarch64-linux-gnu libc6-dev-arm64-cross
chmod +x ./build.sh
./build.sh -r b
- name: Upload binary files (windows_amd64)
uses: actions/upload-artifact@v2
with:
name: cloudreve_windows_amd64
path: release/cloudreve*windows_amd64.*
- name: Upload binary files (linux_amd64)
uses: actions/upload-artifact@v2
with:
name: cloudreve_linux_amd64
path: release/cloudreve*linux_amd64.*
- name: Upload binary files (linux_arm)
uses: actions/upload-artifact@v2
with:
name: cloudreve_linux_arm
path: release/cloudreve*linux_arm.*
- name: Upload binary files (linux_arm64)
uses: actions/upload-artifact@v2
with:
name: cloudreve_linux_arm64
path: release/cloudreve*linux_arm64.*
-57
View File
@@ -1,57 +0,0 @@
name: Build and push docker image
on:
push:
tags:
- 3.* # triggered on every push with tag 3.*
workflow_dispatch: # or just on button clicked
jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- run: git fetch --prune --unshallow
- name: Setup Environments
id: envs
run: |
CLOUDREVE_LATEST_TAG=$(git describe --tags --abbrev=0)
DOCKER_IMAGE="cloudreve/cloudreve"
echo "RELEASE_VERSION=${GITHUB_REF#refs}"
TAGS="${DOCKER_IMAGE}:latest,${DOCKER_IMAGE}:${CLOUDREVE_LATEST_TAG}"
echo "CLOUDREVE_LATEST_TAG:${CLOUDREVE_LATEST_TAG}"
echo ::set-output name=tags::${TAGS}
- name: Setup QEMU Emulator
uses: docker/setup-qemu-action@master
with:
platforms: all
- name: Setup Docker Buildx Command
id: buildx
uses: docker/setup-buildx-action@master
- name: Login to Dockerhub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build Docker Image and Push
id: docker_build
uses: docker/build-push-action@v2
with:
push: true
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: ${{ steps.envs.outputs.tags }}
- name: Update Docker Hub Description
uses: peter-evans/dockerhub-description@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: cloudreve/cloudreve
short-description: ${{ github.event.repository.description }}
- name: Image Digest
run: echo ${{ steps.docker_build.outputs.digest }}
-35
View File
@@ -1,35 +0,0 @@
name: Test
on:
pull_request:
branches:
- master
push:
branches: [master]
jobs:
test:
name: Test
runs-on: ubuntu-18.04
steps:
- name: Set up Go 1.17
uses: actions/setup-go@v2
with:
go-version: "1.17"
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
submodules: "recursive"
- name: Build static files
run: |
mkdir assets/build
touch assets/build/test.html
- name: Test
run: go test -coverprofile=coverage.txt -covermode=atomic ./...
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v2
+5 -31
View File
@@ -1,31 +1,5 @@
# Binaries for programs and plugins
cloudreve
*.exe
*.exe~
*.dll
*.so
*.dylib
*.db
*.bin
/release/
assets.zip
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Development enviroment
.idea/*
uploads/*
temp
# Version control
version.lock
# Config file
*.ini
conf/conf.ini
/statik/
.vscode/
*.map
composer.lock
*.chunk.js
*.chunk.css
runtime~*.js
-3
View File
@@ -1,3 +0,0 @@
[submodule "assets"]
path = assets
url = https://github.com/cloudreve/frontend.git
+9
View File
@@ -0,0 +1,9 @@
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
</IfModule>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+7 -29
View File
@@ -1,29 +1,7 @@
language: go
go:
- 1.17.x
node_js: "12.16.3"
git:
depth: 1
before_install:
- mkdir assets/build
- touch assets/build/test.html
script:
- go test -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
before_deploy:
- sudo apt-get update
- sudo apt-get -y install gcc-mingw-w64-x86-64
- sudo apt-get -y install gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
- sudo apt-get -y install gcc-aarch64-linux-gnu libc6-dev-arm64-cross
- chmod +x ./build.sh
- ./build.sh -r b
deploy:
provider: releases
api_key: $GITHUB_TOKEN
file_glob: true
file: release/*
draft: true
skip_cleanup: true
on:
tags: true
language: php
php:
- 7.1
before_script:
- composer install
- chmod +x ./deploy.sh
script: ./deploy.sh
+164
View File
@@ -0,0 +1,164 @@
<?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);
self::prepareStatic($event);
$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 prepareStatic(Event $event){
$ioContext = $event->getIO();
$ioContext->write("Fetching static files from cloudreve.org...");
if(!is_writable("runtime") || !is_writable("static")){
$ioContext->writeError("[Error] 'runtime' or 'static' directory is not writable. To retry, run 'composer install'");
exit();
}
if(file_exists("runtime/static.zip")){
$ioContext->writeError("[Error] 'runtime/static.zip' already exist. To retry, run 'composer install'");
exit();
}
if(file_exists("runtime/static")){
$ioContext->writeError("[Error] directory 'runtime/static' already exist. To retry, run 'composer install'");
exit();
}
try{
file_put_contents("runtime/static.zip",file_get_contents("https://cloudreve.org/download.php?newestStatic"));
}catch (Exception $e) {
$ioContext->writeError("[Error] Failed to download static file. To retry, run 'composer install'");
exit();
}
$ioContext->write("Unzip static files...");
$zip = new \ZipArchive;
$res = $zip->open('runtime/static.zip');
if ($res === TRUE) {
$zip->extractTo('runtime/static/');
$zip->close();
}else {
$ioContext->writeError("[Error] Failed to open runtime/static.zip. To retry, run 'composer install'");
exit();
}
self::recurse_copy("runtime/static/build/static", "static");
}
public static function recurse_copy($src,$dst) {
$dir = opendir($src);
@mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
self::recurse_copy($src . '/' . $file,$dst . '/' . $file);
}else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
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));
}
}
?>
+53
View File
@@ -0,0 +1,53 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '{hostname}',
// 数据库名
'database' => '{database}',
// 用户名
'username' => '{username}',
// 密码
'password' => '{password}',
// 端口
'hostport' => '{hostport}',
// 连接dsn
'dsn' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'sd_',
// 数据库调试模式
'debug' => true,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 数据集返回类型
'resultset_type' => 'array',
// 自动写入时间戳字段
'auto_timestamp' => false,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 是否需要进行SQL性能分析
'sql_explain' => false,
];
File diff suppressed because one or more lines are too long
-42
View File
@@ -1,42 +0,0 @@
FROM golang:1.17-alpine as cloudreve_builder
# install dependencies and build tools
RUN apk update && apk add --no-cache wget curl git yarn build-base gcc abuild binutils binutils-doc gcc-doc zip
WORKDIR /cloudreve_builder
RUN git clone --recurse-submodules https://github.com/cloudreve/Cloudreve.git
# build frontend
WORKDIR /cloudreve_builder/Cloudreve/assets
RUN yarn install --network-timeout 1000000
RUN yarn run build && find . -name "*.map" -type f -delete
# build backend
WORKDIR /cloudreve_builder/Cloudreve
RUN zip -r - assets/build >assets.zip
RUN tag_name=$(git describe --tags) \
&& export COMMIT_SHA=$(git rev-parse --short HEAD) \
&& go build -a -o cloudreve -ldflags " -X 'github.com/HFO4/cloudreve/pkg/conf.BackendVersion=$tag_name' -X 'github.com/HFO4/cloudreve/pkg/conf.LastCommit=$COMMIT_SHA'"
# build final image
FROM alpine:latest
WORKDIR /cloudreve
RUN apk update && apk add --no-cache tzdata
# we using the `Asia/Shanghai` timezone by default, you can do modification at your will
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
COPY --from=cloudreve_builder /cloudreve_builder/Cloudreve/cloudreve ./
# prepare permissions and aria2 dir
RUN chmod +x ./cloudreve && mkdir -p /data/aria2 && chmod -R 766 /data/aria2
EXPOSE 5212
VOLUME ["/cloudreve/uploads", "/cloudreve/avatar", "/data"]
ENTRYPOINT ["./cloudreve"]
+1 -1
View File
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+84 -104
View File
@@ -1,126 +1,106 @@
<h1 align="center">
<br>
<a href="https://cloudreve.org/" alt="logo" ><img src="https://raw.githubusercontent.com/cloudreve/frontend/master/public/static/img/logo192.png" width="150"/></a>
<br>
Cloudreve
<br>
</h1>
![logo_white.png](https://raw.githubusercontent.com/HFO4/Cloudreve/master/static/img/logo_white.png)
<h4 align="center">支持多家云存储驱动的公有云文件系统.</h4>
Cloudreve - Make the cloud easy for everyone
=========================
[![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)
<p align="center">
<a href="https://github.com/cloudreve/Cloudreve/actions/workflows/test.yml">
<img src="https://img.shields.io/github/workflow/status/cloudreve/Cloudreve/Test?style=flat-square"
alt="GitHub Test Workflow">
</a>
<a href="https://codecov.io/gh/cloudreve/Cloudreve"><img src="https://img.shields.io/codecov/c/github/cloudreve/Cloudreve?style=flat-square"></a>
<a href="https://goreportcard.com/report/github.com/cloudreve/Cloudreve">
<img src="https://goreportcard.com/badge/github.com/cloudreve/Cloudreve?style=flat-square">
</a>
<a href="https://github.com/cloudreve/Cloudreve/releases">
<img src="https://img.shields.io/github/v/release/cloudreve/Cloudreve?include_prereleases&style=flat-square" />
</a>
<a href="https://hub.docker.com/r/cloudreve/cloudreve">
<img src="https://img.shields.io/docker/image-size/cloudreve/cloudreve?style=flat-square"/>
</a>
</p>
[主页](https://cloudreve.org) | [论坛](https://forum.cloudreve.org) | [演示站](https://drive.aoaoao.me) | [QQ群](https://jq.qq.com/?_wv=1027&k=5TX6sJY)
<p align="center">
<a href="https://demo.cloudreve.org">演示站</a> •
<a href="https://forum.cloudreve.org/">讨论社区</a> •
<a href="https://docs.cloudreve.org/">文档</a> •
<a href="https://github.com/cloudreve/Cloudreve/releases">下载</a> •
<a href="https://t.me/cloudreve_official">Telegram 群组</a> •
<a href="#scroll-许可证">许可证</a>
</p>
使用ThinkPHP + React + Redux + Material-UI构建的网盘系统,能够助您以较低成本快速搭建起公私兼备的网盘。
![Screenshot](https://raw.githubusercontent.com/cloudreve/docs/master/images/homepage.png)
![HomePage](https://i.loli.net/2019/03/16/5c8caf825282a.jpg)
## :sparkles: 特性
目前已经实现的特性:
* :cloud: 支持本机、从机、七牛、阿里云 OSS、腾讯云 COS、又拍云、OneDrive (包括世纪互联版) 作为存储
* :outbox_tray: 上传/下载 支持客户端直传,支持下载限速
* 💾 可对接 Aria2 离线下载,可使用多个从机机点分担下载任务
* 📚 在线 压缩/解压缩、多文件打包下载
* 💻 覆盖全部存储策略的 WebDAV 协议支持
* :zap: 拖拽上传、目录上传、流式上传处理
* :card_file_box: 文件拖拽管理
* :family_woman_girl_boy: 多用户、用户组
* :link: 创建文件、目录的分享链接,可设定自动过期
* :eye_speech_bubble: 视频、图像、音频、文本、Office 文档在线预览
* :art: 自定义配色、黑暗模式、PWA 应用、全站单页应用
* :rocket: All-In-One 打包,开箱即用
* 🌈 ... ...
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3、Onedrive、自建远程服务器,当然,还有本地存储
* 可限制单文件最大大小、MIMEType、文件后缀、用户可用容量
* 自定义主题配色
* 基于Aria2的离线下载
* 图片、音频、视频、文本、Markdown、Ofiice文档 在线预览
* 移动端全站响应式布局
* 文件、目录分享系统,可创建私有分享或公开分享链接
* 用户个人主页,可查看用户所有分享
* 多用户系统、用户组支持
* 初步完善的后台,方便管理
* 拖拽上传、分片上传、断点续传、下载限速(*实验性功能)
* 多上传策略,可为不同用户组分配不同策略
* 用户组基础权限设置、二步验证
* WebDAV协议支持
## :hammer_and_wrench: 部署
下载适用于您目标机器操作系统、CPU架构的主程序,直接运行即可。
安装需求
------------
* LNMP/AMP With PHP5.6+
* curl、fileinfo、gd扩展
* Composer
```shell
# 解压程序包
tar -zxvf cloudreve_VERSION_OS_ARCH.tar.gz
简要安装说明
------------
# 赋予执行权限
chmod +x ./cloudreve
# 启动 Cloudreve
./cloudreve
#### 1.使用Composer安装主程序
```
#安装开发版
$ composer create-project hfo4/cloudreve:dev-master
```
以上为最简单的部署示例,您可以参考 [文档 - 起步](https://docs.cloudreve.org/) 进行更为完善的部署。
## :gear: 构建
自行构建前需要拥有 `Go >= 1.17``yarn`等必要依赖。
#### 克隆代码
```shell
git clone --recurse-submodules https://github.com/cloudreve/Cloudreve.git
```
#等待安装依赖库后,会自动执行安装脚本,按照提示输入数据库账户信息
___ _ _
/ __\ | ___ _ _ __| |_ __ _____ _____
/ / | |/ _ \| | | |/ _` | '__/ _ \ \ / / _ \
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver XX
================================================
#按提示输入信息
......
```
#### 构建静态资源
```shell
# 进入前端子模块
cd assets
# 安装依赖
yarn install
# 开始构建
yarn run build
```
#出现如下提示表示安装完成
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` 目录也需要有写入权限
```shell
# 获得当前版本号、Commit
export COMMIT_SHA=$(git rev-parse --short HEAD)
export VERSION=$(git describe --tags)
# 开始编译
go build -a -o cloudreve -ldflags "-s -w -X 'github.com/cloudreve/Cloudreve/v3/pkg/conf.BackendVersion=$VERSION' -X 'github.com/cloudreve/Cloudreve/v3/pkg/conf.LastCommit=$COMMIT_SHA'"
#### 3.URL重写
对于Apache服务器,项目目录下的`.htaccess`已经配置好重写规则,如有需求酌情修改.
对于Nginx服务器,以下是一个可供参考的配置:
```
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
break;
}
}
```
你也可以使用项目根目录下的`build.sh`快速开始构建:
#### 4.完成
后台地址:`http://您的域名/Admin` 初始用户名:`admin@cloudreve.org` 初始密码:`admin`
#### 后续操作
以下操作不是必须的,但仍推荐你完成这些操作:
* 修改初始账户密码
* 到 设置-基础设置 中更改站点URL,如果不更改,程序无法正常接受回调请求
* 添加Crontab定时任务 :你的域名/Cron
* 如果你打算使用本地上传策略并且不准备开启外链功能,请将·public/uploads·目录设置为禁止外部访问
* 如需启用二步验证功能,请依次执行`composer require phpgangsta/googleauthenticator:dev-master` `composer require endroid/qr-code`安装二步验证支持库
* 给本项目一个Star~
```shell
./build.sh [-a] [-c] [-b] [-r]
a - 构建静态资源
c - 编译二进制文件
b - 构建前端 + 编译二进制文件
r - 交叉编译,构建用于release的版本
```
文档
------------
* [完整安装说明](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)
## :alembic: 技术栈
* [Go ](https://golang.org/) + [Gin](https://github.com/gin-gonic/gin)
* [React](https://github.com/facebook/react) + [Redux](https://github.com/reduxjs/redux) + [Material-UI](https://github.com/mui-org/material-ui)
## :scroll: 许可证
GPL V3
---
> GitHub [@HFO4](https://github.com/HFO4) &nbsp;&middot;&nbsp;
> Twitter [@abslant00](https://twitter.com/abslant00)
许可证
------------
GPLV3
+1
View File
@@ -0,0 +1 @@
database.php
+1
View File
@@ -0,0 +1 @@
deny from all
+14
View File
@@ -0,0 +1,14 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
return [
'app\index\command\Task',
];
+41
View File
@@ -0,0 +1,41 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 流年 <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 应用公共文件
function array_column_fix($array,$key){
if(count($array) == count($array,1)){
return array(0 =>$array[$key]);
}else{
return array_column($array,$key);
}
}
function getAllowedExt($ext){
$returnValue = "";
foreach (json_decode($ext,true) as $key => $value) {
$returnValue .= $value["ext"].",";
}
return rtrim($returnValue, ",");
}
function getDirName($name){
$explode = explode("/", $name);
return end($explode);
}
function getSize($bit,$array=false){
$type = array('Bytes','KB','MB','GB','TB');
$box = array('1','1024','1048576','1073741824','TB');
for($i = 0; $bit >= 1024; $i++) {
$bit/=1024;
}
if($array){
return [(floor($bit*100)/100),$box[$i]];
}
return (floor($bit*100)/100).$type[$i];
}
+276
View File
@@ -0,0 +1,276 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
return [
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
//密码加盐
'salt' => 'sdshare',
// 应用命名空间
'app_namespace' => 'app',
// 应用调试模式
'app_debug' => true,
// 应用Trace
'app_trace' => false,
// 应用模式状态
'app_status' => '',
// 是否支持多模块
'app_multi_module' => true,
// 入口自动绑定模块
'auto_bind_module' => false,
// 注册的根命名空间
'root_namespace' => [],
// 扩展函数文件
'extra_file_list' => [THINK_PATH . 'helper' . EXT],
// 默认输出类型
'default_return_type' => 'html',
// 默认AJAX 数据返回格式,可选json xml ...
'default_ajax_return' => 'json',
// 默认JSONP格式返回的处理方法
'default_jsonp_handler' => 'jsonpReturn',
// 默认JSONP处理方法
'var_jsonp_handler' => 'callback',
// 默认时区
'default_timezone' => 'PRC',
// 是否开启多语言
'lang_switch_on' => false,
// 默认全局过滤方法 用逗号分隔多个
'default_filter' => '',
// 默认语言
'default_lang' => 'zh-cn',
// 应用类库后缀
'class_suffix' => false,
// 控制器类后缀
'controller_suffix' => false,
// +----------------------------------------------------------------------
// | 模块设置
// +----------------------------------------------------------------------
// 默认模块名
'default_module' => 'index',
// 禁止访问模块
'deny_module_list' => ['common'],
// 默认控制器名
'default_controller' => 'Index',
// 默认操作名
'default_action' => 'index',
// 默认验证器
'default_validate' => '',
// 默认的空控制器名
'empty_controller' => 'Error',
// 操作方法后缀
'action_suffix' => '',
// 自动搜索控制器
'controller_auto_search' => false,
// +----------------------------------------------------------------------
// | URL设置
// +----------------------------------------------------------------------
// PATHINFO变量名 用于兼容模式
'var_pathinfo' => 's',
'url_convert' => false,
// 兼容PATH_INFO获取
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
// pathinfo分隔符
'pathinfo_depr' => '/',
// URL伪静态后缀
'url_html_suffix' => '',
// URL普通方式参数 用于自动生成
'url_common_param' => false,
// URL参数方式 0 按名称成对解析 1 按顺序解析
'url_param_type' => 0,
// 是否开启路由
'url_route_on' => true,
// 路由使用完整匹配
'route_complete_match' => false,
// 路由配置文件(支持配置多个)
'route_config_file' => ['route'],
// 是否强制使用路由
'url_route_must' => false,
// 域名部署
'url_domain_deploy' => false,
// 域名根,如thinkphp.cn
'url_domain_root' => '',
// 是否自动转换URL中的控制器和操作名
'url_convert' => false,
// 默认的访问控制器层
'url_controller_layer' => 'controller',
// 表单请求类型伪装变量
'var_method' => '_method',
// 表单ajax伪装变量
'var_ajax' => '_ajax',
// 表单pjax伪装变量
'var_pjax' => '_pjax',
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
'request_cache' => false,
// 请求缓存有效期
'request_cache_expire' => null,
// 全局请求缓存排除规则
'request_cache_except' => [],
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
'template' => [
// 模板引擎类型 支持 php think 支持扩展
'type' => 'Think',
// 模板路径
'view_path' => '',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DS,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'taglib_end' => '}',
],
// 视图输出字符串内容替换
'view_replace_str' => [],
// 默认跳转页面对应的模板文件
'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
'dispatch_error_tmpl' => "application/index/view/error.html",
// +----------------------------------------------------------------------
// | 异常及错误设置
// +----------------------------------------------------------------------
// 异常页面的模板文件
'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '很抱歉,出现错误 :(',
// 显示错误信息
'show_error_msg' => false,
// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => '',
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
'log' => [
// 日志记录方式,内置 file socket 支持扩展
'type' => 'File',
// 日志保存目录
'path' => LOG_PATH,
// 日志记录级别
'level' => ["error"],
],
// +----------------------------------------------------------------------
// | Trace设置 开启 app_trace 后 有效
// +----------------------------------------------------------------------
'trace' => [
// 内置Html Console 支持扩展
'type' => 'Html',
],
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
'cache' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => CACHE_PATH,
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
],
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
'session' => [
'id' => '',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// SESSION 前缀
'prefix' => 'sd',
// 驱动方式 支持redis memcache memcached
'type' => '',
// 是否自动开启 SESSION
'auto_start' => true,
],
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
'cookie' => [
// cookie 名称前缀
'prefix' => '',
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => '',
// 是否使用 setcookie
'setcookie' => true,
],
//分页配置
'paginate' => [
'type' => 'bootstrap',
'var_page' => 'page',
'list_rows' => 15,
],
//分片上传大小(只对本地策略有效)
'upload'=>[
'chunk_size' => 10*1024*1024,
],
// +----------------------------------------------------------------------
// | 验证码设置
// +----------------------------------------------------------------------
'captcha'=>[
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
// 验证码字符集合
'expire' => 1800,
// 验证码过期时间(s
'useZh' => false,
// 使用背景图片
'fontSize' => 25,
// 验证码字体大小(px)
'useCurve' => true,
// 是否画混淆曲线
'useNoise' => true,
// 是否添加杂点
'imageH' => 0,
// 验证码图片高度
'imageW' => 0,
// 验证码图片宽度
'length' => 4,
// 验证码位数
'fontttf' => '',
// 验证码字体,不设置随机获取
'bg' => [243, 251, 254],
// 背景颜色
'reset' => true],
];
+53
View File
@@ -0,0 +1,53 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => 'tp',
// 用户名
'username' => 'root',
// 密码
'password' => 'root',
// 端口
'hostport' => '3306',
// 连接dsn
'dsn' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'sd_',
// 数据库调试模式
'debug' => true,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 数据集返回类型
'resultset_type' => 'array',
// 自动写入时间戳字段
'auto_timestamp' => false,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 是否需要进行SQL性能分析
'sql_explain' => false,
];
+14
View File
@@ -0,0 +1,14 @@
<?php
function classLoader($class)
{
$path = str_replace('\\', DIRECTORY_SEPARATOR, $class);
$file = __DIR__ . '/src/' . $path . '.php';
if (file_exists($file)) {
require_once $file;
}
}
spl_autoload_register('classLoader');
require_once __DIR__ . '/src/Qiniu/functions.php';
+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"]);
}
}
}
}
?>
+25
View File
@@ -0,0 +1,25 @@
<?php
function countSize($bit,$array=false){
$type = array('Bytes','KB','MB','GB','TB');
$box = array('1','1024','1048576','1073741824','TB');
for($i = 0; $bit >= 1024; $i++) {
$bit/=1024;
}
if($array){
return [(floor($bit*100)/100),$box[$i]];
}
return (floor($bit*100)/100).$type[$i];
}
function isPreview($fileName){
$allowedSuffix=["jpg","jpeg","gif","bmp","png","svg","mp4","mp3","ogg"];
$suffix = explode(".",$fileName);
if(in_array(end($suffix),$allowedSuffix)){
return "yes";
}
return "no";
}
function getDay($sec){
return floor($sec/86400);
}
?>
+566
View File
@@ -0,0 +1,566 @@
<?php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;
use think\Cookie;
use think\Db;
use \app\index\model\Option;
use \app\index\model\AdminHandler;
use \app\index\model\FileManage;
class Admin extends Controller{
public $userObj;
public $siteOptions;
public $adminObj;
public function _initialize(){
$this->siteOptions = Option::getValues(["basic","admin"]);
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if(!$this->userObj->loginStatus){
$this->redirect(url('/Login','',''));
exit();
}
if($this->userObj->groupData["id"] != 1){
$this->error('你无权访问此页面',403,$this->siteOptions);
}
$this->adminObj = new AdminHandler($this->siteOptions);
}
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,
]);
}
public function Config(){
$configType=input("?param.type") ? input("param.type") : "common";
$configFile = $this->adminObj->getConfigFile($configType);
return view('config_file', [
'options' => $this->siteOptions,
'type' => $configType,
'content' => $configFile[0],
'path' => $configFile[1],
]);
}
public function SaveConfigFile(){
return $this->adminObj->saveConfigFile(input('post.'));
}
public function SettingReg(){
return view('reg_setting', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["login","register"]),
'groups' => $this->adminObj->getAvaliableGroup(),
]);
}
public function Color(){
$options = Option::getValues(["basic"]);
return view('color', [
'options' => $this->siteOptions,
'optionsForSet' => $options,
'colors'=> \json_decode($options["themes"],true)
]);
}
public function Theme(){
$fileName=input("?param.name") ? input("param.name") : "error";
$dir = ROOT_PATH."application/index/view/";
if(!function_exists("scandir")){
return "scandir被禁用";
}
$fileList=[];
$fileList=$fileList+scandir($dir);
$pathList=["/"=>$fileList];
foreach (["admin","explore","file","home","index","member","profile","share"] as $key => $value) {
$childPath = scandir($dir.$value."/");
$fileList=array_merge($fileList,$childPath);
$pathList = array_merge($pathList,[$value => $childPath]);
}
foreach ($fileList as $key => $value) {
if(substr_compare($value, ".html", -strlen(".html")) != 0){
unset($fileList[$key]);
}
}
foreach($pathList as $key=>$val){
if(in_array($fileName.".html",$val)){
$parentPath = $key;
break;
}
}
$fileContent = file_get_contents($dir.rtrim($parentPath,"/")."/".$fileName.".html");
return view('theme', [
'options' => $this->siteOptions,
'list' => $fileList,
'content' => $fileContent,
'path' => $parentPath,
'name' => $fileName,
]);
}
public function SaveThemeFile(){
return $this->adminObj->saveThemeFile(input('post.'));
}
public function SaveColorSetting(){
return $this->adminObj->saveColorSetting(input('post.'));
}
public function SettingMail(){
return view('mail_setting', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["mail_template","mail"]),
]);
}
public function SettingPay(){
return view('pay_setting', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["payment"]),
]);
}
public function SettingOther(){
return view('other_setting', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["file_edit","share","avatar","admin","storage_policy","download"]),
]);
}
public function Files(){
$this->adminObj->listFile();
return view('file_list', [
'options' => $this->siteOptions,
'groups' => $this->adminObj->getAvaliableGroup(),
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
'policy' => $this->adminObj->getAvaliablePolicy(),
]);
}
Public function Users(){
$this->adminObj->listUser();
$group = $this->adminObj->getAvaliableGroup();
return view('user_list', [
'options' => $this->siteOptions,
'group' => $group,
'groups' => $group,
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
'policy' => $this->adminObj->getAvaliablePolicy(),
]);
}
public function Shares(){
$this->adminObj->listShare();
return view('share_list', [
'options' => $this->siteOptions,
'groups' => $this->adminObj->getAvaliableGroup(),
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function PolicyList(){
$this->adminObj->listPolicy();
return view('policy_list', [
'options' => $this->siteOptions,
'groups' => $this->adminObj->getAvaliableGroup(),
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function GroupList(){
$this->adminObj->listGroup();
return view('group_list', [
'options' => $this->siteOptions,
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function OrderList(){
$this->adminObj->listOrder();
return view('order_list', [
'options' => $this->siteOptions,
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function SaveBasicSetting(){
return $this->adminObj->saveBasicSetting(input('post.'));
}
public function SaveRegSetting(){
return $this->adminObj->saveRegSetting(input('post.'));
}
public function SaveMailSetting(){
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.'));
}
public function SaveMailTemplate(){
return $this->adminObj->saveMailTemplate(input('post.'));
}
public function GetFileInfo(){
return $this->adminObj->getFileInfo(input('post.id'));
}
public function GetUserInfo(){
return $this->adminObj->getUserInfo(input('post.id'));
}
public function savePolicy(){
return $this->adminObj->addPolicy(input('post.'));
}
public function SaveEditPolicy(){
return $this->adminObj->editPolicy(input('post.'));
}
public function SaveGroup(){
return $this->adminObj->saveGroup(input('post.'));
}
public function AddPack(){
return $this->adminObj->addPack(input('post.'));
}
public function AddGroupPurchase(){
return $this->adminObj->addGroupPurchase(input('post.'));
}
public function SaveCron(){
$this->adminObj->saveCron(input('post.'));
$this->redirect("/Admin/Cron",302);
}
public function SaveUser(){
return $this->adminObj->saveUser(input('post.'));
}
public function BanUser(){
return $this->adminObj->banUser(input('post.id'),$this->userObj->uid);
}
public function AddUser(){
return $this->adminObj->addUser(input('post.'));
}
public function Preview(){
$fileId = input('param.id');
$fileRecord = Db::name("files")->where("id",$fileId)->find();
$fileObj = new FileManage(rtrim($fileRecord["dir"],"/")."/".$fileRecord["orign_name"],$fileRecord["upload_user"]);
$previewHandler = $fileObj->PreviewHandler(true);
if($previewHandler[0]){
$this->redirect($previewHandler[1],302);
}
}
public function Download(){
$fileId = input('param.id');
$fileRecord = Db::name("files")->where("id",$fileId)->find();
$fileObj = new FileManage(rtrim($fileRecord["dir"],"/")."/".$fileRecord["orign_name"],$fileRecord["upload_user"]);
$FileHandler = $fileObj->Download(true);
if($FileHandler[0]){
$this->redirect($FileHandler[1],302);
}
}
public function Delete(){
return $this->adminObj->deleteSingle(input('post.id'));
}
public function DeleteShare(){
return $this->adminObj->deleteShare([0=>input('post.id')]);
}
public function DeleteShareMultiple(){
return $this->adminObj->deleteShare(\json_decode(input('post.id'),true));
}
public function DeleteMultiple(){
return $this->adminObj->deleteMultiple(input('post.id'));
}
public function DeletePolicy(){
return $this->adminObj->deletePolicy(input('post.id'));
}
public function DeleteGroup(){
return $this->adminObj->deleteGroup(input('post.id'));
}
public function DeleteOrder(){
return $this->adminObj->deleteOrder(input('post.id'));
}
public function ChangeShareType(){
return $this->adminObj->changeShareType(input('post.id'));
}
public function DeletePack(){
return $this->adminObj->deletePack(input('post.id'));
}
public function DeleteGroupPurchase(){
return $this->adminObj->deleteGroupPurchase(input('post.id'));
}
public function DeleteUser(){
return $this->adminObj->deleteUser(input('post.id'),$this->userObj->uid);
}
public function DeleteUsers(){
$uidGroup = \json_decode(input('post.id'),true);
foreach ($uidGroup as $key => $value) {
$this->adminObj->deleteUser($value,$this->userObj->uid);
}
return ["error"=>false,"msg"=>"删除成功"];
}
public function SwitchColor(){
$colorNow = Option::getValues(["admin"]);
if($colorNow["admin_color_body"] == "fixed-nav sticky-footer bg-light"){
$colorNew = [
"admin_color_body" => "fixed-nav sticky-footer bg-dark",
"admin_color_nav" => "navbar navbar-expand-lg fixed-top navbar-dark bg-dark",
];
}else{
$colorNew = [
"admin_color_body" => "fixed-nav sticky-footer bg-light",
"admin_color_nav" => "navbar navbar-expand-lg fixed-top navbar-light bg-light",
];
}
foreach ($colorNew as $key => $value) {
Db::name("options")->where("option_name",$key)->update(["option_value" => $value]);
}
}
public function EditPolicy(){
$policyId = input('param.id');
$policyRecord = Db::name("policy")->where("id",$policyId)->find();
return view('edit_policy', [
'options' => $this->siteOptions,
'policy' => $policyRecord,
]);
}
public function EditGroup(){
$groupId = input('param.id');
$groupRecord = Db::name("groups")->where("id",$groupId)->find();
return view('edit_group', [
'options' => $this->siteOptions,
'group' => $groupRecord,
'policy' => $this->adminObj->getAvaliablePolicy(),
]);
}
public function AddGroup(){
return $this->adminObj->addGroup(input('post.'));
}
public function PolicyAdd(){
return view('add_policy', [
'options' => $this->siteOptions,
]);
}
public function Cron(){
$cronData = Db::name("corn")->select();
$neverExcute = true;
foreach ($cronData as $key => $value) {
if($value["last_excute"] !=0){
$neverExcute = false;
}
}
return view('cron_list', [
'options' => $this->siteOptions,
'cron' => $cronData,
'neverExcute' => $neverExcute,
]);
}
public function PolicyAddS3(){
return view('add_policy_s3', [
'options' => $this->siteOptions,
]);
}
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', [
'options' => $this->siteOptions,
'programVersion' => $verison,
"dbsVersion" => Option::getValue("database_version"),
]);
}
public function Purchase(){
$packData = \json_decode(Option::getValue("pack_data"),true);
return view('purchase', [
'options' => $this->siteOptions,
'pack' => $packData,
]);
}
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) {
$groupData[$key]["group"] = Db::name("groups")->where("id",$value["goup_id"])->find();
}
return view('purchase_group', [
'options' => $this->siteOptions,
'group' => $groupData,
'group_list' => $this->adminObj->getAvaliableGroup(),
]);
}
public function GroupAdd(){
return view('add_group', [
'options' => $this->siteOptions,
'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);
}
}
+91
View File
@@ -0,0 +1,91 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\CallbackHandler;
use \app\index\model\User;
use \app\index\model\Option;
use \app\index\model\FileManage;
use think\Session;
class Callback extends Controller{
public function index(){
return "";
}
public function Qiniu(){
ob_end_clean();
header('Content-Type: application/json');
$handllerObj = new CallbackHandler(file_get_contents("php://input"));
$handllerObj -> qiniuHandler(Request::instance()->header('Authorization'));
}
public function Oss(){
ob_end_clean();
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'));
}
public function TmpPreview(){
$basicOptions = Option::getValues(['basic']);
$params = explode(":",input("param.key"));
$fileData = Db::name("files")->where("id",$params[0])->find();
if (empty($fileData)){
abort(404);
}
$userData = Db::name("users")->where("id",$fileData["upload_user"])->find();
if(md5($userData["user_pass"].$fileData["id"].$params[1].config("salt")) != $params[2] || time()<$params[0]){
abort(403);
}
$fileObj = new FileManage(rtrim($fileData["dir"],"/")."/".$fileData["orign_name"],$userData["id"]);
$Redirect = $fileObj->PreviewHandler();
}
public function Upyun(){
$signToken = Request::instance()->header('Authorization');
$reqDate = Request::instance()->header('Date');
$contentMd5 = Request::instance()->header('Content-MD5');
ob_end_clean();
header('Content-Type: application/json');
$callbackData = [
"code" => input("post.code"),
"file_size" => input("post.file_size"),
"url" => input("post.url"),
"image-width" => input("post.image-width"),
"image-height" => input("post.image-height"),
"ext-param" => json_decode(input("post.ext-param"),true),
];
$handllerObj = new CallbackHandler($callbackData);
$handllerObj -> upyunHandler($signToken,$reqDate,$contentMd5);
}
public function S3(){
$request = Request::instance();
if($request->method() == "OPTIONS"){
ob_end_clean();
header("Access-Control-Allow-Origin: *");
exit();
}
ob_end_clean();
header("Access-Control-Allow-Origin: *");
$callbackKey = input("param.key");
$callbackData = [
"bucket" => input("get.bucket"),
"key" => input("get.key"),
];
$handllerObj = new CallbackHandler($callbackData);
$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'));
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\CronHandler;
use think\Session;
class Cron extends Controller{
public function index(){
$cornObj = new CronHandler();
$cornObj->Doit();
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;
use think\Cookie;
use think\Db;
use \app\index\model\Option;
class Explore extends Controller{
public $visitorObj;
public $userObj;
public $siteOptions;
public function _initialize(){
}
public function Search(){
$this->visitorObj = new User(cookie('user_id'),cookie('login_key'));
$this->siteOptions = Option::getValues(["basic"],$this->visitorObj->userSQLData);
$keyWords=input("param.key");
if(empty($keyWords)){
$this->error("搜索词不为空",200,$this->siteOptions);
}else{
$list = Db::name('shares')
->where('type',"public")
->where('origin_name',"like","%".$keyWords."%")
->order('share_time DESC')
->select();
}
$listData = $list;
foreach ($listData as $key => $value) {
unset($listData[$key]["source_name"]);
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = $value["origin_name"];
}else{
$pathDir = explode("/",$value["source_name"]);
$listData[$key]["fileData"] = end($pathDir);
}
}
return view("result",[
"options" => $this->siteOptions,
'loginStatus' => $this->visitorObj->loginStatus,
'userData' => $this->visitorObj->getInfo(),
'list' => json_encode($listData),
'keyWords' => $keyWords,
]);
}
}
+174
View File
@@ -0,0 +1,174 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\Option;
use \app\index\model\User;
use \app\index\model\FileManage;
use \app\index\model\ShareHandler;
use think\Session;
class File extends Controller{
public $userObj;
/**
* [_initialize description]
* @return [type] [description]
*/
public function _initialize(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if(!$this->userObj->loginStatus){
echo "Bad request";
exit();
}
}
public function index(){
return "";
}
public function ListFile(){
$reqPath = stripslashes(json_decode(file_get_contents("php://input"),true)['path']);
return json(FileManage::ListFile($reqPath,$this->userObj->uid));
}
public function SearchFile(){
$keyWords = stripslashes(json_decode(file_get_contents("php://input"),true)['path']);
return json(FileManage::searchFile($keyWords,$this->userObj->uid));
}
public function Delete(){
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = array_key_exists("dirs",$reqData)?$reqData["items"]:array();
$dirPath = array_key_exists("dirs",$reqData)?$reqData["dirs"]:array();
FileManage::DirDeleteHandler($dirPath,$this->userObj->uid);
return json(FileManage::DeleteHandler($reqPath,$this->userObj->uid));
}
public function Move(){
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = array_key_exists("dirs",$reqData)?$reqData["items"]:array();
$dirPath = array_key_exists("dirs",$reqData)?$reqData["dirs"]:array();
$newPath = $reqData['newPath'];
return FileManage::MoveHandler($reqPath,$dirPath,$newPath,$this->userObj->uid);
}
public function Rename(){
$reqPath = json_decode(file_get_contents("php://input"),true)['item'];
$newPath = json_decode(file_get_contents("php://input"),true)['newItemPath'];
return FileManage::RenameHandler($reqPath,$newPath,$this->userObj->uid);
}
public function Preview(){
$reqPath =$_GET["path"];
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$Redirect = $fileObj->PreviewHandler();
if($Redirect[0]){
$this->redirect($Redirect[1],302);
}
}
public function ListPic(){
$reqPath = $_GET["path"];
return json(FileManage::listPic($reqPath,$this->userObj->uid));
}
public function Download(){
$reqPath = $_GET["path"];
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$FileHandler = $fileObj->Download();
if($FileHandler[0]){
$this->redirect($FileHandler[1],302);
}
}
public function Share(){
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = $reqData['item'];
$shareType = $reqData['shareType'];
$sharePwd = $reqData['pwd'];
ShareHandler::createShare($reqPath,$shareType,$sharePwd,$this->userObj->getSQLData(),$this->userObj->getGroupData());
}
public function gerSource(){
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = $reqData['path'];
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$FileHandler = $fileObj->Source();
}
public function Content(){
$reqPath = urldecode(input("get.path"));
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$FileHandler = $fileObj->getContent();
}
public function Edit(){
$reqPath = json_decode(file_get_contents("php://input"),true)['item'];
$fileContent = json_decode(file_get_contents("php://input"),true)['content'];
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$FileHandler = $fileObj->saveContent($fileContent);
}
public function OssDownload(){
return view('oss_download', [
'url' => urldecode(input("get.url")),
'name' => urldecode(input("get.name")),
]);
}
public function DocPreview(){
$filePath = input("get.path");
$fileObj = new FileManage($filePath,$this->userObj->uid);
$tmpUrl = $fileObj->signTmpUrl();
$this->redirect("http://view.officeapps.live.com/op/view.aspx?src=".urlencode($tmpUrl),302);
}
public function Thumb(){
$filePath = input("get.path");
if(input("get.isImg") != "true"){
return "";
}
$fileObj = new FileManage($filePath,$this->userObj->uid);
$Redirect = $fileObj->getThumb();
if($Redirect[0]){
$this->redirect($Redirect[1],302);
}
}
public function GoogleDocPreview(){
$filePath = input("get.path");
$fileObj = new FileManage($filePath,$this->userObj->uid);
$tmpUrl = $fileObj->signTmpUrl();
$this->redirect("https://docs.google.com/viewer?url=".urlencode($tmpUrl),302);
}
/**
* [createFolder description]
* @Author Aaron
* @DateTime 2017-07-03
* @return [type] [description]
*/
public function createFolder(){
$reqPath = stripslashes(json_decode(file_get_contents("php://input"),true)['newPath']);
$pathSplit = explode("/",$reqPath);
$dirName = end($pathSplit);
$dirPosition="/";
foreach ($pathSplit as $key => $value) {
if (empty($value)){
}else if($key == (count($pathSplit)-2)){
$dirPosition = $dirPosition.$value;
}else if($key == (count($pathSplit)-1)){
}else{
$dirPosition = $dirPosition.$value."/";
}
}
return json(FileManage::createFolder($dirName,$dirPosition,$this->userObj->uid));
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use think\Session;
class Home extends Controller{
public $userObj;
public function _initialize(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if(!$this->userObj->loginStatus){
$this->redirect(url('/Login','',''));
exit();
}
}
public function index(){
$userInfo = $this->userObj->getInfo();
$policyData = $this->userObj->getPolicy();
$groupData = $this->userObj->getGroupData();
$extJson = json_decode($policyData["filetype"],true);
$extLimit="";
foreach ($extJson as $key => $value) {
$extLimit.='{ title : "'.$value["title"].'", extensions : "'.$value["ext"].'" },';
}
$policyData["max_size"] = $policyData["max_size"]/(1024*1024);
return view('home', [
'options' => Option::getValues(['basic','upload'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'extLimit' => $extLimit,
'policyData' => $policyData,
'groupData' => $groupData,
'chunkSize' => config('upload.chunk_size'),
'path' => empty(input("get.path"))?"/":input("get.path"),
]);
}
public function Download(){
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
return view('download', [
'options' => Option::getValues(['basic','group_sell'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'groupData' => $groupData,
]);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
class Index extends Controller{
public $userObj;
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']),
'userInfo' => $userInfo,
]);
}
}
+327
View File
@@ -0,0 +1,327 @@
<?php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;
use think\Cookie;
use think\Db;
use \app\index\model\Option;
use \app\index\model\Avatar;
use \PHPGangsta_GoogleAuthenticator;
use \app\index\model\TwoFactor;
class Member extends Controller{
public $userObj;
public function index(){
echo "Pong";
}
public function Register(){
if(input('?post.username-reg') && input('?post.password-reg')){
$regAction = User::register(input('post.username-reg'),input('post.password-reg'),input('post.captchaCode'));
if ($regAction[0]){
return json(['code' => '200','message' => $regAction[1]]);
}else{
return json(['code' => '1','message' => $regAction[1]]);
}
}else{
return json(['code' => '1','message' => "信息不完整"]);
}
}
public function ForgetPwd(){
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]]);
}else{
return json(['code' => '1','message' => $findAction[1]]);
}
}else{
return json(['code' => '1','message' => "信息不完整"]);
}
}
public function Login(){
if(input('?post.userMail') && input('?post.userPass')){
$logAction = User::login(input('post.userMail'),input('post.userPass'),input('post.captchaCode'));
if ($logAction[0]){
return json(['code' => '200','message' => '登陆成功']);
}else{
return json(['code' => '1','message' => $logAction[1]]);
}
}else{
return json(['code' => '1','message' => "信息不完整"]);
}
}
public function LogOut(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->userObj->clear();
$this->redirect("/Login",302);
}
public function Memory(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->userObj->getMemory();
}
public function SignUp(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "register",
]);
}
public function FindPwd(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "resetPwd",
]);
}
public function LoginForm(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "login",
]);
}
public function TwoStepCheck(){
$checkCode = input("post.code");
if(empty($checkCode)){
return json(['code' => '1','message' => "验证码不能为空"]);
}
$userId = session("user_id_tmp");
$userData = Db::name('users')->where('id',$userId)->find();
$ga = new PHPGangsta_GoogleAuthenticator();
$checkResult = $ga->verifyCode($userData["two_step"], $checkCode, 2);
if($checkResult) {
cookie('user_id',session("user_id_tmp"),604800);
cookie('login_status',session("login_status_tmp"),604800);
cookie('login_key',session("login_key_tmp"),604800);
return json(['code' => '200','message' => '登陆成功']);
}else{
return json(['code' => '1','message' => "验证失败"]);
}
}
public function TwoStep(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "TwoStep",
]);
}
public function setWebdavPwd(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->loginStatusCheck();
Db::name("users")->where("id",$this->userObj->uid)
->update([
"webdav_key" => md5($this->userObj->userSQLData["user_email"].":CloudreveWebDav:".input("post.pwd")),
]);
return json(['error' => '200','msg' => '设置成功']);
}
public function emailActivate(){
$activationKey = input('param.key');
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
$basicOptions = Option::getValues(['basic','register','login'],$this->userObj->userSQLData);
$activeAction = User::activicateUser($activationKey);
if($activeAction[0]){
return view('login', [
'options' => $basicOptions,
'RegOptions' => $basicOptions,
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "emailActivate",
]);
}else{
$this->error($activeAction[1],403,$basicOptions);
}
}
public function resetPwd(){
$resetKey = input('param.key');
$userId = input('get.uid');
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$basicOptions = Option::getValues(['basic','register','login'],$this->userObj->userSQLData);
$this->isLoginStatusCheck();
$resetAction = User::resetUser($resetKey,$userId);
if($resetAction[0]){
return view('login', [
'options' => $basicOptions,
'RegOptions' => $basicOptions,
'loginStatus' => $this->userObj->loginStatus,
'key' => $resetKey."_".$userId,
'pageId' => "resetPwdForm",
]);
}else{
$this->error($resetAction[1],403,$basicOptions);
}
}
public function Reset(){
$newPwd = input('post.pwd');
$resetKey = input('post.key');
$resetAction = User::resetPwd($resetKey,$newPwd);
if($resetAction[0]){
return json(['code' => '200','message' => '重设成功,请前往登录页登录']);
}else{
return json(['code' => '1','message' => $resetAction[1]]);
}
}
public function Setting(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$userInfo = $this->userObj->getInfo();
$this->loginStatusCheck();
$policyList=[];
foreach (explode(",",$this->userObj->groupData["policy_list"]) as $key => $value) {
$policyList[$key] = $value;
}
$avaliablePolicy = Db::name("policy")->where("id","in",$policyList)->select();
$basicOptions = Option::getValues(['basic'],$this->userObj->userSQLData);
return view('setting', [
'options' => $basicOptions,
'userInfo' => $userInfo,
'userSQL' => $this->userObj->userSQLData,
'groupData' => $this->userObj->groupData,
'loginStatus' => $this->userObj->loginStatus,
'avaliablePolicy' => $avaliablePolicy,
]);
}
public function SaveAvatar(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$file = request()->file("avatar");
$avatarObj = new Avatar(true,$file);
if(!$avatarObj->SaveAvatar()){
return json_encode($avatarObj->errorMsg);
}else{
$avatarObj->bindUser($this->userObj->uid);
return json_encode(["result" => "success"]);
}
}
public function Avatar(){
if(!input("get.cache")=="no"){
header("Cache-Control: max-age=10800");
}
$userId = input("param.uid");
$avatarObj = new Avatar(false,$userId);
$avatarImg = $avatarObj->Out(input("param.size"));
$this->redirect($avatarImg,302);
}
public function SetGravatar(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$avatarObj = new Avatar(false,$this->userObj->uid);
$avatarObj->setGravatar();
}
public function Nick(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$userInfo = $this->userObj->getInfo();
$this->loginStatusCheck();
$saveAction = $this->userObj->changeNick(input("post.nick"));
if($saveAction[0]){
return json(['error' => '200','msg' => '设置成功']);
}else{
return json(['error' => '1','msg' => $saveAction[1]]);
}
}
public function ChangeThemeColor(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->loginStatusCheck();
$saveAction = $this->userObj->changeOption("preferTheme",input("post.theme"));
if($saveAction[0]){
return json(['error' => '200','msg' => '设置成功']);
}else{
return json(['error' => '1','msg' => $saveAction[1]]);
}
}
public function HomePage(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$userInfo = $this->userObj->getInfo();
$this->loginStatusCheck();
$saveAction = $this->userObj->homePageToggle(input("post.status"));
if($saveAction[0]){
return json(['error' => '200','msg' => '设置成功']);
}else{
return json(['error' => '1','msg' => $saveAction[1]]);
}
}
public function EnableTwoFactor(){
$twoFactor = new TwoFactor();
$twoFactor->qrcodeRender();
}
public function TwoFactorConfirm(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$userInfo = $this->userObj->getInfo();
$this->loginStatusCheck();
$twoFactor = new TwoFactor();
$confirmResult = $twoFactor->confirmCode(session("two_factor_enable"),input("post.code"));
if($confirmResult[0]){
$twoFactor->bindUser($this->userObj->uid);
return json(['error' => '200','msg' => '设置成功']);
}else{
return json(['error' => '1','msg' => $confirmResult[1]]);
}
}
public function ChangePwd(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$userInfo = $this->userObj->getInfo();
$this->loginStatusCheck();
$changeAction = $this->userObj->changePwd(input("post.origin"),input("post.new"));
if($changeAction[0]){
return json(['error' => '200','msg' => '设置成功']);
}else{
return json(['error' => '1','msg' => $changeAction[1]]);
}
}
private function loginStatusCheck($login=true){
if(!$this->userObj->loginStatus){
if($login){
$this->redirect(url('/Login','',''));
}else{
$this->redirect(url('/Home','',''));
}
exit();
}
}
private function isLoginStatusCheck(){
if($this->userObj->loginStatus){
$this->redirect(url('/Home','',''));
exit();
}
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;
use think\Cookie;
use think\Db;
use \app\index\model\Option;
class Profile extends Controller{
public $visitorObj;
public $userObj;
public $siteOptions;
public function _initialize(){
}
public function getList(){
$userId = (string)input("post.uid");
$userData = Db::name("users")->where("id",$userId)->find();
$page = (int)input("post.page");
if (empty($userId) || empty($userData) || $userData["profile"] == 0){
$this->error('用户主页不存或者用户关闭了个人主页',404,$this->siteOptions);
}
switch (input("post.type")) {
case 'all':
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('share_time DESC')
->page($page.',10')
->select();
break;
case 'hot':
$num = Option::getValue("hot_share_num");
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('download_num DESC')
->limit($num)
->select();
break;
default:
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('share_time DESC')
->page($page.',10')
->select();
break;
}
$listData = $list;
foreach ($listData as $key => $value) {
unset($listData[$key]["share_pwd"]);
unset($listData[$key]["source_name"]);
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = Db::name('files')->where('id',$value["source_name"])->find()["orign_name"];
}else{
$pathDir = explode("/",$value["source_name"]);
$listData[$key]["fileData"] = end($pathDir);
}
}
return json($listData);
}
public function index(){
$this->visitorObj = new User(cookie('user_id'),cookie('login_key'));
$this->siteOptions = Option::getValues(["basic"],$this->visitorObj->userSQLData);
$userId = (string)input("param.uid");
$userData = Db::name("users")->where("id",$userId)->find();
if (empty($userId) || empty($userData) || $userData["profile"] == 0){
$this->error('用户主页不存或者用户关闭了个人主页',404,$this->siteOptions);
}
$groupData = Db::name("groups")->where("id",$userData["user_group"])->find();
$shareCount = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->count();
$regDays = (int)((time()-strtotime($userData["user_date"]))/86400);
return view("profile",[
"options" => $this->siteOptions,
'loginStatus' => $this->visitorObj->loginStatus,
'targetUserInfo' => $userData,
'userSQL' => $this->visitorObj->userSQLData,
'userInfo' => $this->visitorObj->getInfo(),
'groupData' => $groupData,
'type' => input("get.type"),
'shareCount' => $shareCount,
'regDays' => $regDays,
]);
}
}
+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" && $policyData["policy_type"] != "onedrive"){
return json(["result"=>['success'=>false,'error'=>"您当前的无用户无法执行此操作"]]);
}
$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);
}
}
+282
View File
@@ -0,0 +1,282 @@
<?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\ShareHandler;
use think\Session;
use think\Request;
class Share extends Controller{
public $userObj;
public $siteOptions;
public function _initialize(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->siteOptions = Option::getValues(["basic","share"]);
}
public function index(){
$shareKey = input('param.key');
$shareObj = new ShareHandler($shareKey);
if(!$shareObj->querryStatus){
header('HTTP/1.1 404 Not Found');
$this->error('当前分享不存在或者已经失效',404,$this->siteOptions);
}
if(!$shareObj->lockStatus){
$shareObj->numIncrease("view_num");
if($shareObj->shareData["source_type"] == "dir"){
return view('share_dir', [
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userInfo' => $shareObj->shareOwner->userSQLData,
'dirData' => $shareObj->dirData,
'shareData' => $shareObj->shareData,
'loginStatus' => $this->userObj->loginStatus,
'userData' => $this->userObj->getInfo(),
'groupData' => $shareObj->shareOwner->groupData,
'allowPreview' => Option::getValue("allowdVisitorDownload"),
'path' => empty(input("get.path"))?"/":input("get.path"),
]);
}else{
return view('share_single', [
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userInfo' => $shareObj->shareOwner->userSQLData,
'fileData' => $shareObj->fileData,
'shareData' => $shareObj->shareData,
'loginStatus' => $this->userObj->loginStatus,
'userData' => $this->userObj->getInfo(),
'allowPreview' => Option::getValue("allowdVisitorDownload"),
'path' => empty(input("get.path"))?"/":input("get.path"),
]);
}
}else{
return view('share_lock', [
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userInfo' => $shareObj->shareOwner->userSQLData,
'fileData' => $shareObj->fileData,
'shareData' => $shareObj->shareData,
'loginStatus' => $this->userObj->loginStatus,
'userData' => $this->userObj->getInfo(),
'pwd' => input("?get.pwd") ? input("get.pwd") : "",
]);
}
}
public function getDownloadUrl(){
$shareId = input('key');
$shareObj = new ShareHandler($shareId,false);
return json($shareObj->getDownloadUrl($this->userObj));
}
public function Download(){
$shareId = input('param.key');
$filePath = input('get.path');
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$DownloadHandler = $shareObj->Download($this->userObj);
}else{
$DownloadHandler = $shareObj->DownloadFolder($this->userObj,$filePath);
}
if($DownloadHandler[0]){
$this->redirect($DownloadHandler[1],302);
}else{
$this->error($DownloadHandler[1],404,$this->siteOptions);
}
}
public function Content(){
$shareId = input('param.key');
$filePath = input('get.path');
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$contentHandller = $shareObj->getContent($this->userObj,$filePath,false);
}else{
$contentHandller = $shareObj->getContent($this->userObj,$filePath,true);
}
if(!$contentHandller[0]){
return json(["result"=>["success"=>false,"error"=>$contentHandller[1]]]);
}
}
public function chekPwd(){
$shareId = input('key');
$inputPwd = input('password');
$shareObj = new ShareHandler($shareId,false);
if(!$shareObj->querryStatus){
return array(
"error" => 1,
"msg" => "分享不存在"
);
}
return json($shareObj->checkPwd($inputPwd));
}
private function referCheck(){
$agent = Request::instance()->header('referer');
if(substr($agent, 0, strlen($this->siteOptions["siteURL"])) !== $this->siteOptions["siteURL"]){
return false;
}
return true;
}
public function Preview(){
$shareId = input('param.key');
$filePath = input('get.path');
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$previewHandler = $shareObj->Preview($this->userObj);
}else{
if(!empty(input('get.folder'))){
$previewHandler = $shareObj->PreviewFolder($this->userObj,$filePath,true);
}else{
$previewHandler = $shareObj->PreviewFolder($this->userObj,$filePath);
}
}
if($previewHandler[0]){
$this->redirect($previewHandler[1],302);
}else{
$this->error($previewHandler[1],404,$this->siteOptions);
}
}
public function ListFile(){
$shareId = input('param.key');
$reqPathTo = stripslashes(json_decode(file_get_contents("php://input"),true)['path']);
$shareObj = new ShareHandler($shareId,false);
return json($shareObj->ListFile($reqPathTo));
}
public function ListPic(){
$filePath = input('get.path');
$shareId = input('get.id');
$shareObj = new ShareHandler($shareId,false);
return $shareObj->listPic($shareId,$filePath);
}
public function Thumb(){
$shareId = input('param.key');
$filePath = urldecode(input('get.path'));
if(input("get.isImg") != "true"){
return "";
}
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$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 DocPreview(){
$shareId = input('param.key');
$filePath = urldecode(input('get.path'));
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$Redirect = $shareObj->getDocPreview($this->userObj,$filePath,false);
}else{
$Redirect = $shareObj->getDocPreview($this->userObj,$filePath,true);
}
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);
if(!$shareObj->querryStatus){
return json(array(
"error" => 1,
"msg" => "分享不存在"
));
}
return json($shareObj->deleteShare($this->userObj->uid));
}
public function ChangePromission(){
$shareId = input('post.id');
$shareObj = new ShareHandler($shareId,false);
if(!$shareObj->querryStatus){
return json(array(
"error" => 1,
"msg" => "分享不存在"
));
}
return json($shareObj->changePromission($this->userObj->uid));
}
public function ListMyShare(){
if(!$this->userObj->loginStatus){
$this->redirect(url('/Login','',''));
exit();
}
$list = Db::name('shares')
->where('owner',$this->userObj->uid)
->order('share_time DESC')
->page(input("post.page").",18")
->select();
$listData = $list;
foreach ($listData as $key => $value) {
unset($listData[$key]["source_name"]);
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = Db::name('files')->where('id',$value["source_name"])->find()["orign_name"];
}else{
$listData[$key]["fileData"] = $value["source_name"];
}
}
return json($listData);
}
public function My(){
if(!$this->userObj->loginStatus){
$this->redirect(url('/Login','',''));
exit();
}
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
return view('share_home', [
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userData' => $userInfo,
'groupData' => $groupData,
]);
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\Option;
use \app\index\model\User;
use \app\index\model\UploadHandler;
use think\Session;
class Upload 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();
}
}
public function index(){
ob_end_clean();
$file = request()->file('file');
$fileInfo = Request::instance()->request();
$UploadHandler = new UploadHandler($this->userObj->groupData['policy_name'],$this->userObj->uid);
return $UploadHandler->fileReceive($file,$fileInfo);
}
public function Token(){
$uploadObj = new UploadHandler($this->userObj->groupData['policy_name'],$this->userObj->uid);
$upToken = $uploadObj->getToken();
if(!empty($uploadObj->upyunPolicy)){
return json([
"token" => $upToken,
"policy" => $uploadObj->upyunPolicy,
]);
}
if(!empty($uploadObj->s3Policy)){
return json([
"policy" => $uploadObj->s3Policy,
"sign" => $uploadObj->s3Sign,
"key" => $uploadObj->dirName,
"credential" => $uploadObj->s3Credential,
"x_amz_date" => $uploadObj->x_amz_date,
"siteUrl"=>$uploadObj->siteUrl,
"callBackKey" => $uploadObj->callBackKey,
]);
}
if(!$uploadObj->getToken()){
return json([
"uptoken" => $uploadObj->ossToken,
"sign" => $uploadObj->ossSign,
"id" => $uploadObj->ossAccessId,
"key" => $uploadObj->ossFileName,
"callback" => $uploadObj->ossCallBack,
]);
}
return json(["uptoken" => $uploadObj->getToken()]);
}
public function chunk(){
$file = file_get_contents('php://input');
$uploadObj = new UploadHandler($this->userObj->groupData['policy_name'],$this->userObj->uid);
$uploadObj->setChunk(input('param.chunk'),input('param.chunks'),$file);
}
public function mkFile(){
$ctx = file_get_contents('php://input');
$originName = UploadHandler::b64Decode(input('param.fname'));
$filePath = UploadHandler::b64Decode(input('param.path'));
$uploadObj = new UploadHandler($this->userObj->groupData['policy_name'],$this->userObj->uid);
$uploadObj->generateFile($ctx,$originName,$filePath);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use think\Session;
use \app\index\model\FileManage;
use \app\index\model\ShareHandler;
class Viewer extends Controller{
public $userObj;
public function _initialize(){
// $this->userObj = new User(cookie('user_id'),cookie('login_key'));
// if(!$this->userObj->loginStatus){
// $this->redirect(url('/Login','',''));
// exit();
// }
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
}
public function Video(){
$path = input("get.path");
$pathSplit = explode("/",urldecode($path));
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$url = "/File/Preview?action=preview&path=".$path;
if(input("get.share")==true){
$url = "/Share/Preview/".input("get.shareKey")."/?path=".$path;
}else if(input("get.single")==true){
$url = "/Share/Preview/".input("get.shareKey");
}
return view('video', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'groupData' => $groupData,
'url' => $url,
'fileName' => end($pathSplit),
'isSharePage' => input("?get.share")?"true":"false",
]);
}
public function Markdown(){
$path = input("get.path");
$pathSplit = explode("/",urldecode($path));
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$url = "/File/Content?action=preview&path=".$path;
if(input("get.share")==true){
$url = "/Share/Content/".input("get.shareKey")."/?path=".$path;
}else if(input("get.single")==true){
$url = "/Share/Content/".input("get.shareKey");
}
return view('markdown', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'groupData' => $groupData,
'url' => $url,
'fileName' => end($pathSplit),
'path' => urldecode($path),
'isSharePage' => input("?get.share")?"true":"false",
]);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?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\Directory;
use \app\index\model\Objects;
use \app\index\model\DavAuth;
use \app\index\model\BasicCallBack;
use Sabre\DAV;
use Sabre\DAV\Auth;
class WebDav extends Controller{
public $userObj;
public $uid;
public function index(){
}
public function Api(){
$this->uid = input("param.uid");
$publicDir = new Directory($this->uid."/");
$server = new DAV\Server($publicDir);
$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);
$check = new DavAuth($this->uid);
$callBack = new BasicCallBack($check);
$authPlugin = new Auth\Plugin($callBack);
$server->addPlugin($authPlugin);
$server->addPlugin(new DAV\Browser\Plugin());
$server->addPlugin(new \Sabre\DAV\Browser\GuessContentType());
ob_end_clean();
$server->exec();
}
}
+811
View File
@@ -0,0 +1,811 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Mail;
use \app\index\model\FileManage;
use \Krizalys\Onedrive\Client;
class AdminHandler extends Model{
public $siteOptions;
public $pageData;
public $listData;
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();
$statics["publicShareNum"] = Db::name('shares')->where("type","public")->count();
$statics["userNum"] = Db::name('users')->where("user_status",0)->count();
if($statics["fileNum"]==0){
$statics["imgRate"] = 0;
$statics["audioRate"] = 0;
$statics["videoRate"] = 0;
$statics['otherRate'] = 0;
}else{
$statics["imgRate"] =floor(Db::name('files')
->where('pic_info',"<>"," ")
->where('pic_info',"<>","0,0")
->where('pic_info',"<>","null,null")
->count()/$statics["fileNum"]*10000)/100;
$statics["audioRate"] =floor(Db::name('files')
->where(function ($query) {
$query->where('orign_name', "like","%mp3")
->whereor('orign_name', "like","%flac")
->whereor('orign_name', "like","%wma")
->whereor('orign_name', "like","%aac")
->whereor('orign_name', "like","%wav")
->whereor('orign_name', "like","%ogg");
})
->count()/$statics["fileNum"]*10000)/100;
$statics["videoRate"] =floor(Db::name('files')
->where(function ($query) {
$query->where('orign_name', "like","%mp4")
->whereor('orign_name', "like","%avi")
->whereor('orign_name', "like","%rmvb")
->whereor('orign_name', "like","%aac")
->whereor('orign_name', "like","%wav")
->whereor('orign_name', "like","%mkv");
})
->count()/$statics["fileNum"]*10000)/100;
$statics['otherRate'] = 100-($statics["videoRate"]+$statics["audioRate"]+$statics["imgRate"]);
}
$timeNow=time();
$statics["trendFile"]="";
$statics["trendUser"]="";
$statics["trendDate"]="";
for ($i=0; $i < 13; $i++) {
$statics["trendFile"].= Db::name('files')->where('upload_date','between time',[date("Y-m-d",$timeNow-(12-$i)*3600*24),date("Y-m-d",$timeNow-(11-$i)*3600*24)])->count().",";
}
for ($i=0; $i < 13; $i++) {
$statics["trendUser"].= Db::name('users')->where('user_date','between time',[date("Y-m-d",$timeNow-(12-$i)*3600*24),date("Y-m-d",$timeNow-(11-$i)*3600*24)])->count().",";
}
for ($i=0; $i < 13; $i++) {
$statics["trendDate"].='"'.date("m月d日",$timeNow-(12-$i)*3600*24).'",';
}
$statics["trendFile"] = rtrim($statics["trendFile"],",");
$statics["trendDate"] = rtrim($statics["trendDate"],",");
$statics["trendUser"] = rtrim($statics["trendUser"],",");
return $statics;
}
public function saveBasicSetting($options){
$siteUrl = rtrim($options["siteURL"],"/")."/";
$options["siteURL"]=$siteUrl;
return $this->saveOptions($options);
}
public function saveColorSetting($options){
$colorOptions = [];
foreach ($options["color"] as $key => $value) {
$color = \json_decode($value,true);
$validate = ($color!==null)&&array_key_exists("palette",$color) &&
array_key_exists("common",$color["palette"]) &&
array_key_exists("black",$color["palette"]["common"]) &&
array_key_exists("white",$color["palette"]["common"]) &&
array_key_exists("background",$color["palette"]) &&
array_key_exists("primary",$color["palette"]) &&
array_key_exists("secondary",$color["palette"]) &&
array_key_exists("error",$color["palette"]) &&
array_key_exists("text",$color["palette"]) &&
array_key_exists("explorer",$color["palette"]) &&
array_key_exists("main",$color["palette"]["primary"]) &&
array_key_exists("main",$color["palette"]["secondary"]);
if(!$validate){
return ["error"=>1,"msg"=>"".($key+1)." 行配色方案格式有误"];
}
$colorOptions[$color["palette"]["primary"]["main"]] = $color;
}
if(!array_key_exists($options["header"],$colorOptions)){
return ["error"=>1,"msg"=>"".($key+1)." 默认配色不存在"];
}
$optionsForSet = [
"defaultTheme" => $options["header"],
"themes" => json_encode($colorOptions),
];
return $this->saveOptions($optionsForSet);
}
public function saveRegSetting($options){
foreach(["email_active","login_captcha","reg_captcha","forget_captcha"] as $key){
$options[$key] = array_key_exists($key,$options) ? $options[$key] : 0;
}
return $this->saveOptions($options);
}
public function saveMailSetting($options){
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);
}
public function AddGroup($options){
$options["max_storage"] = $options["max_storage"]*$options["sizeTimes"];
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) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
}
public function addPolicy($options){
$options["max_size"] = $options["max_size"]*$options["sizeTimes"];
unset($options["sizeTimes"]);
$options["server"] = isset($options["server"]) ? $options["server"] : "/Upload";
foreach (["bucketname","bucket_private","bucketname","ak","sk","op_name","op_pwd","mimetype","namerule"] as $key => $value) {
$options[$value] = isset($options[$value]) ? $options[$value] : "0";
}
if(empty($options["filetype"])){
$options["filetype"]="[]";
}else{
$options["filetype"] = json_encode([0=>["ext"=>$options["filetype"],"title"=>"default"]]);
}
if($options["policy_type"] == "upyun"){
$options["server"] = "https://v0.api.upyun.com/".$options["bucketname"];
}
try {
Db::name("policy")->insert($options);
} catch (Exception $e) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存","id"=>Db::name('policy')->getLastInsID()];
}
public function editPolicy($options){
$policyId = $options["id"];
$options["max_size"] = $options["max_size"]*$options["sizeTimes"];
unset($options["sizeTimes"]);
unset($options["id"]);
if(empty($options["filetype"])){
$options["filetype"]="[]";
}else{
$options["filetype"] = json_encode([0=>["ext"=>$options["filetype"],"title"=>"default"]]);
}
try {
Db::name("policy")->where("id",$policyId)->update($options);
} catch (Exception $e) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
}
public function saveGroup($options){
$groupId = $options["id"];
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) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
}
public function saveOptions($options){
try {
foreach ($options as $key => $value) {
Db::name("options")->where("option_name",$key)->update(["option_value"=>$value]);
}
} catch (Exception $e) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
}
public function getAvaliableGroup(){
$groupData = Db::name("groups")->where("id","neq",2)->select();
return $groupData;
}
public function saveCron($options){
$cronId = $options["id"];
unset($options["id"]);
Db::name("corn")->where("id",$cronId)->update($options);
}
public function getAvaliablePolicy(){
$policyData = Db::name("policy")->select();
return $policyData;
}
public function sendTestMail($options){
$mailObj = new Mail();
if(empty($options["receiveMail"])){
return ["error"=>1,"msg"=>"接收邮箱不能为空"];
}
$sendResult = $mailObj->Send($options["receiveMail"],"发信测试",$options["subject"],$options["content"]);
if($sendResult){
return ["error"=>200,"msg"=>"发送成功"];
}else{
return ["error"=>1,"msg"=>$mailObj->errorMsg];
}
}
public function deleteSingle($id){
$fileRecord = Db::name("files")->where("id",$id)->find();
return FileManage::DeleteHandler([0 => rtrim($fileRecord["dir"],"/")."/".$fileRecord["orign_name"]],$fileRecord["upload_user"]);
}
public function deletePolicy($id){
$groupData = Db::name("groups")->where("policy_name",$id)->select();
if(!empty($groupData)){
return ["error"=>true,"msg"=>"此上传策略正在被以下用户组使用:".join(",",array_column($groupData, "group_name"))];
}
Db::name("policy")->where("id",$id)->delete();
return ["error"=>false,"msg"=>"已删除"];
}
public function deleteGroup($id){
$userData = Db::name("users")->where("user_group",$id)->find();
if(!empty($userData)){
return ["error"=>true,"msg"=>"此用户组下仍有用户,请先删除这些用户"];
}
if($id == 1 || $id == 2){
return ["error"=>true,"msg"=>"系统保留用户组,无法删除"];
}
Db::name("groups")->where("id",$id)->delete();
return ["error"=>false,"msg"=>"已删除"];
}
public function getConfigFile($type){
switch ($type) {
case 'common':
$configPath = ROOT_PATH ."application/config.php";
$basicPath = "application/config.php";
break;
case 'database':
if(file_exists( ROOT_PATH ."application/database.lock")){
return ["出于安全考虑,默认禁止直接编辑数据库配置文件。如果需要开启编辑,请手动删除 application/database.lock 文件。","application/database.php"];
}
$configPath = ROOT_PATH ."application/database.php";
$basicPath = "application/database.php";
break;
case 'route':
$configPath = ROOT_PATH ."application/route.php";
$basicPath = "application/route.php";
break;
case 'tags':
$configPath = ROOT_PATH ."application/tags.php";
$basicPath = "application/tags.php";
break;
default:
die("");
break;
}
return [file_get_contents($configPath),$basicPath];
}
public function saveConfigFile($options){
switch ($options["type"]) {
case 'common':
file_put_contents(ROOT_PATH ."application/config.php",$options["content"]);
break;
case 'route':
file_put_contents(ROOT_PATH ."application/route.php",$options["content"]);
break;
case 'tags':
file_put_contents(ROOT_PATH ."application/tags.php",$options["content"]);
break;
case 'database':
if(file_exists( ROOT_PATH ."application/database.lock")){
return ["error"=>true,"msg"=>"出于安全考虑,默认禁止直接编辑数据库配置文件。如果需要开启编辑,请手动删除 application/database.lock 文件。"];
}
file_put_contents(ROOT_PATH ."application/database.php",$options["content"]);
break;
default:
# code...
break;
}
return ["error"=>false,"msg"=>""];
}
public function deleteMultiple($id){
$fileInfo = json_decode($id,true);
$pathGroup = [];
foreach ($fileInfo as $key => $value) {
$pathGroup[$value["uid"]] = isset($pathGroup[$value["uid"]]) ? $pathGroup[$value["uid"]] : [];
array_push($pathGroup[$value["uid"]], $value["path"]);
}
foreach ($pathGroup as $key => $value) {
FileManage::DeleteHandler($value,$key);
}
return ["error"=>200,"msg"=>"删除成功"];
}
public function deleteShare($ids){
Db::name("shares")->where("id","in",$ids)->delete();
return ["error"=>false,"msg"=>"删除成功"];
}
public function deleteOrder($id){
Db::name("order")->where("id",$id)->delete();
return ["error"=>false,"msg"=>"删除成功"];
}
public function deleteUser($id,$userNow){
if($userNow == $id){
return ["error"=>true,"msg"=>"我的老伙计,你可不能删除你自己"];
}
//删除用户所有文件及目录
FileManage::DirDeleteHandler([0 => "/"],$id);
//删除此用户所有分享
Db::name("shares")->where("owner",$id)->delete();
//删除此用户
Db::name("users")->where("id",$id)->delete();
return ["error"=>false,"msg"=>"删除成功"];
}
public function changeShareType($id){
$shareId = $id;
$shareObj = new ShareHandler($shareId,false);
if(!$shareObj->querryStatus){
return array(
"error" => 1,
"msg" => "分享不存在"
);
}
return $shareObj->changePromission(0,true);
}
public function getUserInfo($id){
$userData = Db::name("users")->where("id",$id)->find();
$userData["used_storage"] =getSize($userData["used_storage"]);
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 = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodFile')) ? "id DESC" : cookie('orderMethodFile');
$this->pageData = Db::name("files")
->where(function ($query) {
if(!empty(cookie('fileSearch'))){
$query->where('orign_name', "like","%".cookie('fileSearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('filePolicy'))){
$query->where('policy_id', cookie('filePolicy'));
}
})
->where(function ($query) {
if(!empty(cookie('searchValue'))){
$query->where(cookie('searchCol'),"like", cookie('searchValue'));
}
})
->order($orderType)
->paginate($pageSize);
$this->dataTotal = Db::name("files")
->where(function ($query) {
if(!empty(cookie('fileSearch'))){
$query->where('orign_name', "like","%".cookie('fileSearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('filePolicy'))){
$query->where('policy_id', cookie('filePolicy'));
}
})
->where(function ($query) {
if(!empty(cookie('searchValue'))){
$query->where(cookie('searchCol'),"like", cookie('searchValue'));
}
})
->order($orderType)
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$userCache=[];
$userCacheList=[];
foreach ($this->listData as $key => $value) {
if(in_array($value["upload_user"], $userCacheList)){
$this->listData[$key]["user"] = $userCache[$value["upload_user"]];
}else{
$this->listData[$key]["user"] = Db::name("users")->where("id",$value["upload_user"])->find();
array_push($userCacheList,$value["upload_user"]);
$userCache[$value["upload_user"]] = $this->listData[$key]["user"];
}
}
$this->pageNow = input("?get.page")?input("get.page"):1;
}
public function listUser(){
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodUser')) ? "id DESC" : cookie('orderMethodUser');
$this->pageData = Db::name("users")
->where(function ($query) {
if(!empty(cookie('userStatus'))){
$query->where('user_status', cookie('userStatus')-1);
}
})
->where(function ($query) {
if(!empty(cookie('userSearch'))){
$query->where('user_nick', "like","%".cookie('userSearch')."%")
->whereOr("id",cookie('userSearch'))
->whereOr("user_email","like","%".cookie('userSearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('userGroup'))){
$query->where('user_group', cookie('userGroup'));
}
})
->where(function ($query) {
if(!empty(cookie('searchValueUser'))){
$query->where(cookie('searchColUser'),"like", cookie('searchValueUser'));
}
})
->order($orderType)
->paginate($pageSize);
$this->dataTotal = Db::name("users")
->where(function ($query) {
if(!empty(cookie('userStatus'))){
$query->where('user_status', cookie('userStatus')-1);
}
})
->where(function ($query) {
if(!empty(cookie('userSearch'))){
$query->where('user_nick', "like","%".cookie('userSearch')."%")
->whereOr("id",cookie('userSearch'))
->whereOr("user_email","like","%".cookie('userSearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('userGroup'))){
$query->where('user_group', cookie('userGroup'));
}
})
->where(function ($query) {
if(!empty(cookie('searchValueUser'))){
$query->where(cookie('searchColUser'),"like", cookie('searchValueUser'));
}
})
->order($orderType)
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$groupCache=[];
$groupCacheList=[];
foreach ($this->listData as $key => $value) {
if(in_array($value["user_group"], $groupCacheList)){
$this->listData[$key]["group"] = $groupCache[$value["user_group"]];
}else{
$this->listData[$key]["group"] = Db::name("groups")->where("id",$value["user_group"])->find();
array_push($groupCacheList,$value["user_group"]);
$groupCache[$value["user_group"]] = $this->listData[$key]["group"];
}
}
$this->pageNow = input("?get.page")?input("get.page"):1;
}
public function listShare(){
$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'))){
$query->where('source_name', "like","%".cookie('shareSearch')."%")->whereOr('origin_name', "like","%".cookie('shareSearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('shareType'))){
$query->where('type', cookie('shareType'));
}
})
->order($orderType)
->paginate($pageSize);
$this->dataTotal = Db::name("shares")
->where(function ($query) {
if(!empty(cookie('shareSearch'))){
$query->where('source_name', "like","%".cookie('shareSearch')."%")->whereOr('origin_name', "like","%".cookie('shareSearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('shareType'))){
$query->where('type', cookie('shareType'));
}
})
->order($orderType)
->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"];
}
}
$this->pageNow = input("?get.page")?input("get.page"):1;
}
public function listPolicy(){
$pageSize =!cookie('?pageSize') ? 10 : cookie('pageSize');
$this->pageData = Db::name("policy")
->where(function ($query) {
if(!empty(cookie('policySearch'))){
$query->where('policy_name', "like","%".cookie('policySearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('policyType'))){
$query->where('policy_type', cookie('policyType'));
}
})
->order("id DESC")
->paginate($pageSize);
$this->dataTotal = Db::name("policy")
->where(function ($query) {
if(!empty(cookie('policySearch'))){
$query->where('policy_name', "like","%".cookie('policySearch')."%");
}
})
->where(function ($query) {
if(!empty(cookie('policyType'))){
$query->where('policy_type', cookie('policyType'));
}
})
->order("id DESC")
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$this->pageNow = input("?get.page")?input("get.page"):1;
foreach ($this->listData as $key => $value) {
$this->listData[$key]["file_num"] = Db::name("files")->where("policy_id",$value["id"])->count();
$this->listData[$key]["file_size"] = Db::name("files")->where("policy_id",$value["id"])->sum("size");
}
}
public function getFileInfo($id){
$fileRecord = Db::name("files")->where("id",$id)->find();
$policyRecord = Db::name("policy")->where("id",$fileRecord["policy_id"])->find();
$fileRecord["policy"] = $policyRecord;
return $fileRecord;
}
public function saveThemeFile($options){
$fileName=$options["name"];
$dir = ROOT_PATH."application/index/view/";
$fileList=[];
$fileList=$fileList+scandir($dir);
$pathList=["/"=>$fileList];
foreach (["admin","explore","file","home","index","member","profile","share"] as $key => $value) {
$childPath = scandir($dir.$value."/");
$fileList=array_merge($fileList,$childPath);
$pathList = array_merge($pathList,[$value => $childPath]);
}
foreach ($fileList as $key => $value) {
if(substr_compare($value, ".html", -strlen(".html")) != 0){
unset($fileList[$key]);
}
}
foreach($pathList as $key=>$val){
if(in_array($fileName.".html",$val)){
$parentPath = $key;
break;
}
}
file_put_contents($dir.rtrim($parentPath,"/")."/".$fileName.".html",$options["content"]);
return ["error"=>false,"msg"=>"成功"];
}
public function saveUser($options){
if(empty($options["user_pass"])){
unset($options["user_pass"]);
}else{
$options["user_pass"] = md5(config('salt').$options["user_pass"]);
}
$userId = $options["uid"];
unset($options["uid"]);
try {
Db::name("users")->where("id",$userId)->update($options);
} catch (Exception $e) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
}
public function banUser($id,$uid){
if($id == $uid){
return ["error"=>1,"msg"=>"我的老伙计,你怎么能封禁你自己?"];
}
$userData = Db::name("users")->where("id",$id)->find();
$statusNew = $userData["user_status"] == 1 ? 0 : 1;
Db::name("users")->where("id",$id)->update(["user_status" => $statusNew]);
return ["error"=>200,"msg"=>"设置已保存"];
}
public function listGroup(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$this->pageData = Db::name("groups")
->order("id DESC")
->paginate($pageSize);
$this->dataTotal = Db::name("groups")
->order("id DESC")
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$this->pageNow = input("?get.page")?input("get.page"):1;
foreach ($this->listData as $key => $value) {
$this->listData[$key]["policy"] = Db::name("policy")->where("id",$value["policy_name"])->find();
$this->listData[$key]["user_num"] = Db::name("users")->where("user_group",$value["id"])->count();
}
}
public function addUser($options){
$options["user_pass"] = md5(config('salt').$options["user_pass"]);
if(Db::name('users')->where('user_email',$options["user_email"])->find() !=null){
return ["error" => true,"msg"=>"该邮箱已被注册"];
}
$sqlData = [
'user_email' => $options["user_email"],
'user_pass' => $options["user_pass"],
'user_status' => $options["user_status"],
'user_group' => $options["user_group"],
'group_primary' => $options["user_group"],
'user_date' => date("Y-m-d H:i:s"),
'user_nick' => $options["user_nick"],
'user_activation_key' => "n",
'used_storage' => 0,
'two_step'=>"0",
'webdav_key' =>$options["user_pass"],
'delay_time' =>0,
'avatar' => "default",
'profile' => true,
];
if(Db::name('users')->insert($sqlData)){
$userId = Db::name('users')->getLastInsID();
Db::name('folders')->insert( [
'folder_name' => '根目录',
'parent_folder' => 0,
'position' => '.',
'owner' => $userId,
'date' => date("Y-m-d H:i:s"),
'position_absolute' => '/',
]);
}
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>";
}
}
?>
+469
View File
@@ -0,0 +1,469 @@
<?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"])){
$deleteAction = @unlink($quenInfo["files"][$sqlData["file_index"]]["path"]);
@self::remove_directory(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
if(file_exists(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]))){
$task = new Task();
$task->taskName = "Delete remote download temp file";
$task->taskType = "deleteFolder";
$task->taskContent = json_encode([
"folder" => dirname($quenInfo["files"][$sqlData["file_index"]]["path"]),
]);
$task->userId = $this->uid;
$task->saveTask();
}
}
}
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);
}
}
?>
+146
View File
@@ -0,0 +1,146 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \app\index\model\LocalAdapter;
class Avatar extends Model{
public $avatarObj;
public $errorMsg;
public $fileName;
public $avatarKey;
public $userData;
public $avatarType;
public function __construct($new=false,$obj){
$this->avatarObj = $obj;
if(!$new){
$userData = Db::name("users")->where('id',$obj)->find();
$this->userData = $userData;
if(empty($userData)||$userData["avatar"] == "default"){
$this->avatarType = "default";
}else{
$avatarPrarm = explode(".",$userData["avatar"]);
$this->avatarType = $avatarPrarm[0];
$this->fileName = ltrim(ltrim($userData["avatar"],"g."),"f.");
}
}
}
public function SaveAvatar(){
$info = $this->avatarObj->validate(['size'=>2097152,'ext'=>'jpg,png,gif.bmp'])->rule('uniqid')->move(ROOT_PATH . 'public' . DS . 'avatars');
if($info){
$_path = ROOT_PATH.'public/avatars/'.$info->getSaveName();
$_img = new Image($_path);
$_img->thumb(200, 200);
$_img->output();
$_img = new Image($_path);
$_img->thumb(130, 130);
$_img->output("_130");
$_img = new Image($_path);
$_img->thumb(50, 50);
$_img->output("_50");
$this->fileName = $info->getSaveName();
return true;
}else{
$this->errorMsg=["result"=>"error","msg"=>$this->avatarObj->getError()];
return false;
}
}
public function bindUser($uid){
$this->avatarKey = "f.".$this->fileName;
Db::name("users")->where('id',$uid)->update(["avatar" => $this->avatarKey]);
}
public function Out($size){
switch ($this->avatarType) {
case 'f':
$this->outPutFile($size);
exit();
break;
case 'default':
$this->defaultAvatar($size);
exit();
break;
case 'g':
return $this->outGravatar($size);
break;
default:
# code...
break;
}
}
public function outPutFile($size){
switch ($size) {
case 's':
$siezSuffix = "_50";
break;
case 'm':
$siezSuffix = "_130";
break;
default:
$siezSuffix = "";
break;
}
$filePath = ROOT_PATH . 'public/avatars/' . $this->fileName.$siezSuffix;
if(file_exists($filePath)){
ob_end_clean();
header('Content-Type: '.LocalAdapter::getMimetype($filePath));
$fileObj = fopen($filePath,"r");
while(!feof($fileObj)){
echo fread($fileObj,2097152);
}
}else{
$this->defaultAvatar($siezSuffix);
}
}
public function outGravatar($size){
switch ($size) {
case 's':
$siezSuffix = "50";
break;
case 'm':
$siezSuffix = "130";
break;
default:
$siezSuffix = "200";
break;
}
ob_end_clean();
$gravatarServer = Option::getValue("gravatar_server");
return $gravatarServer.$this->fileName."?d=mm&s=".$siezSuffix;
}
public function defaultAvatar($size){
switch ($size) {
case 's':
$siezSuffix = "_50";
break;
case 'm':
$siezSuffix = "_130";
break;
default:
$siezSuffix = "";
break;
}
ob_end_clean();
$filePath = ROOT_PATH . 'static/img/default.png' .$siezSuffix;
header('Content-Type: '.LocalAdapter::getMimetype($filePath));
$fileObj = fopen($filePath,"r");
while(!feof($fileObj)){
echo fread($fileObj,2097152);
}
}
public function setGravatar(){
$this->avatarKey="g.".md5($this->userData["user_email"]);
Db::name("users")->where('id',$this->userData["id"])->update(["avatar" => $this->avatarKey]);
}
}
+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("");
}
}
?>
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace app\index\model;
use think\Model;
use Sabre\DAV;
use Sabre\DAV\Auth\Backend;
class BasicCallBack extends \Sabre\DAV\Auth\Backend\AbstractDigest {
/**
* Callback
*
* @var callable
*/
protected $callBack;
/**
* Creates the backend.
*
* A callback must be provided to handle checking the username and
* password.
*
* @param callable $callBack
* @return void
*/
function __construct(callable $callBack) {
$this->callBack = $callBack;
$this->realm = 'CloudreveWebDav';
}
public function getDigestHash($realm, $username){
$cb = $this->callBack;
return $cb($realm,$username);
}
}
+246
View File
@@ -0,0 +1,246 @@
<?php
namespace app\index\model;
require_once 'extend/Qiniu/functions.php';
use think\Model;
use think\Db;
use Qiniu\Auth;
use \app\index\model\Option;
use \app\index\model\FileManage;
use \app\index\model\UploadHandler;
class CallbackHandler extends Model{
public $CallbackData;
public $policyData;
public $userData;
public function __construct($data){
$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();
$this->policyData = Db::name('policy')->where('id',$CallbackSqlData['pid'])->find();
if(!$this->IsQiniuCallback($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"];
$picInfo = $picInfo==="null,null"?"":$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 ossHandler($auth,$pubKey){
if(!$this->IsOssCallback($auth,$pubKey)){
$this->setError("Undelegated Request");
}
$jsonData = json_decode($this->CallbackData,true);
$jsonData["fname"] = urldecode($jsonData["fname"]);
$jsonData["objname"] = urldecode($jsonData["objname"]);
$jsonData["path"] = urldecode($jsonData["path"]);
$CallbackSqlData = Db::name('callback')->where('callback_key',$jsonData['callbackkey'])->find();
$this->policyData = Db::name('policy')->where('id',$CallbackSqlData['pid'])->find();
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 upyunHandler($token,$date,$md5){
$this->policyData = Db::name("policy")->where("id",$this->CallbackData["ext-param"]["pid"])->find();
if(!$this->IsUpyunCallback($token,$date,$md5)){
$this->setError("Undelegated Request",false,true);
}
if(!FileManage::sotrageCheck($this->CallbackData["ext-param"]["uid"],$this->CallbackData["file_size"])){
FileManage::deleteFile($this->CallbackData["url"],$this->policyData);
$this->setError("空间容量不足",false,true);
}
$picInfo = empty($this->CallbackData["image-width"]) ? "" :$this->CallbackData["image-width"].",".$this->CallbackData["image-height"];
$fileNameExplode = explode("CLSUFF",$this->CallbackData["url"]);
$jsonData["fname"] = end($fileNameExplode);
$jsonData["objname"] = $this->CallbackData["url"];
$jsonData["path"] = $this->CallbackData["ext-param"]["path"];
$jsonData["fsize"] = $this->CallbackData["file_size"];
$addAction = FileManage::addFile($jsonData,$this->policyData,$this->CallbackData["ext-param"]["uid"],$picInfo);
if(!$addAction[0]){
FileManage::deleteFile($this->CallbackData["url"],$this->policyData);
$this->setError($addAction[1],false,true);
}
FileManage::storageCheckOut($this->CallbackData["ext-param"]["uid"],$jsonData["fsize"]);
$this->setSuccess($jsonData['fname']);
}
public function s3Handler($key){
$CallbackSqlData = Db::name('callback')->where('callback_key',$key)->find();
//删除callback记录
if(empty($CallbackSqlData)){
$this->setError("Undelegated Request",false,true);
}
Db::name('callback')->where('callback_key',$key)->delete();
$this->policyData = Db::name('policy')->where('id',$CallbackSqlData['pid'])->find();
$this->userData = Db::name('users')->where('id',$CallbackSqlData['uid'])->find();
$paths = explode("/",$this->CallbackData["key"]);
$jsonData["fname"] = end($paths);
$jsonData["objname"] = $this->CallbackData["key"];
$jsonData["path"] ="";
foreach ($paths as $key => $value) {
if($key == 0 || $key == count($paths)-1) continue;
$jsonData["path"].=$value.",";
}
$jsonData["path"] = rtrim($jsonData["path"],",");
$jsonData["fsize"] = $this->getS3FileInfo();
if(!$jsonData["fsize"]){
$this->setError("File not exist",false,true);
}
$jsonData["fsize"] = $jsonData["fsize"];
$picInfo = "";
$addAction = FileManage::addFile($jsonData,$this->policyData,$this->userData["id"],"");
if(!$addAction[0]){
FileManage::deleteFile($this->CallbackData["key"],$this->policyData);
$this->setError($addAction[1],false,true);
}
FileManage::storageCheckOut($this->userData["id"],$jsonData["fsize"]);
$this->setSuccess($jsonData['fname']);
}
private function getS3FileInfo(){
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyData["op_name"],
'endpoint' => $this->policyData["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyData["ak"],
'secret' => $this->policyData["sk"],
],
]);
try {
$returnVal = $s3->headObject([
'Bucket'=>$this->policyData["bucketname"],
'Key'=>$this->CallbackData["key"]
]);
} catch (Exception $e) {
return false;
}
return $returnVal["ContentLength"];
}
public function setSuccess($fname){
die(json_encode(["key"=> $fname]));
}
public function setError($text,$delete = false,$ignore=false){
header("HTTP/1.1 401 Unauthorized");
if(!$ignore){
$deletedFile = json_decode($this->CallbackData,true);
$fileNmae = $deletedFile['objname'];
if($delete){
FileManage::deleteFile($fileNmae,$this->policyData);
}
}
die(json_encode(["error"=> $text]));
}
private function isUpyunCallback($token,$date,$md5){
if(UploadHandler::upyunSign($this->policyData["op_name"],md5($this->policyData["op_pwd"]),"POST","/Callback/Upyun",$date,$md5) != $token){
return false;
}
return true;
}
public function IsQiniuCallback($httpHeader){
$auth = new Auth($this->policyData['ak'], $this->policyData['sk']);
$callbackBody = $this->CallbackData;
$contentType = 'application/json';
$authorization = $httpHeader;
$url = Option::getValue("siteUrl")."Callback/Qiniu";
$isQiniuCallback = $auth->verifyCallback($contentType, $authorization, $url,$callbackBody);
if ($isQiniuCallback) {
return true;
} else {
return false;
}
}
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");
exit();
}
$authorization = base64_decode($auth);
$pubKeyUrl = base64_decode($pubKey);
$pubOssKey = file_get_contents($pubKeyUrl);
if ($pubOssKey == ""){
return false;
}
$body = file_get_contents('php://input');
$authStr = '';
$path = $_SERVER['REQUEST_URI'];
$pos = strpos($path, '?');
if ($pos === false){
$authStr = urldecode($path)."\n".$body;
}else{
$authStr = urldecode(substr($path, 0, $pos)).substr($path, $pos, strlen($path) - $pos)."\n".$body;
}
$ok = openssl_verify($authStr, $authorization, $pubOssKey, OPENSSL_ALGO_MD5);
if ($ok == 1){
return true;
}else{
return false;
}
}
}
?>
+177
View File
@@ -0,0 +1,177 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
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{
public $cornTasks;
public $timeNow;
public $notifiedUid = [];
public function __construct(){
$this->cornTasks = Db::name('corn')->where('enable',1)->order('rank desc')->select();
$this->timeNow = time();
}
public function checkInterval($interval,$last){
return ($last+$interval)<= $this->timeNow ? true : false;
}
public function setComplete($name){
Db::name('corn')->where('name', $name)->update(['last_excute' => $this->timeNow]);
}
public function Doit(){
foreach ($this->cornTasks as $key => $value) {
switch ($value["name"]) {
case 'delete_unseful_chunks':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->deleteUnsefulChunks($value["interval_s"]);
}
break;
case 'delete_callback_data':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$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;
case 'delete_remote_downloader_failed_folder':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->deleteRemoteDownloaderFailedFolder($value["interval_s"]);
}
break;
default:
# code...
break;
}
}
}
private function deleteUnsefulChunks($interval){
echo("deleteUnsefulChunks...");
$chunkInfo = Db::name('chunks')->whereTime('time', '<', date('Y-m-d', time()-86400))->select();
$deleteList=[];
foreach ($chunkInfo as $key => $value) {
$fileSize = @filesize(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
@unlink(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
FileManage::storageGiveBack($value["user"],$fileSize?$fileSize:0);
$deleteList["$key"] = $value["id"];
}
Db::name('chunks')->where(['id' => ["in",$deleteList],])->delete();
$this->setComplete("delete_unseful_chunks");
echo("Complete<br>");
}
private function deleteCallbackData($interval){
echo("deleteCallbackData...");
Db::name("callback")->delete(true);
echo("Complete<br>");
$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 deleteRemoteDownloaderFailedFolder($interval){
echo("flushOnedriveToken...");
$toBeDeleted = Db::name("task")
->where("type","deleteFolder")
->where("status","todo")
->select();
$success=[];
$todo = [];
foreach ($toBeDeleted as $key => $value) {
$attr = json_decode($value["attr"],true);
if(file_exists($attr["folder"])){
self::remove_directory($attr["folder"]);
}
if(file_exists($attr["folder"])){
$todo[] = $value["id"];
}else{
$success[] = $value["id"];
}
}
Db::name("task")->where("id","in",$success)->update(["status"=>"success"]);
}
/**
* 移除整个目录
*
* @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);
}
}
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");
}
}
?>
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use app\index\model\User;
class DavAuth extends Model{
public $uid;
function __construct($id) {
$this->uid = $id;
}
public function __invoke($realm,$um){
$userData = Db::name("users")->where("id",$this->uid)->find();
if(empty($userData) || $userData["user_email"] != $um){
return null;
}
$userGroup = Db::name("groups")->where("id",$userData["user_group"])->find();
if(!$userGroup["webdav"]){
return null;
}
return $userData["webdav_key"];
}
}
?>
+192
View File
@@ -0,0 +1,192 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use Sabre\DAV;
use \app\index\model\FileManage;
use \app\index\model\User;
use \app\index\model\UploadHandler;
class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
private $myPath;
public $uid;
public $userObj;
function __construct($path) {
$ex = explode("/",$path);
$this->uid = $ex[0];
if(empty($this->uid)){
return false;
}
$t = strpos($path,$this->uid);
if($t == 0){
$this->myPath = substr($path,$t+strlen($this->uid));
}else{
$this->myPath = $path;
}
$this->myPath = empty($this->myPath) ? "/" : $this->myPath;
}
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();
$uploadHandle = new UploadHandler($groupData["policy_name"],$this->uid);
$allowedExt = UploadHandler::getAllowedExt(json_decode($policyData["filetype"],true));
if(!empty($allowedExt)){
$ex = explode(".",$name);
$fileSuffix = end($ex);
if(!in_array($fileSuffix, explode(",",$allowedExt))){
throw new DAV\Exception\InvalidResourceType('File type not allowed');
}
}
if($policyData["policy_type"] !="local"){
throw new DAV\Exception\Forbidden('Poliyc not supported yet');
}
$fileSize = fstat($data)["size"];
if(empty($fileSize)){
$fileSize = -1;
}
if($fileSize>$policyData["max_size"]){
throw new DAV\Exception\InsufficientStorage('File is to large');
}
if(!FileManage::sotrageCheck($this->uid,$fileSize)){
throw new DAV\Exception\InsufficientStorage('Quota is not enough');
}
if($policyData['autoname']){
$fileName = $uploadHandle->getObjName($policyData['namerule'],"local",$name);
}else{
$fileName = $name;
}
$generatePath = $uploadHandle->getDirName($policyData['dirrule']);
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath;
if(!file_exists($savePath)){
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,
"objname" => $generatePath."/".$fileName,
"fsize" => $fileSize,
);
@list($width, $height, $type, $attr) = getimagesize(rtrim($savePath, DS).DS.$fileName);
$picInfo = empty($width)?" ":$width.",".$height;
$addAction = FileManage::addFile($jsonData,$policyData,$this->uid,$picInfo);
if(!$addAction[0]){
unlink($savePath."/".$fileName);
throw new DAV\Exception\Conflict($addAction[1]);
}
FileManage::storageCheckOut($this->uid,$jsonData["fsize"]);
//echo json_encode(array("key" => $info["name"]));
}
function getQuotaInfo() {
$this->userObj = new User($this->uid,"",true);
$quotaInfo = json_decode($this->userObj->getMemory(true),true);
return [
$quotaInfo["used"],
$quotaInfo["total"]
];
}
function setName($name){
$reqPath = $this->myPath;
$ex = explode("/",$reqPath);
$newPath = rtrim(dirname($reqPath) == "\\" ?"/":dirname($reqPath),"/")."/".$name;
$renameAction = json_decode(FileManage::RenameHandler($reqPath,$newPath,$this->uid,true),true);
if(!$renameAction["result"]["success"]){
throw new DAV\Exception\InvalidResourceType($renameAction["result"]["error"]);
}
}
function getChildren() {
$children = array();
$fileList = Db::name('files')->where('upload_user',$this->uid)->where('dir',$this->myPath)->select();
$dirList = Db::name('folders')->where('owner',$this->uid)->where('position',$this->myPath)->select();
foreach($fileList as $node) {
// Ignoring files staring with .
$children[] = $this->getChildFile($node,false);
}
foreach($dirList as $node) {
// Ignoring files staring with .
$children[] = $this->getChildDir($node,true);
}
return $children;
}
function getChildFile($name){
$path = $this->uid.rtrim($this->myPath,"/") . '/' . $name["orign_name"];
return new Objects($path);
}
function getChildDir($name){
$path = $this->uid.rtrim($this->myPath,"/") . '/' . $name["folder_name"];
return new Directory($path);
}
function delete(){
foreach ($this->getChildren() as $child) $child->delete();
FileManage::DirDeleteHandler([0=>$this->myPath],$this->uid);
}
function getChild($name) {
$name = str_replace(" ","",$name);
if(!$this->childExists($name)){
throw new DAV\Exception\NotFound('File with name ' . $name . ' could not be located');
}
$path = $this->uid.rtrim($this->myPath,"/") . '/' . $name;
if($this->findDir(rtrim($this->myPath,"/") . '/' . $name)){
$returnObj = new Directory($path);
return $returnObj;
}else{
return new Objects($path);
}
}
function childExists($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;
}
return false;
}
public function findDir($path){
if($path == "/"){
return true;
}
$explode = explode("/",$path);
$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",getDirName($path))->find();
if(empty($dirData)){
return false;
}
return true;
}
function getName() {
$explode = explode("/", $this->myPath);
return end($explode);
}
function createDirectory($name) {
$createAction = FileManage::createFolder($name,$this->myPath,$this->uid);
if(!$createAction["result"]["success"]){
die($this->myPath);
throw new DAV\Exception\InvalidResourceType($createAction["result"]["error"]);
}
}
}
?>
+941
View File
@@ -0,0 +1,941 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use think\Validate;
use \app\index\model\Option;
class FileManage extends Model{
public $filePath;
public $fileData;
public $userID;
public $userData;
public $policyData;
public $deleteStatus = true;
private $adapter;
/**
* construct function
*
* @param string $path 文件路径/文件ID
* @param int $uid 用户ID
* @param boolean $byId 是否根据文件ID寻找文件
*/
public function __construct($path,$uid,$byId=false){
if($byId){
$fileRecord = Db::name('files')->where('id',$path)->find();
$this->filePath = rtrim($fileRecord["dir"],"/")."/".$fileRecord["orign_name"];
}else{
$this->filePath = $path;
$fileInfo = $this->getFileName($path);
$fileName = $fileInfo[0];
$path = $fileInfo[1];
$fileRecord = Db::name('files')->where('upload_user',$uid)->where('orign_name',$fileName)->where('dir',$path)->find();
}
if (empty($fileRecord)){
die('{ "result": { "success": false, "error": "文件不存在" } }');
}
$this->fileData = $fileRecord;
$this->userID = $uid;
$this->userData = Db::name('users')->where('id',$uid)->find();
$this->policyData = Db::name('policy')->where('id',$this->fileData["policy_id"])->find();
switch ($this->policyData["policy_type"]) {
case 'local':
$this->adapter = new \app\index\model\LocalAdapter($this->fileData,$this->policyData,$this->userData);
break;
case 'qiniu':
$this->adapter = new \app\index\model\QiniuAdapter($this->fileData,$this->policyData,$this->userData);
break;
case 'oss':
$this->adapter = new \app\index\model\OssAdapter($this->fileData,$this->policyData,$this->userData);
break;
case 'upyun':
$this->adapter = new \app\index\model\UpyunAdapter($this->fileData,$this->policyData,$this->userData);
break;
case 's3':
$this->adapter = new \app\index\model\S3Adapter($this->fileData,$this->policyData,$this->userData);
break;
case 'remote':
$this->adapter = new \app\index\model\RemoteAdapter($this->fileData,$this->policyData,$this->userData);
break;
case 'onedrive':
$this->adapter = new \app\index\model\OnedriveAdapter($this->fileData,$this->policyData,$this->userData);
break;
default:
# code...
break;
}
}
/**
* 获取文件外链地址
*
* @return void
*/
public function Source(){
if(!$this->policyData["origin_link"]){
die('{"url":"此文件不支持获取源文件URL"}');
}else{
echo ('{"url":"'.$this->policyData["url"].$this->fileData["pre_name"].'"}');
}
}
/**
* 获取可编辑文件内容
*
* @return void
*/
public function getContent(){
$sizeLimit=(int)Option::getValue("maxEditSize");
if($this->fileData["size"]>$sizeLimit){
die('{ "result": { "success": false, "error": "您当前用户组最大可编辑'.$sizeLimit.'字节的文件"} }');
}else{
try{
$fileContent = $this->adapter->getFileContent();
}catch(\Exception $e){
die('{ "result": { "success": false, "error": "'.$e->getMessage().'"} }');
}
$fileContent = $this->adapter->getFileContent();
$result["result"] = $fileContent;
if(empty(json_encode($result))){
$result["result"] = iconv('gb2312','utf-8',$fileContent);
}
echo json_encode($result);
}
}
/**
* 保存可编辑文件
*
* @param string $content 要保存的文件内容
* @return void
*/
public function saveContent($content){
$contentSize = strlen($content);
$originSize = $this->fileData["size"];
if(!FileManage::sotrageCheck($this->userID,$contentSize)){
die('{ "result": { "success": false, "error": "空间容量不足" } }');
}
$this->adapter->saveContent($content);
FileManage::storageGiveBack($this->userID,$originSize);
FileManage::storageCheckOut($this->userID,$contentSize);
Db::name('files')->where('id', $this->fileData["id"])->update(['size' => $contentSize]);
echo ('{ "result": { "success": true} }');
}
/**
* 文件名合法性初步检查
*
* @param string $value 文件名
* @return bool 检查结果
*/
static function fileNameValidate($value){
$validate = new Validate([
'val' => 'require|max:250',
'val' => 'chsDash'
]);
$data = [
'val' => $value
];
if (!$validate->check($data)) {
return false;
}
return true;
}
/**
* 处理重命名
*
* @param string $fname 原文件路径
* @param string $new 新文件路径
* @param int $uid 用户ID
* @param boolean $notEcho 过程中是否不直接输出结果
* @return mixed
*/
static function RenameHandler($fname,$new,$uid,$notEcho = false){
$folderTmp = $new;
$originFolder = $fname;
$new = str_replace("/", "", self::getFileName($new)[0]);
if(!$notEcho){
$newToBeVerify = str_replace(" ", "", $new);
}
//检查是否全为空格
$varifyExplode = explode(".",$newToBeVerify);
$isFullBlackspace = false;
foreach ($varifyExplode as $key => $value) {
if($value == ""){
$isFullBlackspace = true;
break;
}
}
$toBeValidated = str_replace(".","",$newToBeVerify);
if(!self::fileNameValidate($toBeValidated) || $isFullBlackspace){
if($notEcho){
return '{ "result": { "success": false, "error": "文件名只支持汉字、字母、数字和下划线_及破折号-" } }';
}
die('{ "result": { "success": false, "error": "文件名只支持汉字、字母、数字和下划线_及破折号-" } }');
}
$path = self::getFileName($fname)[1];
$fname = self::getFileName($fname)[0];
$fileRecord = Db::name('files')->where('upload_user',$uid)->where('orign_name',$fname)->where('dir',$path)->find();
if (empty($new)){
if($notEcho){
return '{ "result": { "success": false, "error": "文件重名或文件名非法" } }';
}
die('{ "result": { "success": false, "error": "文件重名或文件名非法" } }');
}
if(empty($fileRecord)){
self::folderRename($originFolder,$folderTmp,$uid,$notEcho);
die();
}
$newSuffix = explode(".",$new);
$originSuffix = explode(".",$fileRecord["orign_name"]);
if(end($originSuffix) != end($newSuffix)){
if($notEcho){
return '{ "result": { "success": false, "error": "请不要更改文件扩展名" } }';
}
die('{ "result": { "success": false, "error": "请不要更改文件扩展名" } }');
}
Db::name('files')->where([
'upload_user' => $uid,
'dir' => $path,
'orign_name' =>$fname,
])->setField('orign_name', $new);
if($notEcho){
return '{ "result": { "success": true} }';
}
echo ('{ "result": { "success": true} }');
}
/**
* 处理目录重命名
*
* @param string $fname 原文件路径
* @param string $new 新文件路径
* @param int $uid 用户ID
* @param boolean $notEcho 过程中是否不直接输出结果
* @return void
*/
static function folderRename($fname,$new,$uid,$notEcho = false){
$newTmp = $new;
$nerFolderTmp = explode("/",$new);
$new = array_pop($nerFolderTmp);
$oldFolderTmp = explode("/",$fname);
$old = array_pop($oldFolderTmp);
if(!self::fileNameValidate($new)){
if($notEcho){
return '{ "result": { "success": false, "error": "目录名只支持数字、字母、下划线" } }';
}
die('{ "result": { "success": false, "error": "目录名只支持数字、字母、下划线" } }');
}
$folderRecord = Db::name('folders')->where('owner',$uid)->where('position_absolute',$fname)->find();
if(empty($folderRecord)){
if($notEcho){
return '{ "result": { "success": false, "error": "目录不存在" } }';
}
die('{ "result": { "success": false, "error": "目录不存在" } }');
}
$newPositionAbsolute = substr($fname, 0, strrpos( $fname, '/'))."/".$new;
Db::name('folders')->where('owner',$uid)->where('position_absolute',$fname)->update([
'folder_name' => $new,
'position_absolute' => $newPositionAbsolute,
]);
$childFolder = Db::name('folders')->where('owner',$uid)->where('position',"like",$fname."%")->select();
foreach ($childFolder as $key => $value) {
$tmpPositionAbsolute = "";
$tmpPosition = "";
$pos = strpos($value["position_absolute"], $fname);
if ($pos === false) {
$tmpPositionAbsolute = $value["position_absolute"];
}
$tmpPositionAbsolute = substr_replace($value["position_absolute"], $newTmp, $pos, strlen($fname));
$pos = strpos($value["position"], $fname);
if ($pos === false) {
$tmpPosition = $value["position"];
}
$tmpPosition = substr_replace($value["position"], $newTmp, $pos, strlen($fname));
Db::name('folders')->where('id',$value["id"])->update([
'position_absolute' => $tmpPositionAbsolute,
'position' =>$tmpPosition,
]);
}
$childFiles = Db::name('files')->where('upload_user',$uid)->where('dir',"like",$fname."%")->select();
foreach ($childFiles as $key => $value) {
$tmpPosition = "";
$pos = strpos($value["dir"], $fname);
if ($pos === false) {
$tmpPosition = $value["dir"];
}
$tmpPosition = substr_replace($value["dir"], $newTmp, $pos, strlen($fname));
Db::name('files')->where('id',$value["id"])->update([
'dir' =>$tmpPosition,
]);
}
if($notEcho){
return '{ "result": { "success": true} }';
}
echo ('{ "result": { "success": true} }');
}
/**
* 根据文件路径获取文件名和父目录路径
*
* @param string 文件路径
* @return array
*/
static function getFileName($path){
$pathSplit = explode("/",$path);
$fileName = end($pathSplit);
$pathSplitDelete = array_pop($pathSplit);
$path="";
foreach ($pathSplit as $key => $value) {
if (empty($value)){
}else{
$path =$path."/".$value;
}
}
$path = empty($path)?"/":$path;
return [$fileName,$path];
}
/**
* 处理文件预览
*
* @param boolean $isAdmin 是否为管理员预览
* @return array 重定向信息
*/
public function PreviewHandler($isAdmin=false){
return $this->adapter->Preview($isAdmin);
}
/**
* 获取图像缩略图
*
* @return array 重定向信息
*/
public function getThumb(){
return $this->adapter->getThumb();
}
/**
* 处理文件下载
*
* @param boolean $isAdmin 是否为管理员请求
* @return array 文件下载URL
*/
public function Download($isAdmin=false){
return $this->adapter->Download($isAdmin);
}
/**
* 处理目录删除
*
* @param string $path 目录路径
* @param int $uid 用户ID
* @return void
*/
static function DirDeleteHandler($path,$uid){
global $toBeDeleteDir;
global $toBeDeleteFile;
$toBeDeleteDir = [];
$toBeDeleteFile = [];
foreach ($path as $key => $value) {
array_push($toBeDeleteDir,$value);
}
foreach ($path as $key => $value) {
self::listToBeDelete($value,$uid);
}
if(!empty($toBeDeleteFile)){
self::DeleteHandler($toBeDeleteFile,$uid);
}
if(!empty($toBeDeleteDir)){
self::deleteDir($toBeDeleteDir,$uid);
}
}
/**
* 列出待删除文件或目录
*
* @param string $path 对象路径
* @param int $uid 用户ID
* @return void
*/
static function listToBeDelete($path,$uid){
global $toBeDeleteDir;
global $toBeDeleteFile;
$fileData = Db::name('files')->where([
'dir' => $path,
'upload_user' => $uid,
])->select();
foreach ($fileData as $key => $value) {
array_push($toBeDeleteFile,$path."/".$value["orign_name"]);
}
$dirData = Db::name('folders')->where([
'position' => $path,
'owner' => $uid,
])->select();
foreach ($dirData as $key => $value) {
array_push($toBeDeleteDir,$value["position_absolute"]);
self::listToBeDelete($value["position_absolute"],$uid);
}
}
/**
* 删除目录
*
* @param string $path 目录路径
* @param int $uid 用户ID
* @return void
*/
static function deleteDir($path,$uid){
Db::name('folders')
->where("owner",$uid)
->where([
'position_absolute' => ["in",$path],
])->delete();
}
/**
* 处理删除请求
*
* @param string $path 路径
* @param int $uid 用户ID
* @return array
*/
static function DeleteHandler($path,$uid){
if(empty($path)){
return ["result"=>["success"=>true,"error"=>null]];
}
foreach ($path as $key => $value) {
$fileInfo = self::getFileName($value);
$fileName = $fileInfo[0];
$filePath = $fileInfo[1];
$fileNames[$key] = $fileName;
$filePathes[$key] = $filePath;
}
$fileData = Db::name('files')->where([
'orign_name' => ["in",$fileNames],
'dir' => ["in",$filePathes],
'upload_user' => $uid,
])->select();
$fileListTemp=[];
$uniquePolicy = self::uniqueArray($fileData);
foreach ($fileData as $key => $value) {
if(empty($fileListTemp[$value["policy_id"]])){
$fileListTemp[$value["policy_id"]] = [];
}
array_push($fileListTemp[$value["policy_id"]],$value);
}
foreach ($fileListTemp as $key => $value) {
if(in_array($key,$uniquePolicy["qiniuList"])){
QiniuAdapter::DeleteFile($value,$uniquePolicy["qiniuPolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}else if(in_array($key,$uniquePolicy["localList"])){
LocalAdapter::DeleteFile($value,$uniquePolicy["localPolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}else if(in_array($key,$uniquePolicy["ossList"])){
OssAdapter::DeleteFile($value,$uniquePolicy["ossPolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}else if(in_array($key,$uniquePolicy["upyunList"])){
UpyunAdapter::DeleteFile($value,$uniquePolicy["upyunPolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}else if(in_array($key,$uniquePolicy["s3List"])){
S3Adapter::DeleteFile($value,$uniquePolicy["s3PolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}else if(in_array($key,$uniquePolicy["remoteList"])){
RemoteAdapter::DeleteFile($value,$uniquePolicy["remotePolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}else if(in_array($key,$uniquePolicy["onedriveList"])){
OnedriveAdapter::DeleteFile($value,$uniquePolicy["onedrivePolicyData"][$key][0]);
self::deleteFileRecord(array_column($value, 'id'),array_sum(array_column($value, 'size')),$value[0]["upload_user"]);
}
}
return ["result"=>["success"=>true,"error"=>null]];
}
/**
* 处理移动
*
* @param array $file 文件路径列表
* @param array $dir 目录路径列表
* @param string $new 新路径
* @param int $uid 用户ID
* @return void
*/
static function MoveHandler($file,$dir,$new,$uid){
if(in_array($new,$dir)){
die('{ "result": { "success": false, "error": "不能移动目录到自身" } }');
}
$newFolder=Db::name('folders')->where('owner',$uid)->where('position_absolute',$new)->find();
if($newFolder== null){
die('{ "result": { "success": false, "error": "目录不存在" } }');
}
$moveName=[];
$movePath=[];
foreach ($file as $key => $value) {
$fileInfo = self::getFileName($value);
$moveName[$key] = $fileInfo[0];
$movePath[$key] = $fileInfo[1];
}
$dirName=[];
$dirPa=[];
foreach ($dir as $key => $value) {
$dirInfo = self::getFileName($value);
$dirName[$key] = $dirInfo[0];
$dirPar[$key] = $dirInfo[1];
}
$nameCheck = Db::name('files')->where([
'upload_user' => $uid,
'dir' => $new,
'orign_name' =>["in",$moveName],
])->find();
$dirNameCheck = array_merge($dirName,$moveName);
$dirCheck = Db::name('folders')->where([
'owner' => $uid,
'position' => $new,
'folder_name' =>["in",$dirNameCheck],
])->find();
if($nameCheck || $dirCheck){
die('{ "result": { "success": false, "error": "文件名冲突,请检查是否重名" } }');
}
if(!empty($dir)){
die('{ "result": { "success": false, "error": "暂不支持移动目录" } }');
}
Db::name('files')->where([
'upload_user' => $uid,
'dir' => ["in",$movePath],
'orign_name' =>["in",$moveName],
])->update([
'dir'=> $new,
"parent_folder" => $newFolder["id"]
]);
echo ('{ "result": { "success": true} }');
}
/**
* ToDo 移动文件
*
* @param array $file
* @param string $path
* @return void
*/
static function moveFile($file,$path){
}
static function deleteFileRecord($id,$size,$uid){
Db::name('files')->where([
'id' => ["in",$id],
])->delete();
Db::name('shares')
->where(['owner' => $uid])
->where(['source_type' => "file"])
->where(['source_name' => ["in",$id],])
->delete();
Db::name('users')->where([
'id' => $uid,
])->setDec('used_storage', $size);
}
static function filterFile($keyWords,$uid){
switch ($keyWords) {
case '{filterType:video}':
$fileList = Db::name('files')
->where('upload_user',$uid)
->where('orign_name',"like","%.mp4")
->whereOr('orign_name',"like","%.flv")
->whereOr('orign_name',"like","%.avi")
->whereOr('orign_name',"like","%.wmv")
->whereOr('orign_name',"like","%.mkv")
->whereOr('orign_name',"like","%.rm")
->whereOr('orign_name',"like","%.rmvb")
->whereOr('orign_name',"like","%.mov")
->whereOr('orign_name',"like","%.ogv")
->select();
break;
case '{filterType:audio}':
$fileList = Db::name('files')
->where('upload_user',$uid)
->where('orign_name',"like","%.mp3")
->whereOr('orign_name',"like","%.flac")
->whereOr('orign_name',"like","%.ape")
->whereOr('orign_name',"like","%.wav")
->whereOr('orign_name',"like","%.acc")
->whereOr('orign_name',"like","%.ogg")
->select();
break;
case '{filterType:image}':
$fileList = Db::name('files')
->where('upload_user',$uid)
->where('orign_name',"like","%.bmp")
->whereOr('orign_name',"like","%.flac")
->whereOr('orign_name',"like","%.iff")
->whereOr('orign_name',"like","%.png")
->whereOr('orign_name',"like","%.gif")
->whereOr('orign_name',"like","%.jpg")
->whereOr('orign_name',"like","%.jpge")
->whereOr('orign_name',"like","%.psd")
->whereOr('orign_name',"like","%.svg")
->whereOr('orign_name',"like","%.webp")
->select();
break;
case '{filterType:doc}':
$fileList = Db::name('files')
->where('upload_user',$uid)
->where('orign_name',"like","%.txt")
->whereOr('orign_name',"like","%.md")
->whereOr('orign_name',"like","%.pdf")
->whereOr('orign_name',"like","%.doc")
->whereOr('orign_name',"like","%.docx")
->whereOr('orign_name',"like","%.ppt")
->whereOr('orign_name',"like","%.pptx")
->whereOr('orign_name',"like","%.xls")
->whereOr('orign_name',"like","%.xlsx")
->select();
break;
default:
$fileList = [];
break;
}
return $fileList;
}
static function searchFile($keyWords,$uid){
if (0 === strpos($keyWords, '{filterType:')) {
$fileList = self::filterFile($keyWords,$uid);
}else{
$fileList = Db::name('files')
->where('upload_user',$uid)
->where('orign_name',"like","%$keyWords%")
->select();
}
$count= 0;
$fileListData=[
"result"=>[],
];
foreach ($fileList as $key => $value) {
$fileListData['result'][$count]['name'] = $value['orign_name'];
$fileListData['result'][$count]['rights'] = "drwxr-xr-x";
$fileListData['result'][$count]['size'] = $value['size'];
$fileListData['result'][$count]['date'] = $value['upload_date'];
$fileListData['result'][$count]['type'] = 'file';
$fileListData['result'][$count]['name2'] = $value["dir"];
$fileListData['result'][$count]['id'] = $value["id"];
$fileListData['result'][$count]['pic'] = $value["pic_info"];
$fileListData['result'][$count]['path'] = $value['dir'];
$count++;
}
return $fileListData;
}
/**
* 列出文件
*
* @param 路径 $path
* @param 用户UID $uid
* @param boolean $isShare 是否为分享模式下列出文件
* @return void
*/
static function ListFile($path,$uid,$isShare=false,$originPath=null){
$fileList = Db::name('files')->where('upload_user',$uid)->where('dir',$path)->select();
$dirList = Db::name('folders')->where('owner',$uid)->where('position',$path)->select();
$count= 0;
$fileListData=[
"result"=>[],
];
foreach ($dirList as $key => $value) {
$fileListData['result'][$count]['name'] = $value['folder_name'];
$fileListData['result'][$count]['rights'] = "drwxr-xr-x";
$fileListData['result'][$count]['size'] = "0";
$fileListData['result'][$count]['date'] = $value['date'];
$fileListData['result'][$count]['type'] = 'dir';
$fileListData['result'][$count]['name2'] = "";
$fileListData['result'][$count]['id'] = $value['id'];
$fileListData['result'][$count]['pic'] = "";
$fileListData['result'][$count]['path'] = $value['position'];
if($isShare){
if (substr($value['position'], 0, strlen($originPath)) == $originPath) {
$value['position'] = substr($value['position'], strlen($originPath));
}
$fileListData['result'][$count]['path'] = ($value['position']=="")?"/":$value['position'];
}
$count++;
}
foreach ($fileList as $key => $value) {
$fileListData['result'][$count]['name'] = $value['orign_name'];
$fileListData['result'][$count]['rights'] = "drwxr-xr-x";
$fileListData['result'][$count]['size'] = $value['size'];
$fileListData['result'][$count]['date'] = $value['upload_date'];
$fileListData['result'][$count]['type'] = 'file';
$fileListData['result'][$count]['id'] = $value["id"];
$fileListData['result'][$count]['pic'] = $value["pic_info"];
$fileListData['result'][$count]['path'] = $value['dir'];
if($isShare){
if (substr($value['dir'], 0, strlen($originPath)) == $originPath) {
$value['dir'] = substr($value['dir'], strlen($originPath));
}
$fileListData['result'][$count]['path'] = ($value['dir']=="")?"/":$value['dir'];
}
$count++;
}
return $fileListData;
}
static function listPic($path,$uid,$url="/File/Preview?"){
$firstPreview = self::getFileName($path);
$path=$firstPreview[1];
$fileList = Db::name('files')
->where('upload_user',$uid)
->where('dir',$path)
->where('pic_info',"<>"," ")
->where('pic_info',"<>","0,0")
->where('pic_info',"<>","null,null")
->select();
$count= 0;
$fileListData=[];
foreach ($fileList as $key => $value) {
if($value["orign_name"] == $firstPreview[0]){
$previewPicInfo = explode(",",$value["pic_info"]);
$previewSrc = $url."action=preview&path=".urlencode($path."/".$value["orign_name"]);
}else{
$picInfo = explode(",",$value["pic_info"]);
$fileListData[$count]['src'] = $url."action=preview&path=".$path."/".$value["orign_name"];
$fileListData[$count]['w'] = 0;
$fileListData[$count]['h'] = 0;
$fileListData[$count]['title'] = $value["orign_name"];
$count++;
}
}
array_unshift($fileListData,array(
'src' => $previewSrc,
'w' => 0,
'h' => 0,
'title' => $firstPreview[0],
));
return $fileListData;
}
/**
* [createFolder description]
* @param [type] $dirName [description]
* @param [type] $dirPosition [description]
* @param [type] $uid [description]
* @return [type] [description]
*/
static function createFolder($dirName,$dirPosition,$uid){
$dirName = str_replace(" ","",$dirName);
$dirName = str_replace("/","",$dirName);
if(empty($dirName)){
return ["result"=>["success"=>false,"error"=>"目录名不能为空"]];
}
if(Db::name('folders')->where('position_absolute',$dirPosition)->where('owner',$uid)->find() ==null || Db::name('folders')->where('owner',$uid)->where('position',$dirPosition)->where('folder_name',$dirName)->find() !=null || Db::name('files')->where('upload_date',$uid)->where('dir',$dirPosition)->where('pre_name',$dirName)->find() !=null){
return ["result"=>["success"=>false,"error"=>"路径不存在或文件已存在"]];
}
$sqlData = [
'folder_name' => $dirName,
'parent_folder' => Db::name('folders')->where('position_absolute',$dirPosition)->value('id'),
'position' => $dirPosition,
'owner' => $uid,
'date' => date("Y-m-d H:i:s"),
'position_absolute' => ($dirPosition == "/")?($dirPosition.$dirName):($dirPosition."/".$dirName),
];
if(Db::name('folders')->insert($sqlData)){
return ["result"=>["success"=>true,"error"=>null]];
}
}
static function getTotalStorage($uid){
$userData = Db::name('users')->where('id',$uid)->find();
$basicStronge = Db::name('groups')->where('id',$userData['user_group'])->find();
$addOnStorage = Db::name('storage_pack')
->where('uid',$uid)
->where('dlay_time',">",time())
->sum('pack_size');
return $addOnStorage+$basicStronge["max_storage"];
}
static function getUsedStorage($uid){
$userData = Db::name('users')->where('id',$uid)->find();
return $userData['used_storage'];
}
static function sotrageCheck($uid,$fsize){
$totalStorage = self::getTotalStorage($uid);
$usedStorage = self::getUsedStorage($uid);
return ($totalStorage > ($usedStorage + $fsize)) ? True : False;
}
static function storageCheckOut($uid,$size){
Db::name('users')->where('id',$uid)->setInc('used_storage',$size);
}
static function storageGiveBack($uid,$size){
Db::name('users')->where('id',$uid)->setDec('used_storage',$size);
}
static function addFile($jsonData,$policyData,$uid,$picInfo=" "){
$dir = "/".str_replace(",","/",$jsonData['path']);
$fname = $jsonData['fname'];
if(self::isExist($dir,$fname,$uid)){
return[false,"文件已存在"];
}
$folderBelong = Db::name('folders')->where('owner',$uid)->where('position_absolute',$dir)->find();
if($folderBelong ==null){
return[false,"目录不存在"];
}
$sqlData = [
'orign_name' => $jsonData['fname'],
'pre_name' => $jsonData['objname'],
'upload_user' => $uid,
'size' => $jsonData['fsize'],
'upload_date' => date("Y-m-d H:i:s"),
'parent_folder' => $folderBelong['id'],
'policy_id' => $policyData['id'],
'dir' => $dir,
'pic_info' => $picInfo,
];
if(Db::name('files')->insert($sqlData)){
return [true,"上传成功"];
}
}
static function isExist($dir,$fname,$uid){
if(Db::name('files')->where('upload_user',$uid)->where('dir',$dir)->where('orign_name',$fname)->find() !=null){
return true;
}else{
return false;
}
}
static function deleteFile($fname,$policy){
switch ($policy['policy_type']) {
case 'qiniu':
return QiniuAdapter::deleteSingle($fname,$policy);
break;
case 'oss':
return OssAdapter::deleteOssFile($fname,$policy);
break;
case 'upyun':
return UpyunAdapter::deleteUpyunFile($fname,$policy);
break;
case 's3':
return S3Adapter::deleteS3File($fname,$policy);
break;
default:
# code...
break;
}
}
static function uniqueArray($data = array()){
$tempList = [];
$qiniuList = [];
$qiniuPolicyData = [];
$localList = [];
$localPolicyData = [];
$ossList = [];
$ossPolicyData = [];
$upyunList = [];
$upyunPolicyData = [];
$s3List = [];
$s3PolicyData = [];
$remoteList = [];
$remotePolicyData = [];
$onedriveList = [];
$onedrivePolicyData = [];
foreach ($data as $key => $value) {
if(!in_array($value['policy_id'],$tempList)){
array_push($tempList,$value['policy_id']);
$policyTempData = Db::name('policy')->where('id',$value['policy_id'])->find();
switch ($policyTempData["policy_type"]) {
case 'qiniu':
array_push($qiniuList,$value['policy_id']);
if(empty($qiniuPolicyData[$value['policy_id']])){
$qiniuPolicyData[$value['policy_id']] = [];
}
array_push($qiniuPolicyData[$value['policy_id']],$policyTempData);
break;
case 'local':
array_push($localList,$value['policy_id']);
if(empty($localPolicyData[$value['policy_id']])){
$localPolicyData[$value['policy_id']] = [];
}
array_push($localPolicyData[$value['policy_id']],$policyTempData);
break;
case 'oss':
array_push($ossList,$value['policy_id']);
if(empty($ossPolicyData[$value['policy_id']])){
$ossPolicyData[$value['policy_id']] = [];
}
array_push($ossPolicyData[$value['policy_id']],$policyTempData);
break;
case 'upyun':
array_push($upyunList,$value['policy_id']);
if(empty($upyunPolicyData[$value['policy_id']])){
$upyunPolicyData[$value['policy_id']] = [];
}
array_push($upyunPolicyData[$value['policy_id']],$policyTempData);
break;
case 's3':
array_push($s3List,$value['policy_id']);
if(empty($s3PolicyData[$value['policy_id']])){
$s3PolicyData[$value['policy_id']] = [];
}
array_push($s3PolicyData[$value['policy_id']],$policyTempData);
break;
case 'remote':
array_push($remoteList,$value['policy_id']);
if(empty($remotePolicyData[$value['policy_id']])){
$remotePolicyData[$value['policy_id']] = [];
}
array_push($remotePolicyData[$value['policy_id']],$policyTempData);
break;
case 'onedrive':
array_push($onedriveList,$value['policy_id']);
if(empty($onedrivePolicyData[$value['policy_id']])){
$onedrivePolicyData[$value['policy_id']] = [];
}
array_push($onedrivePolicyData[$value['policy_id']],$policyTempData);
break;
default:
# code...
break;
}
}
}
$returenValue=array(
'policyId' => $tempList ,
'qiniuList' => $qiniuList,
'qiniuPolicyData' => $qiniuPolicyData,
'localList' => $localList,
'localPolicyData' => $localPolicyData,
'ossList' => $ossList,
'ossPolicyData' => $ossPolicyData,
'upyunList' => $upyunList,
'upyunPolicyData' => $upyunPolicyData,
's3List' => $s3List,
's3PolicyData' => $s3PolicyData,
'remoteList' => $remoteList,
'remotePolicyData' => $remotePolicyData,
'onedriveList' => $onedriveList,
'onedrivePolicyData' => $onedrivePolicyData,
);
return $returenValue;
}
public function signTmpUrl(){
return $this->adapter->signTmpUrl()[1];
}
}
?>
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace app\index\model;
use think\Model;
class Image extends Model{
private $file; //图片地址
private $width; //图片长度
private $height; //图片长度
private $itype; //图片类型
private $img; //原图的资源句柄
private $new; //新图的资源句柄
//构造方法,初始化
public function __construct($_file) {
$this->file = $_file;
list($this->width, $this->height, $this->itype) = getimagesize($this->file);
$this->img = $this->getFromImg($this->file, $this->itype);
}
//缩略图(固定长高容器,图像等比例,扩容填充,裁剪)[固定了大小,不失真,不变形]
public function thumb($new_width = 0,$new_height = 0) {
if (empty($new_width) && empty($new_height)) {
$new_width = $this->width;
$new_height = $this->height;
}
if (!is_numeric($new_width) || !is_numeric($new_height)) {
$new_width = $this->width;
$new_height = $this->height;
}
//创建一个容器
$_n_w = $new_width;
$_n_h = $new_height;
//创建裁剪点
$_cut_width = 0;
$_cut_height = 0;
if ($this->width < $this->height) {
$new_width = ($new_height / $this->height) * $this->width;
} else {
$new_height = ($new_width / $this->width) * $this->height;
}
if ($new_width < $_n_w) { //如果新高度小于新容器高度
$r = $_n_w / $new_width; //按长度求出等比例因子
$new_width *= $r; //扩展填充后的长度
$new_height *= $r; //扩展填充后的高度
$_cut_height = ($new_height - $_n_h) / 2; //求出裁剪点的高度
}
if ($new_height < $_n_h) { //如果新高度小于容器高度
$r = $_n_h / $new_height; //按高度求出等比例因子
$new_width *= $r; //扩展填充后的长度
$new_height *= $r; //扩展填充后的高度
$_cut_width = ($new_width - $_n_w) / 2; //求出裁剪点的长度
}
$this->new = imagecreatetruecolor($_n_w,$_n_h);
imagecopyresampled($this->new,$this->img,0,0,$_cut_width,$_cut_height,$new_width,$new_height,$this->width,$this->height);
}
//加载图片,各种类型,返回图片的资源句柄
public function getFromImg($_file, $_type) {
switch ($_type) {
case 1 :
$img = imagecreatefromgif($_file);
break;
case 2 :
$img = imagecreatefromjpeg($_file);
break;
case 3 :
$img = imagecreatefrompng($_file);
break;
default:
$img = '';
}
return $img;
}
//图像输出
public function output($name = "") {
imagepng($this->new,$this->file.$name);//第二个参数为新生成的图片名
imagedestroy($this->img);
imagedestroy($this->new);
}
}
?>
+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 = 280;
// $maxHeight = 150;
// $changeWidth = 150*$rate;
// $changeHeight = 280/$rate;
// if($changeWidth>=$maxWidth){
// return [(int)$changeHeight,280];
// }
return [230,200];
}
/**
* 输出缩略图
*
* @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];
}
}
?>
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \PHPMailer\PHPMailer\PHPMailer;
use \PHPMailer\PHPMailer\Exception;
class Mail extends Model{
public $fromName;
public $fromAdress;
public $smtpHost;
public $smtpPort;
public $replyTo;
public $smtpUser;
public $smtpPass;
public $encriptionType;
public $errorMsg;
public function __construct(){
$mailOptions = Option::getValues(["mail"]);
$this->fromName = $mailOptions["fromName"];
$this->fromAdress = $mailOptions["fromAdress"];
$this->smtpHost = $mailOptions["smtpHost"];
$this->smtpPort = $mailOptions["smtpPort"];
$this->replyTo = $mailOptions["replyTo"];
$this->smtpUser = $mailOptions["smtpUser"];
$this->smtpPass = $mailOptions["smtpPass"];
$this->encriptionType = $mailOptions["encriptionType"];
}
public function Send($to,$name,$title,$content){
$mail = new PHPMailer();
$mail->isSMTP();
$mail->SMTPAuth=true;
$mail->Host = $this->smtpHost;
if(!empty($this->encriptionType) && $this->encriptionType != "no"){
$mail->SMTPSecure = $this->encriptionType;
}
$mail->Port = $this->smtpPort;
$mail->CharSet = 'UTF-8';
$mail->FromName = $this->fromName;
$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){
return false;
}
return true;
}
}
?>
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use Sabre\DAV;
use \app\index\model\FileManage;
class Objects extends DAV\File{
private $myPath;
public $id;
public $fileData;
public $uid;
public $fileName;
public $dir;
public $actualPath;
public $isExist;
function __construct($path) {
$ex = explode("/",$path);
$this->uid = $ex[0];
$t = strpos($path,$this->uid);
if($t == 0){
$this->myPath = substr($path,$t+strlen($this->uid));
}else{
$this->myPath = $path;
}
$this->fileName = end($ex);
$this->dir = dirname($this->myPath) == "\\" ? "/" :dirname($this->myPath);
$this->fileData = Db::name('files')->where('upload_user',$this->uid)->where('dir',$this->dir)->where('orign_name',$this->fileName)->find();
if(empty($this->fileData)){
$this->isExist = false;
}else{
$this->isExist = true;
}
$this->actualPath = ROOT_PATH . 'public/uploads/'.$this->fileData["pre_name"];
}
function getName() {
return $this->fileName;
}
function get() {
return fopen($this->actualPath, 'r');
}
function getSize() {
return $this->fileData["size"];
}
function setName($name){
$reqPath = $this->myPath;
$ex = explode("/",$reqPath);
$newPath = rtrim($reqPath,end($ex)).$name;
$renameAction = json_decode(FileManage::RenameHandler($reqPath,$newPath,$this->uid,true),true);
if(!$renameAction["result"]["success"]){
throw new DAV\Exception\Forbidden($renameAction["result"]["error"]);
}
}
function getContentType() {
return null;
}
function put($data){
$fileSize = (int)$_SERVER['CONTENT_LENGTH'];
if(!FileManage::sotrageCheck($this->uid,$fileSize)){
throw new DAV\Exception\InsufficientStorage("Quota is not enough");
}
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileData["pre_name"];
file_put_contents($filePath, "");
file_put_contents($filePath, $data);
FileManage::storageGiveBack($this->uid,$this->fileData["size"]);
FileManage::storageCheckOut($this->uid,$fileSize);
@list($width, $height, $type, $attr) = getimagesize($filePath);
$picInfo = empty($width)?" ":$width.",".$height;
Db::name('files')->where('id', $this->fileData["id"])->update(['size' => $fileSize,'pic_info' => $picInfo]);
}
function delete(){
FileManage::DeleteHandler([0=>$this->myPath],$this->uid);
}
function getETag() {
return '"' . sha1(
fileinode($this->actualPath) .
filesize($this->actualPath) .
filemtime($this->actualPath)
) . '"';
}
}
?>
+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();
}
}
?>
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
class Option extends Model{
static function getValues($groups = ['basic'],$userInfo=null){
$t = Db::name('options')->where('option_type','in',$groups)->column('option_value','option_name');
if(in_array("basic",$groups)){
return array_merge($t,self::getThemeOptions($t,$userInfo));
}
return $t;
}
static function getThemeOptions($basicOptions,$userInfo){
$themes = json_decode($basicOptions["themes"],true);
if($userInfo==null){
return ["themeColor"=>$basicOptions["defaultTheme"],"themeConfig"=>$themes[$basicOptions["defaultTheme"]]];
}else{
$userOptions = json_decode($userInfo["options"],true);
if(empty($userOptions)||!array_key_exists("preferTheme",$userOptions)||!array_key_exists($userOptions["preferTheme"],$themes)){
return ["themeColor"=>$basicOptions["defaultTheme"],"themeConfig"=>$themes[$basicOptions["defaultTheme"]]];
}
return ["themeColor"=>$userOptions["preferTheme"],"themeConfig"=>$themes[$userOptions["preferTheme"]]];
}
}
static function getValue($optionName){
return Db::name('options')->where('option_name',$optionName)->value('option_value');
}
}
?>
+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/1/w/230/h/200");
}
/**
* 删除某一策略下的指定七牛文件
*
* @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();
}
}
?>
+166
View File
@@ -0,0 +1,166 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
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;
}
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyModel["op_name"],
'endpoint' => $this->policyModel["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyModel["ak"],
'secret' => $this->policyModel["sk"],
],
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => $this->policyModel["bucketname"],
'Key' => $this->fileModel["pre_name"],
]);
$timeOut = Option::getValue("timeout");
$req = $s3->createPresignedRequest($cmd, '+'.($timeOut/60).' minutes');
$url = (string)$req->getUri();
return [1,$url];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyModel["op_name"],
'endpoint' => $this->policyModel["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyModel["ak"],
'secret' => $this->policyModel["sk"],
],
]);
$s3->putObject([
'Bucket' => $this->policyModel["bucketname"],
'Key' => $this->fileModel["pre_name"],
'Body' => $content,
]);
}
/**
* 删除某一策略下的指定文件
*
* @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");
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyModel["op_name"],
'endpoint' => $this->policyModel["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyModel["ak"],
'secret' => $this->policyModel["sk"],
],
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => $this->policyModel["bucketname"],
'Key' => $this->fileModel["pre_name"],
'ResponseContentDisposition' => 'attachment; filename='.$this->fileModel["orign_name"],
]);
$timeOut = Option::getValue("timeout");
$req = $s3->createPresignedRequest($cmd, '+'.($timeOut/60).' minutes');
$url = (string)$req->getUri();
return [1,$url];
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteS3File($fname,$policy){
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $policy["op_name"],
'endpoint' => $policy["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $policy["ak"],
'secret' => $policy["sk"],
],
]);
$result = $s3->deleteObject([
'Bucket' => $policy["bucketname"],
'Key' => $fname,
]);
return $result["DeleteMarker"];
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+366
View File
@@ -0,0 +1,366 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \think\Session;
use \app\index\model\FileManage;
use \app\index\model\Option;
use \app\index\model\User;
class ShareHandler extends Model{
public $shareData;
public $querryStatus = true;
public $shareOwner;
public $fileData;
public $dirData;
public $lockStatus = false;
public function __construct($key,$notExist=true){
$this->shareData = Db::name('shares')->where('share_key',$key)->find();
if(empty($this->shareData)){
$this->querryStatus = false;
}else{
if($notExist){
if($this->shareData["source_type"] == "file"){
$this->fileShareHandler();
}else{
$this->dirShareHandler();
}
}
}
if($this->shareData["type"] == "private"){
$this->lockStatus = Session::has("share".$this->shareData["id"])?false:true;
}
}
public function deleteShare($uid){
if($this->shareData["owner"] != $uid){
return array(
"error" => 1,
"msg" => "无权操作"
);
}
Db::name('shares')->where('share_key',$this->shareData["share_key"])->delete();
return array(
"error" => 0,
"msg" => "分享取消成功"
);
}
public function changePromission($uid,$ignore=false){
if($this->shareData["owner"] != $uid && $ignore == false){
return array(
"error" => 1,
"msg" => "无权操作"
);
}
$newPwd = self::getRandomKey(6);
Db::name('shares')->where('share_key',$this->shareData["share_key"])->update([
'type' => $this->shareData["type"] == "public"?"private":"public",
'share_pwd' => $newPwd
]);
return array(
"error" =>0,
"msg" => "更改成功",
"newPwd" => $newPwd,
);
}
public function checkPwd($pwd){
if($pwd == $this->shareData["share_pwd"]){
Session::set("share".$this->shareData["id"],"1");
return array(
"error" =>0,
);
}else{
return array(
"error" =>1,
"msg" => "密码错误"
);
}
}
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 getDocPreview($user,$path,$inFolder){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
if($inFolder){
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
}else{
$reqPath = Db::name('files')->where('id',$this->shareData["source_name"])->find();
if($reqPath["dir"] == "/"){
$reqPath["dir"] = $reqPath["dir"].$reqPath["orign_name"];
}else{
$reqPath["dir"] = $reqPath["dir"]."/".$reqPath["orign_name"];
}
$fileObj = new FileManage($reqPath["dir"],$this->shareData["owner"]);
}
$tmpUrl = $fileObj->signTmpUrl();
return[true,"http://view.officeapps.live.com/op/view.aspx?src=".urlencode($tmpUrl)];
}
public function getContent($user,$path=null,$inFolder){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
if($inFolder){
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
}else{
$reqPath = Db::name('files')->where('id',$this->shareData["source_name"])->find();
if($reqPath["dir"] == "/"){
$reqPath["dir"] = $reqPath["dir"].$reqPath["orign_name"];
}else{
$reqPath["dir"] = $reqPath["dir"]."/".$reqPath["orign_name"];
}
$fileObj = new FileManage($reqPath["dir"],$this->shareData["owner"]);
}
$fileObj->getContent();
exit();
}
public function checkSession($user){
if($this->lockStatus){
return [false,"会话过期,请刷新页面"];
}
if(Option::getValue("allowdVisitorDownload") == "false" && !$user->loginStatus){
return [false,"未登录用户禁止下载,请先登录"];
}
if(!$this->querryStatus){
return [false,"分享不存在,请检查链接是否正确"];
}
return[true,null];
}
public function Download($user){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
$reqPath = Db::name('files')->where('id',$this->shareData["source_name"])->find();
if($reqPath["dir"] == "/"){
$reqPath["dir"] = $reqPath["dir"].$reqPath["orign_name"];
}else{
$reqPath["dir"] = $reqPath["dir"]."/".$reqPath["orign_name"];
}
$fileObj = new FileManage($reqPath["dir"],$this->shareData["owner"]);
$FileHandler = $fileObj->Download();
return $FileHandler;
}
public function DownloadFolder($user,$path){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$path = $path == "/"?"":$path;
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
$this->numIncrease("download_num");
$FileHandler = $fileObj->Download();
return $FileHandler;
}
public function ListFile($path){
if($this->lockStatus){
die('{ "result": { "success": false, "error": "会话过期,请重新登陆" } }');
}
if(!$this->querryStatus){
return [false,"分享不存在,请检查链接是否正确"];
}
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$path = $path == "/"?"":$path;
return FileManage::ListFile($this->shareData["source_name"].$path,$this->shareData["owner"],true,$this->shareData["source_name"]);
}
public function Preview($user){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
$reqPath = Db::name('files')->where('id',$this->shareData["source_name"])->find();
if($reqPath["dir"] == "/"){
$reqPath["dir"] = $reqPath["dir"].$reqPath["orign_name"];
}else{
$reqPath["dir"] = $reqPath["dir"]."/".$reqPath["orign_name"];
}
$fileObj = new FileManage($reqPath["dir"],$this->shareData["owner"]);
return $fileObj->PreviewHandler();
}
public function PreviewFolder($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->PreviewHandler();
}
public function listPic($id,$path){
if($this->lockStatus){
die('{ "result": { "success": false, "error": "会话过期,请重新登陆" } }');
}
if(!$this->querryStatus){
return [false,"分享不存在,请检查链接是否正确"];
}
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$path = $path == "/"?"":$path;
return FileManage::listPic($this->shareData["source_name"].$path,$this->shareData["owner"],"/Share/Preview/".$this->shareData["share_key"]."?folder=true");
}
public function numIncrease($name){
Db::name('shares')->where('share_key',$this->shareData["share_key"])->setInc($name);
}
public function getDownloadUrl($user){
if(Option::getValue("allowdVisitorDownload") == "false" && !$user->loginStatus){
return array(
"error" => 1,
"msg" => "未登录用户禁止下载,请先登录",
);
}else{
$this->numIncrease("download_num");
return array(
"error" => 0,
"result" => "/Share/Download/".$this->shareData["share_key"],
);
}
}
public function fileShareHandler(){
$this->shareOwner = new User($this->shareData["owner"],null,true);
$this->fileData = Db::name('files')
->where('upload_user',$this->shareData["owner"])
->where('id',(int)$this->shareData["source_name"])
->find();
if(!$this->shareOwner->loginStatus || empty($this->fileData)){
$this->querryStatus = false;
}else{
$this->querryStatus = true;
}
}
public function dirShareHandler(){
$this->shareOwner = new User($this->shareData["owner"],null,true);
$this->dirData = Db::name('folders')
->where('owner',$this->shareData["owner"])
->where('position_absolute',$this->shareData["source_name"])
->find();
if(!$this->shareOwner->loginStatus || empty($this->dirData)){
$this->querryStatus = false;
}else{
$this->querryStatus = true;
}
}
static function createShare($fname,$type,$pwd,$user,$group){
if(!$group["allow_share"]){
self::setError("您当前的用户组无权分享文件");
}
$path = FileManage::getFileName($fname)[1];
$fnameTmp = FileManage::getFileName($fname)[0];
$fileRecord = Db::name('files')->where('upload_user',$user["id"])->where('orign_name',$fnameTmp)->where('dir',$path)->find();
if(empty($fileRecord)){
self::createDirShare($fname,$type,$pwd,$user,$group);
}else{
self::createFileShare($fileRecord,$type,$pwd,$user,$group);
}
}
static function setError($text){
die('{ "result": { "success": false, "error": "'.$text.'" } }');
}
static function setSuccess($text){
die('{ "result": "'.$text.'" }');
}
static function createDirShare($fname,$type,$pwd,$user,$group){
$dirRecord = Db::name('folders')->where('owner',$user["id"])->where('position_absolute',$fname)->find();
if(empty($dirRecord)){
self::setError("目录不存在");
}
$shareKey = self::getRandomKey(8);
$sharePwd = $type=="public" ? "0" : $pwd;
$SQLData = [
'type' => $type=="public" ? "public" : "private",
'share_time' => date("Y-m-d H:i:s"),
'owner' => $user["id"],
'source_name' => $fname,
'origin_name' => $fname,
'download_num' => 0,
'view_num' => 0,
'source_type' => "dir",
'share_key' => $shareKey,
'share_pwd' => $sharePwd,
];
if(Db::name('shares')->insert($SQLData)){
if($sharePwd == "0"){
self::setSuccess(Option::getValue("siteURL")."s/".$shareKey);
}else{
self::setSuccess("链接:".Option::getValue("siteURL")."s/".$shareKey." 密码:".$sharePwd);
}
}
}
static function createFileShare($file,$type,$pwd,$user,$group){
$shareKey = self::getRandomKey(8);
$sharePwd = $type=="public" ? "0" : $pwd;
$SQLData = [
'type' => $type=="public" ? "public" : "private",
'share_time' => date("Y-m-d H:i:s"),
'owner' => $user["id"],
'source_name' => $file["id"],
'origin_name' => $file["orign_name"],
'download_num' => 0,
'view_num' => 0,
'source_type' => "file",
'share_key' => $shareKey,
'share_pwd' => $sharePwd,
];
if(Db::name('shares')->insert($SQLData)){
if($sharePwd == "0"){
self::setSuccess(Option::getValue("siteURL")."s/".$shareKey);
}else{
self::setSuccess("链接:".Option::getValue("siteURL")."s/".$shareKey." 密码:".$sharePwd);
}
}
}
static function getRandomKey($length = 16){
$charTable = 'abcdefghijklmnopqrstuvwxyz0123456789';
$result = "";
for ( $i = 0; $i < $length; $i++ ){
$result .= $charTable[ mt_rand(0, strlen($charTable) - 1) ];
}
return $result;
}
}
?>
+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"]);
}
}
?>
+91
View File
@@ -0,0 +1,91 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
class Thumb extends Model{
private $file; //图片地址
private $width; //图片长度
private $height; //图片长度
private $img_type; //图片类型
private $img; //原图的资源句柄
private $new; //新图的资源句柄
//构造方法,初始化
public function __construct($_file) {
$this->file = $_file;
list($this->width, $this->height, $this->img_type) = getimagesize($this->file);
$this->img = $this->getFromImg($this->file, $this->img_type);
}
//缩略图(固定长高容器,图像等比例,扩容填充,裁剪)[固定了大小,不失真,不变形]
public function thumb($new_width = 0,$new_height = 0) {
if (empty($new_width) && empty($new_height)) {
$new_width = $this->width;
$new_height = $this->height;
}
if (!is_numeric($new_width) || !is_numeric($new_height)) {
$new_width = $this->width;
$new_height = $this->height;
}
//创建一个容器
$_n_w = $new_width;
$_n_h = $new_height;
//创建裁剪点
$_cut_width = 0;
$_cut_height = 0;
if ($this->width < $this->height) {
$new_width = ($new_height / $this->height) * $this->width;
} else {
$new_height = ($new_width / $this->width) * $this->height;
}
if ($new_width < $_n_w) { //如果新高度小于新容器高度
$r = $_n_w / $new_width; //按长度求出等比例因子
$new_width *= $r; //扩展填充后的长度
$new_height *= $r; //扩展填充后的高度
$_cut_height = ($new_height - $_n_h) / 2; //求出裁剪点的高度
}
if ($new_height < $_n_h) { //如果新高度小于容器高度
$r = $_n_h / $new_height; //按高度求出等比例因子
$new_width *= $r; //扩展填充后的长度
$new_height *= $r; //扩展填充后的高度
$_cut_width = ($new_width - $_n_w) / 2; //求出裁剪点的长度
}
$this->new = imagecreatetruecolor($_n_w,$_n_h);
imagecopyresampled($this->new,$this->img,0,0,$_cut_width,$_cut_height,$new_width,$new_height,$this->width,$this->height);
}
//加载图片,各种类型,返回图片的资源句柄
private function getFromImg($_file, $_type) {
switch ($_type) {
case 1 :
$img = imagecreatefromgif($_file);
break;
case 2 :
$img = imagecreatefromjpeg($_file);
break;
case 3 :
$img = imagecreatefrompng($_file);
break;
default:
$img = '';
}
return $img;
}
//图像输出
public function out($name) {
imagepng($this->new,$name);//第二个参数为新生成的图片名
imagedestroy($this->img);
imagedestroy($this->new);
}
}
?>
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \think\Session;
use \PHPGangsta_GoogleAuthenticator;
use \Endroid\QrCode\QrCode;
use \app\index\model\Option;
class TwoFactor extends Model{
public $ga;
public $secretKey;
public $checkResult;
public function __construct(){
$this->ga = new PHPGangsta_GoogleAuthenticator();
}
public function qrcodeRender(){
ob_end_clean();
$this->secretKey = $this->ga->createSecret();
session("two_factor_enable",$this->secretKey);
$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)){
return [0,"验证码不能为空"];
}
if(empty($key)){
return [0,"二维码过期,请刷新页面后重新扫描"];
}
$this->checkResult = $this->ga->verifyCode($key, $code, 2);
if($this->checkResult){
return [1,"验证成功"];
}else{
return [0,"验证码错误"];
}
}
public function bindUser($uid){
Db::name("users")->where("id",$uid)->update(["two_step" => $this->secretKey]);
}
}
?>
+572
View File
@@ -0,0 +1,572 @@
<?php
namespace app\index\model;
require_once 'extend/Qiniu/functions.php';
use think\Model;
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{
public $policyId;
public $policyContent;
public $userId;
public $chunkData;
public $fileSizeTmp;
public $ossToken;
public $ossSign;
public $ossCallback;
public $ossAccessId;
public $ossFileName;
public $ossCallBack;
public $upyunPolicy;
public $s3Policy;
public $s3Sign;
public $dirName;
public $s3Credential;
public $siteUrl;
public $callBackKey;
public function __construct($id,$uid){
$this->policyId = $id;
$this->userId = $uid;
$this->policyContent = Db::name('policy')->where('id',$id)->find();
}
public function setChunk($chunkId,$chunkSum,$file){
$sqlData = [
'user' => $this->userId,
'ctx' => self::getRandomKey(),
'obj_name' => self::getRandomKey(8),
'time' => date("Y-m-d H:i:s"),
'chunk_id' => $chunkId,
'sum' => $chunkSum,
];
$this->chunkData = $sqlData;
Db::name('chunks')->insert($sqlData);
$this->saveChunk($file);
$this->chunkInfo();
}
public function saveChunk($file){
$chunkSize = strlen($file);
if(!FileManage::sotrageCheck($this->userId,$chunkSize)){
$this->setError("空间容量不足",false);
}
FileManage::storageCheckOut($this->userId,$chunkSize);
if($chunkSize >config('upload.chunk_size')){
$this->setError("分片错误",false);
}
$chunkObj=fopen (ROOT_PATH . 'public/uploads/chunks/'.$this->chunkData["obj_name"].".chunk","w+");
$chunkObjWrite = fwrite ($chunkObj,$file);
if(!$chunkObj || !$chunkObjWrite){
$this->setError("分片创建错误",false);
}
}
public function chunkInfo(){
$returnJson = array(
"ctx" => $this->chunkData["ctx"],
);
echo json_encode($returnJson);
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 = 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']){
$fileName = $this->getObjName($this->policyContent['namerule'],"local",$fname).".".$fileSuffix;
}else{
$fileName = $fname;
}
$generatePath = $this->getDirName($this->policyContent['dirrule']);
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath;
is_dir($savePath)? :mkdir($savePath,0777,true);
if(file_exists($savePath.DS.$fileName)){
$this->setError("文件重名",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}
if($this->policyContent["policy_type"] == "onedrive"){
if($path == "ROOTDIR"){
$path = "";
}
$task = new Task();
$task->taskName = "Upload Big File " . $fname . " to Onedrive";
$task->taskType = "uploadChunksToOnedrive";
$task->taskContent = json_encode([
"path" => $path,
"fname" => $fname,
"objname" => $fileName,
"savePath" => $generatePath,
"fsize" => $this->fileSizeTmp,
"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));
}
}
}
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)));
$sufficCheck = !in_array($fileSuffix,$allowedSuffix);
if(empty(self::getAllowedExt(json_decode($this->policyContent["filetype"],true)))){
$sufficCheck = false;
}
if(($fileSize >= (int)$this->policyContent["max_size"]) || $sufficCheck){
FileManage::storageGiveBack($this->userId,$fileSize);
$this->setError("文件效验失败",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}
$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+");
$deleteList=[];
foreach ($fname as $key => $value) {
$chunkObj = fopen(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk", "rb");
if(!$fileObj || !$chunkObj){
$this->setError("文件创建失败",false);
}
$content = fread($chunkObj, config('upload.chunk_size'));
fwrite($fileObj, $content, config('upload.chunk_size'));
unset($content);
fclose($chunkObj);
unlink(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
array_push($deleteList, $value["id"]);
}
$chunks = Db::name('chunks')->where([
'id' => ["in",$deleteList],
])->delete();
return $fileName;
}
public function fileReceive($file,$info){
$allowedExt = self::getAllowedExt(json_decode($this->policyContent["filetype"],true));
$filter = array('size'=>(int)$this->policyContent["max_size"]);
if(!empty($allowedExt)){
$filter = array_merge($filter,array("ext" => $allowedExt));
}
if(!FileManage::sotrageCheck($this->userId,$file->getInfo('size'))){
$this->setError("空间容量不足",false);
}
if($this->policyContent['autoname']){
$fileName = $this->getObjName($this->policyContent['namerule'],"local",$file->getInfo('name'));
}else{
$fileName = $file->getInfo('name');
}
$generatePath = $this->getDirName($this->policyContent['dirrule']);
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath;
$Uploadinfo = $file
->validate($filter)
->move($savePath,$fileName,false);
if($Uploadinfo){
$jsonData = array(
"path" => $info["path"],
"fname" => $info["name"],
"objname" => $generatePath."/".$Uploadinfo->getSaveName(),
"fsize" => $Uploadinfo->getSize(),
);
@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{
header("HTTP/1.1 401 Unauthorized");
echo json_encode(array("error" => $file->getError()));
}
}
public function setError($text,$delete = false,$fname="",$path=""){
header("HTTP/1.1 401 Unauthorized");
if($delete){
unlink(rtrim($path, DS).DS.$fname);
}
die(json_encode(["error"=> $text]));
}
static function getAllowedExt($ext){
$returnValue = "";
foreach ($ext as $key => $value) {
$returnValue .= $value["ext"].",";
}
return rtrim($returnValue, ",");
}
public function getToken(){
switch ($this->policyContent['policy_type']) {
case 'qiniu':
return $this->getQiniuToken();
break;
case 'local':
return $this->getLocalToken();
break;
case 'onedrive':
return 'nazGTT91tboaLWBC549$:tHSsNyTBxoV4HDfELJeKH1EUmEY=:eyJjYWxsYmFja0JvZHkiOiJ7XCJwYXRoXCI6XCJcIn0iLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb25cL2pzb24iLCJzY29wZSI6ImMxNjMyMTc3LTQ4NGEtNGU1OS1hZDBhLWUwNDc4ZjZhY2NjZSIsImRlYWRsaW5lIjoxNTM2ODMxOTEwfQ==';
break;
case 'oss':
return $this->getOssToken();
break;
case 'upyun':
return $this->getUpyunToken();
break;
case 's3':
return $this->getS3Token();
break;
case 'remote':
return $this->getRemoteToken();
break;
default:
# code...
break;
}
}
public function getObjName($expression,$type = "qiniu",$origin = ""){
$policy = array(
'{date}' =>date("Ymd"),
'{datetime}' =>date("YmdHis"),
'{uid}' =>$this->userId,
'{timestamp}' =>time(),
'{randomkey16}' =>self::getRandomKey(16),
'{randomkey8}' =>self::getRandomKey(8),
);
if($type == "qiniu"){
$policy = array_merge($policy,array("{originname}" => "$(fname)"));
}else if($type == "local"){
$policy = array_merge($policy,array("{originname}" => $origin));
}else if ($type="oss"){
$policy = array_merge($policy,array("{originname}" => '${filename}'));
}else if ($type="upyun"){
$policy = array_merge($policy,array("{originname}" => '{filename}{.suffix}'));
}
return strtr($expression,$policy);
}
public function getDirName($expression){
$policy = array(
'{date}' =>date("Ymd"),
'{datetime}' =>date("YmdHis"),
'{uid}' =>$this->userId,
'{timestamp}' =>time(),
'{randomkey16}' =>self::getRandomKey(16),
'{randomkey8}' =>self::getRandomKey(8),
);
return trim(strtr($expression,$policy),"/");
}
public function getQiniuToken(){
$callbackKey = $this->getRandomKey();
$sqlData = [
'callback_key' => $callbackKey,
'pid' => $this->policyId,
'uid' => $this->userId
];
Db::name('callback')->insert($sqlData);
$auth = new Auth($this->policyContent['ak'], $this->policyContent['sk']);
$policy = array(
'callbackUrl' =>Option::getValue("siteURL").'Callback/Qiniu',
'callbackBody' => '{"fname":"$(fname)","objname":"$(key)","fsize":"$(fsize)","callbackkey":"'.$callbackKey.'","path":"$(x:path)","picinfo":"$(imageInfo.width),$(imageInfo.height)"}',
'callbackBodyType' => 'application/json',
'fsizeLimit' => (int)$this->policyContent['max_size'],
);
$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']));
}
$token = $auth->uploadToken($this->policyContent['bucketname'], null, 3600, $policy);
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){
if ($v){
$elems[] = $v;
}
}
$value = implode('&', $elems);
$sign = base64_encode(hash_hmac('sha1', $value, $secret, true));
return 'UPYUN ' . $key . ':' . $sign;
}
public function getUpyunToken(){
$callbackKey = $this->getRandomKey();
$sqlData = [
'callback_key' => $callbackKey,
'pid' => $this->policyId,
'uid' => $this->userId
];
Db::name('callback')->insert($sqlData);
$options = Option::getValues(["oss","basic"]);
$dateNow = gmdate('D, d M Y H:i:s \G\M\T');
$policy=[
"bucket" => $this->policyContent['bucketname'],
"expiration" => time()+$options["timeout"],
"notify-url" => $options["siteURL"]."Callback/Upyun",
"content-length-range" =>"0,".$this->policyContent['max_size'],
"date" => $dateNow,
"ext-param"=>json_encode([
"path"=>cookie("path"),
"uid" => $this->userId,
"pid" => $this->policyId,
]),
];
$allowedExt = self::getAllowedExt(json_decode($this->policyContent["filetype"],true));
if(!empty($allowedExt)){
$policy = array_merge($policy,array("allow-file-type" => $allowedExt));
}
$dirName = $this->getObjName($this->policyContent['dirrule']);
$policy = array_merge($policy,array("save-key" => $dirName.(empty($dirName)?"":"/").uniqid()."CLSUFF{filename}{.suffix}"));
$this->upyunPolicy = base64_encode(json_encode($policy));
return self::upyunSign($this->policyContent['op_name'], md5($this->policyContent['op_pwd']), "POST", "/".$this->policyContent['bucketname'],$dateNow,$this->upyunPolicy);
}
public function ossCallback(){
$callbackKey = $this->getRandomKey();
$sqlData = [
'callback_key' => $callbackKey,
'pid' => $this->policyId,
'uid' => $this->userId
];
Db::name('callback')->insert($sqlData);
$returnValue["callbackUrl"] = Option::getValue("siteUrl").'Callback/Oss';
$returnValue["callbackBody"] = '{"fname":"${x:fname}","objname":"${object}","fsize":"${size}","callbackkey":"'.$callbackKey.'","path":"${x:path}","picinfo":"${imageInfo.width},${imageInfo.height}"}';
$this->ossCallBack = base64_encode(json_encode($returnValue));
return base64_encode(json_encode($returnValue));
}
public function getS3Token(){
$dirName = $this->getDirName($this->policyContent['dirrule']);
$longDate = gmdate('Ymd\THis\Z');
$shortDate = gmdate('Ymd');
$credential = $this->policyContent['ak'] . '/' . $shortDate . '/' . $this->policyContent['op_name'] . '/s3/aws4_request';
$callbackKey = $this->getRandomKey();
$sqlData = [
'callback_key' => $callbackKey,
'pid' => $this->policyId,
'uid' => $this->userId
];
Db::name('callback')->insert($sqlData);
$this->siteUrl = Option::getValue("siteUrl");
$returnValue = [
"expiration" => date("Y-m-d",time()+1800)."T".date("H:i:s",time()+1800).".000Z",
"conditions" => [
0 => ["bucket" => $this->policyContent['bucketname']],
1 => ["starts-with",'$key', $dirName],
2 => ["starts-with",'$success_action_redirect' ,$this->siteUrl."Callback/S3/key/".$callbackKey],
3 => ["content-length-range",1,(int)$this->policyContent['max_size']],
4 => ['x-amz-algorithm' => 'AWS4-HMAC-SHA256'],
5 => ['x-amz-credential' => $credential],
6 => ['x-amz-date' => $longDate],
7 => ["starts-with", '$name', ""],
8 => ["starts-with", '$Content-Type', ""],
]
];
$this->s3Policy = base64_encode(json_encode($returnValue));
$signingKey = hash_hmac("sha256",$shortDate,"AWS4".$this->policyContent['sk'],true);
$signingKey = hash_hmac("sha256",$this->policyContent['op_name'],$signingKey,true);
$signingKey = hash_hmac("sha256","s3",$signingKey,true);
$signingKey = hash_hmac("sha256","aws4_request",$signingKey,true);
$signingKey = hash_hmac("sha256",$this->s3Policy,$signingKey);
$this->s3Sign = $signingKey;
$this->dirName = $dirName;
$this->s3Credential = $credential;
$this->x_amz_date = $longDate;
$this->callBackKey = $callbackKey;
}
public function getOssToken(){
$dirName = $this->getObjName($this->policyContent['dirrule']);
$returnValu["expiration"] = date("Y-m-d",time()+1800)."T".date("H:i:s",time()+1800).".000Z";
$returnValu["conditions"][0]["bucket"] = $this->policyContent['bucketname'];
$returnValu["conditions"][1][0]="starts-with";
$returnValu["conditions"][1][1]='$key';
if($this->policyContent["autoname"]){
$this->ossFileName = $dirName.(empty($dirName)?"":"/").$this->getObjName($this->policyContent['namerule'],"oss");;
}else{
$this->ossFileName = $dirName.(empty($dirName)?"":"/").'${filename}';
}
$returnValu["conditions"][1][2]=$dirName.(empty($dirName)?"":"/");
$returnValu["conditions"][2]=["content-length-range",1,(int)$this->policyContent['max_size']];
$returnValu["conditions"][3]["callback"] = $this->ossCallback();
$this->ossToken=base64_encode(json_encode($returnValu));
$this->ossSignToken();
$this->ossAccessId = $this->policyContent['ak'];
return false;
}
public function ossSignToken(){
$this->ossSign = base64_encode(hash_hmac("sha1", $this->ossToken, $this->policyContent['sk'],true));
}
public function getLocalToken(){
$auth = new Auth($this->policyContent['ak'], $this->policyContent['sk']);
$policy = array(
'callbackBody' => '{"path":"'.cookie('path').'"}',
'callbackBodyType' => 'application/json',
);
$token = $auth->uploadToken($this->policyContent['bucketname'], null, 3600, $policy);
return $token;
}
static function getRandomKey($length = 16){
$charTable = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$result = "";
for ( $i = 0; $i < $length; $i++ ){
$result .= $charTable[ mt_rand(0, strlen($charTable) - 1) ];
}
return $result;
}
static function b64Decode($string) {
$data = str_replace(array('-','_'),array('+','/'),$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
}
?>
+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();
}
}
?>
+381
View File
@@ -0,0 +1,381 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \app\index\model\Mail;
use think\Validate;
class User extends Model{
public $uid;
public $varifyKey;
public $groupId;
public $regDate;
public $loginStatus = false;
public $userNick;
public $userMail;
public $groupData;
public $userSQLData;
/**
* [__construct description]
* @param [type] $userId [description]
* @param [type] $userKey [description]
*/
public function __construct($userId,$userKey,$ignoreLogin = false){
$userData = Db::name('users')->where('id',$userId)->where('user_status',0)->find();
if(empty($userData)){
$this->loginStatus = false;
$this->setUser();
return false;
}
if(!$ignoreLogin){
if(md5($userData['user_email'].$userData['user_pass'].config('salt')) != $userKey){
$this->loginStatus = false;
$this->setUser();
return false;
}
}
$this->groupData = Db::name('groups')->where('id',$userData['user_group'])->find();
$this->loginStatus = true;
$this->uid = $userId;
$this->groupId = $userData['user_group'];
$this->regDate = $userData['user_date'];
$this->userNick = $userData['user_nick'];
$this->userMail = $userData['user_email'];
$this->varifyKey = $userKey;
$this->userSQLData = $userData;
}
public function setUser(){
$this->groupData = Db::name('groups')->where('id',2)->find();
$this->uid = -1;
}
static function resetPwd($key,$pwd){
$key = explode("_",$key);
$resetKey = $key[0]."_".$key[1];
$userId = $key[2];
$keyCheck = self::resetUser($resetKey,$userId);
if(!$keyCheck[0]){
return $keyCheck;
}else{
if ((mb_strlen($pwd,'UTF8')>64) || (mb_strlen($pwd,'UTF8')<4)){
return [false,"密码不符合规范"];
}
self::Reset($userId,$pwd);
return [true,"密码重设成功"];
}
}
static function Reset($uid,$pwd){
Db::name('users')->where('id',$uid)
->update([
'user_pass' => md5(config('salt').$pwd),
]);
}
/**
* [register description]
* @param [type] $userEmail [description]
* @param [type] $userPass [description]
* @return [type] [description]
*/
static function register($userEmail,$userPass,$captchaCode){
if(Option::getValue("reg_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
}
if (\app\index\model\Option::getValue("regStatus") == '1'){
return [false,"当前站点关闭注册"];
}
$userName = str_replace(" ", "", $userEmail);
$passWord = $userPass;
if ( !filter_var($userName,FILTER_VALIDATE_EMAIL) || (mb_strlen($userName,'UTF8')>32) || (mb_strlen($userName,'UTF8')<4) || (mb_strlen($passWord,'UTF8')>64) || (mb_strlen($passWord,'UTF8')<4)){
return [false,"邮箱或密码不符合规范"];
}
if(Db::name('users')->where('user_email',$userName)->find() !=null){
return [false,"该邮箱已被注册"];
}
$defaultGroup = (int)\app\index\model\Option::getValue("defaultGroup");
$regOptions = Option::getValues(["register"]);
if($regOptions["email_active"] == "1"){
$activationKey = md5(uniqid(rand(), TRUE));
$userStatus = 1;
}else{
$activationKey = "n";
$userStatus = 0;
}
$sqlData = [
'user_email' => $userName,
'user_pass' => md5(config('salt').$passWord),
'user_status' => $userStatus,
'user_group' => $defaultGroup,
'group_primary' => 0,
'user_date' => date("Y-m-d H:i:s"),
'user_nick' => explode("@",$userName)[0],
'user_activation_key' => $activationKey,
'used_storage' => 0,
'two_step'=>"0",
'webdav_key' =>md5(config('salt').$passWord),
'delay_time' =>0,
'avatar' => "default",
'profile' => true,
'options' => "{}",
];
if(Db::name('users')->insert($sqlData)){
$userId = Db::name('users')->getLastInsID();
Db::name('folders')->insert( [
'folder_name' => '根目录',
'parent_folder' => 0,
'position' => '.',
'owner' => $userId,
'date' => date("Y-m-d H:i:s"),
'position_absolute' => '/',
]);
if($regOptions["email_active"] == "1"){
$options = Option::getValues(["basic","mail_template"]);
$replace = array(
'{siteTitle}' =>$options["siteName"],
'{userName}' =>explode("@",$userName)[0],
'{siteUrl}' =>$options["siteURL"],
'{siteSecTitle}' =>$options["siteTitle"],
'{activationUrl}' =>$options["siteURL"]."Member/emailActivate/".$activationKey,
);
$mailContent = strtr($options["mail_activation_template"],$replace);
$mailObj = new Mail();
$mailObj->Send($userName,explode("@",$userName)[0],"".$options["siteName"].""."注册激活",$mailContent);
return [true,"ec"];
}
return [true,"注册成功"];
}
}
static function activicateUser($key){
$userData = Db::name('users')
->where('user_activation_key','neq','n')
->where("user_activation_key",$key)->find();
if(empty($userData)){
return [0,"激活失败,用户在不存在"];
}else{
Db::name('users')->where("id",$userData["id"])->update([
"user_activation_key" => "n",
"user_status" => 0,
]);
return [1,1];
}
}
static function resetUser($key,$uid){
$timeNow = time();
if(empty($key)||empty($uid)){
return [0,"URL参数错误"];
}
$key = explode("_",$key);
$userData = Db::name('users')
->where('user_status',0)
->where("id",$uid)->find();
if(empty($userData)){
return [0,"用户不存在"];
}
if(md5($userData["user_pass"].$key[1]) != $key[0]){
return [0,"参数无效,请检查邮件链接"];
}
if(($timeNow - $key[1])>7200){
return [0,"重设链接过期,请重新提交"];
}
return [1,1];
}
static function findPwd($email,$captchaCode){
if(Option::getValue("forget_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
}
$userData = Db::name('users')->where('user_email',$email)->find();
if(empty($userData)){
return [1,1];
}
$timeNow = time();
$resetHash = md5($userData["user_pass"].$timeNow);
$resetKey = $resetHash."_".$timeNow;
$options = Option::getValues(["basic","mail_template"]);
$replace = array(
'{siteTitle}' =>$options["siteName"],
'{userName}' =>$userData["user_nick"],
'{siteUrl}' =>$options["siteURL"],
'{siteSecTitle}' =>$options["siteTitle"],
'{resetUrl}' =>$options["siteURL"]."Member/resetPwd/".$resetKey."?uid=".$userData["id"],
);
$mailContent = strtr($options["mail_reset_pwd_template"],$replace);
$mailObj = new Mail();
$mailObj->Send($email,$userData["user_nick"],"".$options["siteName"].""."密码重置",$mailContent);
return [true,"ec"];
}
/**
* [login description]
* @param [type] $userEmail [description]
* @param [type] $userPass [description]
* @return [type] [description]
*/
static function login($userEmail,$userPass,$captchaCode){
$userEmail = str_replace(" ", "", $userEmail);
$userData =Db::name('users')->where('user_email',$userEmail)->find();
if(empty($userEmail) || empty($userPass)){
return [false,"表单不完整"];
}
if(Option::getValue("login_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
}
if(Db::name('users')->where('user_email',$userEmail)->value('user_pass') != md5(config('salt').$userPass)){
return [false,"用户名或密码错误"];
}
if(Db::name('users')->where('user_email',$userEmail)->value('user_status') != 0){
return [false,"账号被禁用或未激活"];
}
if($userData["two_step"] != "0"){
session("user_id_tmp",Db::name('users')->where('user_email',$userEmail)->value('id'));
session("login_status_tmp","ok");
session("login_key_tmp",md5($userEmail.md5(config('salt').$userPass).config('salt')));
return [false,"tsp"];
}
$loginKey = md5($userEmail.md5(config('salt').$userPass).config('salt'));
cookie('user_id',Db::name('users')->where('user_email',$userEmail)->value('id'),604800);
cookie('login_status','ok',604800);
cookie('login_key',$loginKey,604800);
return [true,"登录成功",$loginKey];
}
static function checkCaptcha($code){
if(!captcha_check($code)){
return false;
}
return true;
}
/**
* [clear description]
* @return [type] [description]
*/
public function clear(){
$this->loginStatus = false;
$this->uid = null;
$this->groupId = null;
$this->regDate = null;
$this->varifyKey = null;
cookie('user_id', null);
cookie('login_status', null);
cookie('login_key', null);
}
/**
* [getInfo description]
* @return [type] [description]
*/
public function getInfo(){
return [
'uid' => $this->uid,
'groupId' => $this->groupId,
'regDate' => $this->regDate,
'loginStatus' => $this->loginStatus,
'userNick' => $this->userNick,
'userMail' => $this->userMail,
'groupData' => $this->groupData,
'sqlData' => $this->userSQLData,
];
}
public function getSQLData(){
return $this->userSQLData;
}
public function getPolicy(){
return Db::name('policy')->where('id',$this->groupData["policy_name"])->find();
}
public function getGroupData(){
return $this->groupData;
}
public function getMemory($notEcho = false){
$usedMemory = $this->userSQLData["used_storage"];
$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($usedMemory);
$returnData["total"] = self::countSize($groupStorage+$packetStorage);
$returnData["rate"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["basic"] = self::countSize($groupStorage);
$returnData["pack"] = self::countSize($packetStorage);
if($usedMemory > $groupStorage){
$returnData["r1"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = 0;
$returnData["r3"] = 100-$returnData["r1"];
}else{
$returnData["r1"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = floor(($groupStorage-$usedMemory)/($groupStorage+$packetStorage)*100);;
$returnData["r3"] = 100-$returnData["r1"]-$returnData["r2"];
}
if($notEcho){
return json_encode($returnData);
}
echo json_encode($returnData);
}
static function countSize($bit) {
$type = array('Bytes','KB','MB','GB','TB');
for($i = 0; $bit >= 1024; $i++) {
$bit/=1024;
}
return (floor($bit*100)/100).$type[$i];
}
public function changeNick($nick){
$nick=["nick" => $nick];
$rules = [
'nick' => ['require','max'=>'25','chsDash'],
];
$validate = new Validate($rules);
if (!$validate->check($nick)) {
return [0,"昵称必须是1-25位字符,只能包含中英文等常见字符"];
}else{
Db::name("users")->where("id",$this->uid)->update(["user_nick" => $nick["nick"]]);
return [1,1];
}
}
public function changeOption($optionKey,$optionValue){
$options = json_decode($this->userSQLData["options"],true);
$options[$optionKey] = $optionValue;
Db::name("users")->where("id",$this->uid)->update(["options" => json_encode($options)]);
return [1,1];
}
public function changePwd($origin,$new){
if(md5(config('salt').$origin) != $this->userSQLData["user_pass"]){
return [0,"原密码错误"];
}
if ((mb_strlen($new,'UTF8')>64) || (mb_strlen($new,'UTF8')<4)){
return [false,"密码不符合规范"];
}
self::Reset($this->uid,$new);
return [true,"密码重设成功"];
}
public function homePageToggle($status){
Db::name("users")->where("id",$this->uid)->update(["profile" => $status=="true"?1:0]);
return [1,1];
}
}
?>
+72
View File
@@ -0,0 +1,72 @@
{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="row">
<div class="col">
<div class="card">
<div class="card-header" >
<i class="fa fa-eye fa-fw" aria-hidden="true"></i>
系统信息
</div>
<div class="card-body">
<ul>
<li>服务器信息:{:$_SERVER ['SERVER_SOFTWARE']}</li>
<li>服务端系统:{:PHP_OS}</li>
<li>最大上传限制:{:get_cfg_var ("upload_max_filesize")?get_cfg_var ("upload_max_filesize"):"不允许上传"}</li>
<li>脚本最大执行时间:{:get_cfg_var("max_execution_time")."s "}</li>
<li>脚本最大内存:{:get_cfg_var ("memory_limit")?get_cfg_var("memory_limit"):"无"}</li>
<li>PHP扩展:curl{eq name=":extension_loaded('curl')" value="true"}√{else/}×{/eq} openssl{eq name=":extension_loaded('openssl')" value="true"}√{else/}×{/eq} GD{eq name=":extension_loaded('gd')" value="true"}√{else/}×{/eq} fileinfo{eq name=":extension_loaded('fileinfo')" value="true"}√{else/}×{/eq}</li>
</ul>
</div>
</div>
</div>
<div class="col">
<div class="card" style="min-height: 269px;">
<div class="card-header">
<i class="fa fa-cloud fa-fw" aria-hidden="true"></i>
关于Cloudreve
</div>
<div class="card-body">
<h5 class="card-title">Cloudreve {$programVersion.type} {$programVersion.version}</h5>
<ul>
<li>主页:<a href="https://cloudreve.org" target="_blank">https://cloudreve.org</a></li>
<li>Github<a href="https://github.com/HFO4/Cloudreve" target="_blank">https://github.com/HFO4/Cloudreve</a></li>
<li>许可证:<a href="https://github.com/HFO4/Cloudreve/LICENSE" target="_blank">GPL V3</a>(社区版)</li>
<li>其他链接:<a href="https://github.com/HFO4/Cloudreve/graphs/contributors" target="_blank">代码贡献者</a> <a href="https://cloudreve.org/joingroup" target="_blank">Telegram群组</a> <a href="https://forum.cloudreve.org" target="_blank">社区</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script type="text/javascript">
</script>
{/block}
+176
View File
@@ -0,0 +1,176 @@
{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>
<form id="addGroup">
<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 type="text" class="form-control" name="group_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="colFormLabelSm" class="col-form-label col-form-label-sm">上传策略</label>
</div>
<div class="col-md-4">
<select name="policy_name" required class="form-control">
{volist name="policy" id="p"}
<option value="{$p.id}">{$p.policy_name}</option>
{/volist}
</select>
</div>
<div class="col-md-4 option-des"> 为该用户组绑定一个上传策略</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-group mb-3">
<input type="number" class="form-control" name="max_storage" 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="colFormLabelSm" 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="speed" spellcheck="false" min="0" placeholder="留空表示不限速,0表示禁止下载">
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
Kb/s
</span>
</div>
</div>
<div class="col-md-4 option-des"> 限制该用户组下的用户上传的文件的下载速度,<strong>此设置仅对本地上传方案有效</strong></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="range_transfer" value="1" id="allow_range_1" checked>
<label class="" for="allow_range_1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="range_transfer" id="allow_range_0" value="0" >
<label class="" for="allow_range_0">禁止</label></div>
<div class="col-md-4 option-des"> 是否断点续传及多线程下载,关闭后预览视频不支持拖拽播放,<strong>此设置仅对本地上传方案有效</strong></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="allow_share" value="1" id="allow_share_1" checked>
<label class="" for="allow_share_1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="allow_share" id="allow_share_0" value="0" >
<label class="" for="allow_share_0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许创建文件分享链接</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">WebDAV支持</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="webdav" value="1" id="webdav1" checked>
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="webdav" id="webdav0" value="0" >
<label class="" for="webdav0">禁止</label></div>
<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>
</div>
<div class="col-md-4">
<input class="" type="radio" name="color" value="default" id="default" checked>
<label class="" for="default" ><span class="badge badge-primary">蓝色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="primary" id="primary">
<label class="" for="primary" ><span class="badge badge-secondary">灰色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="success" id="success">
<label class="" for="success" ><span class="badge badge-success">绿色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="danger" id="danger">
<label class="" for="danger" ><span class="badge badge-danger">红色</span></label>
<br>
<input class="" type="radio" name="color" value="warning" id="warning">
<label class="" for="warning" ><span class="badge badge-warning">黄色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="info" id="info">
<label class="" for="info" ><span class="badge badge-info">浅蓝色</span></label>
&nbsp;&nbsp;&nbsp;
</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="saveGroup">添加</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/add_group.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,835 @@
{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 id="choose">
<div class="row">
<div class="col-md-3 md-2 mt-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/local.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">本地服务器</h5>
<p class="card-text">将文件存放在Cloudreve主程序的同一个服务器上</p>
<a href="javascript:void()" class="btn btn-primary" id="addLocal">添加</a>
</div>
</div>
</div>
<div class="col-md-3 md-2 mt-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/qiniu.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">七牛云存储</h5>
<p class="card-text">使用<a href="https://www.qiniu.com/" target="_blank">七牛云</a>提供的云存储及CDN服务</p>
<a href="javascript:void();" class="btn btn-primary" id="addQiniu">添加</a>
</div>
</div>
</div>
<div class="col-md-3 md-2 mt-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/oss.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">阿里云OSS</h5>
<p class="card-text">使用<a href="https://www.aliyun.com/product/oss?spm=5176.doc54464.765261.286.v2y7Hk" target="_blank">OSS对象存储</a>存放文件,推荐配合阿里云CDN使用</p>
<a href="javascript:void()" class="btn btn-primary" id="addOss">添加</a>
</div>
</div>
</div>
<div class="col-md-3 md-2 mt-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/upyun.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">又拍云</h5>
<p class="card-text">使用<a href="https://console.upyun.com/register/?invite=ryPX265PW" target="_blank">又拍云存储</a>提供的服务,每月有一定免费额度</p>
<a href="javascript:void()" class="btn btn-primary" id="addUpyun">添加</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/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>
<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>
<div class="card" id="local" style="display: none">
<div class="card-header">
添加本地上传策略
</div>
<div class="card-body">
<form id="localPolicy">
<input type="text" class="form-control" name="policy_type" value="local" 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="policy_name" class="col-form-label col-form-label-sm">允许获取外链</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="origin_link" id="local_allowd_origin1" value="1" >
<label class="" for="local_allowd_origin1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="origin_link" id="local_allowd_origin2" value="0" checked>
<label class="" for="local_allowd_origin2">禁止</label>
</div>
<div class="col-md-4 option-des"> 是否允许获取文件的源URL,推荐禁止</div>
</div>
<div class="row form-setting" id="localOrigin" style="display: none">
<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="{$options.siteURL}public/uploads/">
</div>
<div class="col-md-4 option-des"> 一般保持默认。如果你设置了融合CDN等产品,这里可以填写CDN域名,结尾要加"/"</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 type="text" class="form-control" name="filetype" value="image/*,jpg,png,gif,bmp,mp4,mp3,txt" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 多个请以半角逗号","隔开,留空表示不限制。如果允许图片类型文件,除了图片类型后缀外,请额外添加一项"image/*"</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"> 你可以使用<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="{date}/{uid}" 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>
<div class="card" id="qiniu" style="display: none">
<div class="card-header">
添加七牛上传策略
</div>
<div class="card-body">
<form id="qiniuPolicy">
<input type="text" class="form-control" name="policy_type" value="qiniu" 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">AccessKey </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="ak" required></div>
<div class="col-md-4 option-des"> 七牛账号的AccessKey,请在七牛控制面板查看</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="sk" class="col-form-label col-form-label-sm">SecretKey </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="sk" required></div>
<div class="col-md-4 option-des"> 七牛账号的SecretKey,请在七牛控制面板查看</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">存储空间名称 </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" 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="policy_name" class="col-form-label col-form-label-sm">空间属性</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="bucket_private" id="bucket_private_1" value="1" checked>
<label class="" for="bucket_private_1" >私有空间</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="bucket_private" id="bucket_private_0" value="0">
<label class="" for="bucket_private_0">公开空间</label>
</div>
<div class="col-md-4 option-des"> 与七牛空间访问控制设置保持一致,推荐设置为私有空间</div>
</div>
<div class="row form-setting" id="outlink" style="display: none">
<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 class="" type="radio" name="origin_link" id="qiniu_allowd_origin1" value="1" >
<label class="" for="qiniu_allowd_origin1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="origin_link" id="qiniu_allowd_origin2" value="0" checked>
<label class="" for="qiniu_allowd_origin2">禁止</label>
</div>
<div class="col-md-4 option-des"> 是否允许获取文件的源URL,推荐禁止</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="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 type="text" class="form-control" name="filetype" value="image/*,jpg,png,gif,bmp,mp4,mp3,txt" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 多个请以半角逗号","隔开,留空表示不限制。如果允许图片类型文件,除了图片类型后缀外,请额外添加一项"image/*"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="mimetype" class="col-form-label col-form-label-sm">允许的mimeType</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="mimetype" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 多个请以半角分号";"隔开,留空表示不限制。示例:image/*表示只允许上传图片类型,image/jpeg;image/png表示只允许上传jpg和png类型的图片,!application/json;text/plain表示禁止上传json文本和纯文本。注意最前面的感叹号!</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="qiniu_autoname1" value="1" checked>
<label class="" for="qiniu_autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="qiniu_autoname2" value="0" >
<label class="" for="qiniu_autoname2">关闭</label>
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="qiniu_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"> 你可以使用<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">
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" 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></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
<div id="oss" style="display: none">
<div class="alert alert-primary" role="alert">
阿里云OSS创建空间后请进行如下操作,否则无法正常上传。
<ul>
<li>转到 空间管理 - 基础设置 - 跨域设置;</li>
<li>创建规则,其中来源填写“*”,允许 Methods全部勾选,允许 Headers填写“*”,其他保持默认,点击确定保存.</li>
</ul>
</div>
<div class="card" >
<div class="card-header">
添加阿里云OSS上传策略
</div>
<div class="card-body">
<form id="ossPolicy">
<input type="text" class="form-control" name="policy_type" value="oss" 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">Access Key ID </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="ak" pattern="[^\s]+" title="请不要包含空格" required></div>
<div class="col-md-4 option-des"> 阿里云账号的Access Key ID,请在阿里云控制面板-Access Key管理中创建或查看</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="sk" class="col-form-label col-form-label-sm"> Access Key Secret </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="sk" pattern="[^\s]+" title="请不要包含空格" 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">Bucket名称 </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" required></div>
<div class="col-md-4 option-des"> 创建空间时填写的Bucket名</div>
</div>
<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 class="" type="radio" name="bucket_private" id="oss_private_1" value="1" checked>
<label class="" for="oss_private_1" >私有</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="bucket_private" id="oss_private_0" value="0">
<label class="" for="oss_private_0">公共读</label>
</div>
<div class="col-md-4 option-des"> 与OSS空间访问控制设置保持一致,推荐设置为私有空间</div>
</div>
<div class="row form-setting" id="oss_outlink" style="display: none">
<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 class="" type="radio" name="origin_link" id="oss_allowd_origin1" value="1" >
<label class="" for="oss_allowd_origin1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="origin_link" id="oss_allowd_origin2" value="0" checked>
<label class="" for="oss_allowd_origin2">禁止</label>
</div>
<div class="col-md-4 option-des"> 是否允许获取文件的源URL,推荐禁止</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="url" value="http(s)://" required>
</div>
<div class="col-md-4 option-des"> OSS空间绑定的域名,用于文件下载、访问,结尾要加"/"(推荐配合阿里云CDN使用,此处填写CDN域名)</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<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" value="http(s)://" required>
</div>
<div class="col-md-4 option-des"> OSS空间的域名,一般与上一项保持一致。(如果上一项填写的是CDN域名,则此处与上一项不应相同)</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 type="text" class="form-control" name="filetype" value="image/*,jpg,png,gif,bmp,mp4,mp3,txt" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 多个请以半角逗号","隔开,留空表示不限制。如果允许图片类型文件,除了图片类型后缀外,请额外添加一项"image/*"</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="oss_autoname1" value="1" checked>
<label class="" for="oss_autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="oss_autoname2" value="0" >
<label class="" for="oss_autoname2">关闭</label>
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="oss_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"> 你可以使用<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">
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" 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="saveOss">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
</div>
<div class="card" id="upyun" style="display: none">
<div class="card-header">
又拍云
</div>
<div class="card-body">
<form id="upyunPolicy">
<input type="text" class="form-control" name="policy_type" value="upyun" 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">操作员名称 </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="op_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="sk" class="col-form-label col-form-label-sm">操作员密码 </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="op_pwd" 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">存储服务名 </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" 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="policy_name" class="col-form-label col-form-label-sm">Token 防盗链</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="bucket_private" id="upyun_bucket_private_1" value="1" checked>
<label class="" for="upyun_bucket_private_1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="bucket_private" id="upyun_bucket_private_0" value="0">
<label class="" for="upyun_bucket_private_0">关闭</label>
</div>
<div class="col-md-4 option-des"> 与 功能配置-访问配置-Token 防盗链 保持一致,推荐设置为开启</div>
</div>
<div class="row form-setting" id="upyun_token" style="">
<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="sk"></div>
<div class="col-md-4 option-des"> 与 功能配置-访问配置-Token防盗链 中的密钥保持一致</div>
</div>
<div class="row form-setting" id="upyun_outlink" style="display: none">
<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 class="" type="radio" name="origin_link" id="upyun_allowd_origin1" value="1" >
<label class="" for="upyun_allowd_origin1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="origin_link" id="upyun_allowd_origin2" value="0" checked>
<label class="" for="upyun_allowd_origin2">禁止</label>
</div>
<div class="col-md-4 option-des"> 是否允许获取文件的源URL,推荐禁止</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="url" required>
</div>
<div class="col-md-4 option-des"> 绑定的域名,开头要加http(s)://结尾要加"/"</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 type="text" class="form-control" name="filetype" value="image/*,jpg,png,gif,bmp,mp4,mp3,txt" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 多个请以半角逗号","隔开,留空表示不限制。如果允许图片类型文件,除了图片类型后缀外,请额外添加一项"image/*"</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>
<input type="text" name="autoname" value="1" style="display: none">
<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="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" 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="saveUpyun">保存设置</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>
<!-- /.content-wrapper-->
<div class="modal fade" tabindex="-1" role="dialog" id="varTable">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">自动重命名变量对照表</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th >变量字段</th>
<th >说明</th>
<th >示例</th>
<th >适用范围</th>
</tr>
</thead>
<tbody>
<tr>
<td >{date}</td>
<td>上传日期</td>
<td>20180118</td>
<td>全部</td>
</tr>
<tr>
<td >{datetime}</td>
<td>上传日期时间</td>
<td>20180118121049</td>
<td>全部</td>
</tr>
<tr>
<td >{uid}</td>
<td>上传者UID</td>
<td>154</td>
<td>全部</td>
</tr>
<tr>
<td >{timestamp}</td>
<td>时间戳</td>
<td>1516277624</td>
<td>全部</td>
</tr>
<tr>
<td >{randomkey16}</td>
<td>16位随机字符</td>
<td>16D8lhjErTDWAQjW</td>
<td>全部</td>
</tr>
<tr>
<td >{randomkey8}</td>
<td>8位随机字符</td>
<td>hM4Tpdh6</td>
<td>全部</td>
</tr>
<tr>
<td >{originname}</td>
<td>原始文件名</td>
<td>plus1s.jpg</td>
<td>全部</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="varTableFolder">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">目录变量对照表</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th >变量字段</th>
<th >说明</th>
<th >示例</th>
<th >适用范围</th>
</tr>
</thead>
<tbody>
<tr>
<td >{date}</td>
<td>上传日期</td>
<td>20180118</td>
<td>全部</td>
</tr>
<tr>
<td >{datetime}</td>
<td>上传日期时间</td>
<td>20180118121049</td>
<td>全部</td>
</tr>
<tr>
<td >{uid}</td>
<td>上传者UID</td>
<td>154</td>
<td>全部</td>
</tr>
<tr>
<td >{timestamp}</td>
<td>时间戳</td>
<td>1516277624</td>
<td>全部</td>
</tr>
<tr>
<td >{randomkey16}</td>
<td>16位随机字符</td>
<td>16D8lhjErTDWAQjW</td>
<td>全部</td>
</tr>
<tr>
<td >{randomkey8}</td>
<td>8位随机字符</td>
<td>hM4Tpdh6</td>
<td>全部</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/add_policy.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -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}
@@ -0,0 +1,175 @@
{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-primary" role="alert">
创建储存桶后请在储存桶控制面板添加CORS跨域规则,允许本站的POST请求。请确保您的服务器和用户可以正常连接到AWS服务器。
</ul>
</div>
<div class="card" id="s3" >
<div class="card-header">
添加S3上传策略
</div>
<div class="card-body">
<form id="qiniuPolicy">
<input type="text" class="form-control" name="policy_type" value="s3" 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">AWSAccessKeyId </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="ak" required></div>
<div class="col-md-4 option-des"> AWS账户的访问秘钥信息,请在 控制面板 - 您的安全凭证 中创建</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="sk" class="col-form-label col-form-label-sm">AWSSecretKey </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>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="bucketname" class="col-form-label col-form-label-sm">存储桶名称 </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" required></div>
<div class="col-md-4 option-des"> 创建桶时填写的名称</div>
</div>
<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="url" required>
</div>
<div class="col-md-4 option-des"> 一般格式为:https://s3-分区名.amazonaws.com/储存桶名/ 结尾要加"/"</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" required>
</div>
<div class="col-md-4 option-des"> 一般格式为:https://储存桶名.s3-分区代号.amazonaws.com/</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="op_name" required>
</div>
<div class="col-md-4 option-des"> 例如:ap-southeast-1</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">EndPoint</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="op_name" required>
</div>
<div class="col-md-4 option-des"> 一般格式为:s3-储存区域.amazonaws.com,只填写域名部分</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 type="text" class="form-control" name="filetype" value="image/*,jpg,png,gif,bmp,mp4,mp3,txt" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 多个请以半角逗号","隔开,留空表示不限制。如果允许图片类型文件,除了图片类型后缀外,请额外添加一项"image/*"</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>
<input type="text" name="autoname" value="0" style="display: none">
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{uid}" style="display: none" required>
<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">
</script>
{/block}
@@ -0,0 +1,236 @@
{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="#">管理面板</a>
</li>
<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">
<div class="card-body-icon">
<i class="fa fa-fw fa-users"></i>
</div>
<div class="mr-5">{$statics.userNum} 位用户</div>
</div>
<a class="card-footer text-white clearfix small z-1" href="/Admin/Users">
<span class="float-left">查看详情</span>
<span class="float-right">
<i class="fa fa-angle-right"></i>
</span>
</a>
</div>
</div>
<div class="col-xl-3 col-sm-6 mb-3">
<div class="card text-white bg-warning o-hidden h-100">
<div class="card-body">
<div class="card-body-icon">
<i class="fa fa-fw fa-file"></i>
</div>
<div class="mr-5">{$statics.fileNum} 个文件</div>
</div>
<a class="card-footer text-white clearfix small z-1" href="/Admin/Files">
<span class="float-left">查看详情</span>
<span class="float-right">
<i class="fa fa-angle-right"></i>
</span>
</a>
</div>
</div>
<div class="col-xl-3 col-sm-6 mb-3">
<div class="card text-white bg-success o-hidden h-100">
<div class="card-body">
<div class="card-body-icon">
<i class="fa fa-fw fa-share-alt"></i>
</div>
<div class="mr-5">{$statics.publicShareNum} 个公开分享</div>
</div>
<a class="card-footer text-white clearfix small z-1" href="/Admin/Shares">
<span class="float-left">查看详情</span>
<span class="float-right">
<i class="fa fa-angle-right"></i>
</span>
</a>
</div>
</div>
<div class="col-xl-3 col-sm-6 mb-3">
<div class="card text-white bg-danger o-hidden h-100">
<div class="card-body">
<div class="card-body-icon">
<i class="fa fa-fw fa-lock"></i>
</div>
<div class="mr-5">{$statics.privateShareNum} 个私密分享</div>
</div>
<a class="card-footer text-white clearfix small z-1" href="/Admin/Shares">
<span class="float-left">查看详情</span>
<span class="float-right">
<i class="fa fa-angle-right"></i>
</span>
</a>
</div>
</div>
</div>
<!-- Area Chart Example-->
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-area-chart"></i> 近期趋势</div>
<div class="card-body">
<canvas id="myAreaChart" width="100%" height="30"></canvas>
</div>
</div>
<div class="row">
<div class="col-lg-8">
<!-- Example Bar Chart Card-->
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-bell-o"></i> 订阅新闻</div>
<div class="list-group list-group-flush small" id="news">
</div>
</div>
<!-- Card Columns Example Social Feed-->
<!-- /Card Columns-->
</div>
<div class="col-lg-4">
<!-- Example Pie Chart Card-->
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-pie-chart"></i> 文件组成</div>
<div class="card-body">
<canvas id="myPieChart" width="100%" height="80"></canvas>
</div>
<div class="card-footer small text-muted">*此结果仅供参考</div>
</div>
<!-- Example Notifications Card-->
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
<!-- /.content-wrapper-->
<footer class="sticky-footer">
<div class="container">
<div class="text-center">
<small>Copyright © Cloudreve {php}echo date("Y");{/php} </small>
</div>
</div>
</footer>
<!-- Scroll to Top Button-->
<a class="scroll-to-top rounded" href="#page-top">
<i class="fa fa-angle-up"></i>
</a>
<!-- Logout Modal-->
<!-- Bootstrap core JavaScript-->
{/block}
{block name="js"}
<script type="text/javascript">
$(document).ready(function() {
$.getJSON("https://cloudreve.org/api/notice/?callback=?", function(data){
$("#news").html(data[0]);
})
})
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif',
Chart.defaults.global.defaultFontColor = "#292b2c";
var ctx = document.getElementById("myAreaChart"),
myLineChart = new Chart(ctx, {
type: "line",
data: {
labels: [{$statics.trendDate}],
datasets: [{
label: "上传文件",
lineTension: .3,
backgroundColor: "rgba(2,117,216,0.2)",
borderColor: "rgba(2,117,216,1)",
pointRadius: 5,
pointBackgroundColor: "rgba(2,117,216,1)",
pointBorderColor: "rgba(255,255,255,0.8)",
pointHoverRadius: 5,
pointHoverBackgroundColor: "rgba(2,117,216,1)",
pointHitRadius: 20,
pointBorderWidth: 2,
data: [{$statics.trendFile}]
},{
label: "注册用户",
lineTension: .3,
backgroundColor: "rgba(83, 216, 2, 0.2)",
borderColor: "#4CAF50",
pointRadius: 5,
pointBackgroundColor: "#4CAF50",
pointBorderColor: "rgba(255,255,255,0.8)",
pointHoverRadius: 5,
pointHoverBackgroundColor: "#4CAF50",
pointHitRadius: 20,
pointBorderWidth: 2,
data: [{$statics.trendUser}]
},
]
},
options: {
scales: {
xAxes: [{
time: {
unit: "date"
},
gridLines: {
display: !1
},
ticks: {
maxTicksLimit: 7
}
}],
yAxes: [{
ticks: {
maxTicksLimit: 5
},
gridLines: {
color: "rgba(0, 0, 0, .125)"
}
}]
},
legend: {
display: !1
}
}
}),
ctx = document.getElementById("myPieChart"),
myPieChart = new Chart(ctx, {
type: "pie",
data: {
labels: ["其他", "图片", "音频", "视频"],
datasets: [{
data: [{$statics.otherRate}, {$statics.imgRate}, {$statics.audioRate}, {$statics.videoRate}],
backgroundColor: ["#007bff", "#dc3545", "#ffc107", "#28a745"]
}]
}
});
</script>
{/block}
@@ -0,0 +1,83 @@
{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>
<form id="basicOptionsForm">
<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 type="text" class="form-control" name="siteName" value="{$options.siteName}"></div>
<div class="col-md-4 option-des"> 站点的主标题,将会展示在个页面的标题尾部、邮件中。</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 type="text" class="form-control" name="siteTitle" value="{$options.siteTitle}"></div>
<div class="col-md-4 option-des"> 只会展示在首页的副标题。</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">站点URL</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="siteURL" value="{$options.siteURL}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 站点的URL,此设置很重要,请如实填写,否则会影响系统正常工作.</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 type="text" class="form-control" name="siteKeywords" value="{$options.siteKeywords}"></div>
<div class="col-md-4 option-des"> 位于首页meta标签内的站点关键词</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 type="text" class="form-control" name="siteDes" value="{$options.siteDes}"></div>
<div class="col-md-4 option-des"> 位于首页meta标签内的站点描述</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"> <textarea class="form-control" name="js_code" rows="4" spellcheck="false">{$options.js_code}</textarea></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="button" class="btn btn-primary" id="saveBasic">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/setting.js"></script>
<script type="text/javascript">
</script>
{/block}
+98
View File
@@ -0,0 +1,98 @@
{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>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>配色方案</h2>
<div class="alert alert-primary" role="alert">
<ul>
<li>你可以使用<a href="https://theme.cloudreve.org">Cloudreve配色工具</a>创建配色,并将生成的配置信息填写在下方;</li>
<li>主色调为不同配色的区分标识,请不要创建主色调相同的配色方案。</li>
</ul>
</div>
<div class="row">
<div class="col-md-9">
<form id="colorForm">
<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"><select class="form-control" name="header">
{volist name="colors" id="theme"}
<option value="{$key}" {eq name="optionsForSet.defaultTheme" value="$key"}selected{/eq}>{$key}</option>
{/volist}
</select></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-8">
<table class="table" id="colorTable">
<thead>
<tr>
<th scope="col">主色调</th>
<th scope="col">颜色配置</th>
</tr>
</thead>
<tbody>
{volist name="colors" id="theme"}
<tr>
<th scope="row" style="color:{$key}">{$key}
<br><button id="removeRow" type="button" data-action="removeRow" class="btn btn-default">删除</button>
</th>
<td><textarea class="form-control" rows="4" name="color[]">{:json_encode($theme)}</textarea></td>
</tr>
{/volist}
</tbody>
</table>
<div style="text-align:right;"><button id="addRow" type="button" class="btn btn-default">增加一行</button></div>
</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="saveColor">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
<br><br>
</div>
<div class="col-md-3">
<div class="file_side">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/color.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,51 @@
{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>
{$path}
<br> <br>
<div class="row">
<div class="col-md-9">
<input type="text" id="configType" value="{$type}" style="display: none">
<textarea rows="15" class="form-control" spellcheck="false">{$content}</textarea>
<br>
<button class="btn btn-primary" id="save">保存</button>
<br><br>
</div>
<div class="col-md-3">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link" id="v-pills-home-tab" href="/Admin/Config/type/common" role="tab" data-type="common">系统公共配置</a>
<a class="nav-link" id="v-pills-profile-tab" href="/Admin/Config/type/database" role="tab" data-type="database">数据库</a>
<a class="nav-link" id="v-pills-messages-tab" href="/Admin/Config/type/route" role="tab" data-type="route">路由</a>
<a class="nav-link" id="v-pills-settings-tab" href="/Admin/Config/type/tags" role="tab" data-type="tags">行为扩展</a>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/config.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,84 @@
{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>
{eq name="neverExcute" value="true"}
<div class="alert alert-warning" role="alert">
定时任务从未执行!请将 {$options.siteURL}Cron 加入到您的定时任务列队中(如Crontab)。
</div>
{/eq}
{volist name="cron" id="c"}
<div class="card">
<div class="card-header">
{$c.name}
</div>
<div class="card-body">
<blockquote class="blockquote mb-0">
<p>{$c.des}</p>
</blockquote><form id="cronForm" method="post" action="/Admin/SaveCron">
<div class="row">
<input type="text" name="id" value="{$c.id}" style="display: none;">
<div class="col-md-4">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">优先级</span>
</div>
<input type="number" min="0" class="form-control" name="rank" placeholder="值越高越优先" value="{$c.rank}">
</div>
</div>
<div class="col-md-4">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">执行间隔(s)</span>
</div>
<input type="number" min="1" class="form-control" name="interval_s" value="{$c.interval_s}">
</div>
</div>
<div class="col-md-4">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">启用</span>
</div>
<input type="number" min="0" max="1" class="form-control" name="enable" value="{$c.enable}">
</div>
</div>
</div>
<input type="submit" class="btn btn-primary" value="保存">
</form>
</div>
<div class="card-footer text-muted">
上次执行:{eq name="c.last_excute" value="0"}从未执行{else/}{:date("Y-m-d H:i:s",$c.last_excute)}{/eq}
</div>
</div><br>
{/volist}
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script type="text/javascript">
</script>
{/block}
+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>{:is_array($originList[$key]['fileName'])?"未知":$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}
@@ -0,0 +1,188 @@
{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>
<form id="editGroup">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<input type="text" name="id" value="{$group.id}" style="display: none">
<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="group_name" value="{$group.group_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="colFormLabelSm" class="col-form-label col-form-label-sm">上传策略</label>
</div>
<div class="col-md-4">
<select name="policy_name" required class="form-control" >
{volist name="policy" id="p"}
<option value="{$p.id}">{$p.policy_name}</option>
{/volist}
</select>
</div>
<div class="col-md-4 option-des"> 为该用户组绑定一个上传策略</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-group mb-3">
<input type="number" class="form-control" name="max_storage" spellcheck="false" min="0" value="{:countSize($group.max_storage,true)[0]}" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn" data-value="{:countSize($group.max_storage,true)[1]}">
<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="colFormLabelSm" 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="speed" spellcheck="false" min="0" placeholder="留空表示不限速,0表示禁止下载" value="{$group.speed}">
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
Kb/s
</span>
</div>
</div>
<div class="col-md-4 option-des"> 限制该用户组下的用户上传的文件的下载速度,<strong>此设置仅对本地上传方案有效</strong></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="range_transfer" value="1" id="allow_range_1">
<label class="" for="allow_range_1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="range_transfer" id="allow_range_0" value="0" >
<label class="" for="allow_range_0">禁止</label></div>
<div class="col-md-4 option-des"> 是否断点续传及多线程下载,关闭后预览视频不支持拖拽播放,<strong>此设置仅对本地上传方案有效</strong></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="allow_share" value="1" id="allow_share_1" checked>
<label class="" for="allow_share_1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="allow_share" id="allow_share_0" value="0" >
<label class="" for="allow_share_0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许创建文件分享链接</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">WebDAV支持</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="webdav" value="1" id="webdav1" checked>
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="webdav" id="webdav0" value="0" >
<label class="" for="webdav0">禁止</label></div>
<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>
</div>
<div class="col-md-4">
<input class="" type="radio" name="color" value="default" id="default" checked>
<label class="" for="default" ><span class="badge badge-primary">蓝色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="primary" id="primary">
<label class="" for="primary" ><span class="badge badge-secondary">灰色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="success" id="success">
<label class="" for="success" ><span class="badge badge-success">绿色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="danger" id="danger">
<label class="" for="danger" ><span class="badge badge-danger">红色</span></label>
<br>
<input class="" type="radio" name="color" value="warning" id="warning">
<label class="" for="warning" ><span class="badge badge-warning">黄色</span></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="color" value="info" id="info">
<label class="" for="info" ><span class="badge badge-info">浅蓝色</span></label>
&nbsp;&nbsp;&nbsp;
</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="saveGroup">保存</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/add_group.js"></script>
<script type="text/javascript">
$("option[value='{$group.policy_name}']").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}
File diff suppressed because it is too large Load Diff
+194
View File
@@ -0,0 +1,194 @@
{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>
</ol>
<div class="row">
<div class="col-12">
<h2>文件</h2>
<br>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-tags"></i> 条件检索</div>
<div class="card-body">
<div class="row">
<div class="col-1 form-label-search mb-2">排序方式</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="order">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="id DESC">日期</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="orign_name ASC">文件名</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="size DESC">大小</a>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-1 form-label-search mb-2">上传策略</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="policy_select">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="">全部</a>
</li>
{volist name="policy" id="policy"}
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="{$policy.id}">{$policy.policy_name}</a>
</li>
{/volist}
</ul>
</div>
</div>
<div class="row" style="margin-top: 5px;">
<div class="col-1 form-label-search mb-2">高级检索</div>
<div class="col-md-10">
<div class="row">
<div class="col-md-2">
<select class="form-control form-control-sm" id="searchCol">
<option value="id" selected>ID</option>
<option value="orign_name">文件名</option>
<option value="pre_name">实际文件名</option>
<option value="upload_user">上传者UID</option>
<option value="upload_date">上传日期</option>
<option value="pic_info">图像尺寸</option>
<option value="parent_folder">所属目录ID</option>
<option value="policy_id">上传策略ID</option>
<option value="dir">目录路径</option>
</select>
</div>
<div class="col-md-2">
<input type="text" class="form-control form-control-sm" placeholder="检索表达式" id="searchValue">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-primary btn-sm" id="applySearch">应用</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-table"></i> 文件列表</div>
<div class="card-body">
<div class="table-responsive">
<div id="dataTable_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"><div class="dataTables_length" ><label>每页展示 <select id="dataTable_length" aria-controls="dataTable" class="form-control form-control-sm"><option value="10">10</option><option value="25">25</option><option value="50">50</option><option value="100">100</option></select></label></div></div><div class="col-sm-12 col-md-6"><div id="dataTable_filter" class="dataTables_filter"><label>搜索:<input type="search" class="form-control form-control-sm" placeholder="" id="searchFrom"></label></div></div></div><div class="row"><div class="col-sm-12">
<div class="table-responsive">
<table class="table table-bordered dataTable" id="dataTable" width="100%" cellspacing="0" role="grid" aria-describedby="dataTable_info" style="width: 100%;">
<thead>
<tr role="row" class="textCenter"><th><input type="checkbox" data-type="all"></th><th>文件名</th><th class="textCenter">大小</th><th class="textCenter">上传者</th><th class="textCenter">日期</th><th class="textCenter">操作</th></tr>
</thead>
<tfoot>
<tr><th rowspan="1" colspan="1" class="textCenter"><input type="checkbox" data-type="all"></th><th rowspan="1" colspan="1">文件名</th><th rowspan="1" colspan="1" class="textCenter">大小</th><th rowspan="1" colspan="1" class="textCenter">上传者</th><th rowspan="1" colspan="1" class="textCenter">日期</th><th rowspan="1" colspan="1" class="textCenter">操作</th></tr>
</tfoot>
<tbody>
<tr role="row" class="odd" id="fileList">
{volist name='list' id='file'}
<tr role="row" class="odd">
<td class="sorting_1 textCenter"><input type="checkbox" data-type="mark" data-path="{:rtrim($file.dir,'/')}/{$file.orign_name}" data-uid="{$file.upload_user}"></td>
<td>
{eq name=":isPreview($file.orign_name)" value="yes"}
<a href="/Admin/Preview/id/{$file.id}" class="noAColor" target="_blank">{$file.orign_name|htmlspecialchars=ENT_NOQUOTES}</a>
{else}
<a href="/Admin/Download/id/{$file.id}" class="noAColor" target="_blank">{$file.orign_name|htmlspecialchars=ENT_NOQUOTES}</a>
{/eq}
</td>
<td class="textCenter">{:countSize($file.size)}</td>
<td class="textCenter">{$originList[$key]["user"]["user_nick"]}</td>
<td class="textCenter">{$file.upload_date}</td>
<td class="textCenter"><button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="删除" data-action="delete" data-id="{$file.id}"><i class="fa fa-trash" aria-hidden="true"></i></button>
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="下载" data-action="download" data-id="{$file.id}"><i class="fa fa-download" aria-hidden="true"></i></button>
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="详情" data-action="info" data-id="{$file.id}"><i class="fa fa-info-circle" aria-hidden="true"></i></button>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
<div class="col-md-5 mb-2 mt-2" style="display: none" id="del">
<button type="button" class="btn btn-danger" id="delAll"><i class="fa fa-trash" aria-hidden="true"></i> 删除选中文件</button>
</div>
</div></div><div class="row"><div class="col-sm-12 col-md-5"><div class="dataTables_info" id="dataTable_info" role="status" aria-live="polite">展示第 {$pageNow} 页,共 {$pageTotal} 页 {$dataTotal} 条数据</div></div><div class="col-sm-12 col-md-7">
<div class="dataTables_paginate paging_simple_numbers" id="dataTable_paginate">
{$list->render()}
</div></div></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<div class="modal fade bd-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true" id="fileInfo">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">文件详情</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12"><strong>ID:</strong></div>
<div class="col-md-12 break" id="fileId">12</div>
</div>
<div class="row">
<div class="col-md-12"><strong>文件名:</strong></div>
<div class="col-md-12 break" id="fileName">5c06e532e7afdb4389e4ae846ea7876c_w - 副本 - 副本.jpg</div>
</div>
<div class="row">
<div class="col-md-12"><strong>源文件名:</strong></div>
<div class="col-md-12 break" id="fileOrigin">5c06e532e7afdb4389e4ae846ea7876c_w - 副本 - 副本.jpg</div>
</div>
<div class="row">
<div class="col-md-12"><strong>上传者UID:</strong></div>
<div class="col-md-12 break" id="fileUID">17</div>
</div>
<div class="row">
<div class="col-md-12"><strong>大小(字节):</strong></div>
<div class="col-md-12 break" id="fileSize">7481817</div>
</div>
<div class="row">
<div class="col-md-12"><strong>图像尺寸:</strong></div>
<div class="col-md-12 break" id="filePic">147,445</div>
</div>
<div class="row">
<div class="col-md-12"><strong>上传策略:</strong></div>
<div class="col-md-12 break" id="filePolicy">147,445</div>
</div>
<div class="row">
<div class="col-md-12"><strong>所属目录:</strong></div>
<div class="col-md-12 break" id="fileDir">147,445</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/files.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,131 @@
{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>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>用户组</h2>
<br>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-table"></i> 用户组列表</div>
<div class="card-body">
<div class="table-responsive">
<div id="dataTable_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"><div class="dataTables_length" ><label>每页展示 <select id="dataTable_length" aria-controls="dataTable" class="form-control form-control-sm"><option value="10">10</option><option value="25">25</option><option value="50">50</option><option value="100">100</option></select></label></div></div></div><div class="row"><div class="col-sm-12">
<div class="table-responsive">
<table class="table table-bordered dataTable" id="dataTable" width="100%" cellspacing="0" role="grid" aria-describedby="dataTable_info" style="width: 100%;">
<thead>
<tr role="row" class="textCenter"><th>#</th><th>名称</th><th class="textCenter">上传策略</th><th class="textCenter">最大容量</th><th class="textCenter">下属用户数</th><th class="textCenter">操作</th></tr>
</thead>
<tfoot>
<tr><th rowspan="1" colspan="1" class="textCenter">#</th><th rowspan="1" colspan="1">名称</th><th rowspan="1" colspan="1" class="textCenter">上传策略</th><th rowspan="1" colspan="1" class="textCenter">最大容量</th><th rowspan="1" colspan="1" class="textCenter">下属用户数</th><th rowspan="1" colspan="1" class="textCenter">操作</th></tr>
</tfoot>
<tbody>
<tr role="row" class="odd" id="fileList">
{volist name='list' id='group'}
<tr role="row" class="odd">
<td class="sorting_1 textCenter">{$group.id}</td>
<td>
{$group.group_name}
</td>
<td class="textCenter">
{$originList[$key]["policy"]["policy_name"]}
</td>
<td class="textCenter">{:countSize($group["max_storage"])}</td>
<td class="textCenter">{$originList[$key]["user_num"]}</td>
<td class="textCenter"><button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="删除" data-action="delete" data-id="{$group.id}" data-unable="{eq name="$originList[$key]['user_num']" value="0"}0{else}1{/eq}"><i class="fa fa-trash" aria-hidden="true"></i></button>
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="编辑" data-action="edit" data-id="{$group.id}"><i class="fa fa-edit" aria-hidden="true"></i></button>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
<div class="col-md-5 mb-2 mt-2" style="display: none" id="del">
<button type="button" class="btn btn-danger" id="delAll"><i class="fa fa-trash" aria-hidden="true"></i> 删除选中文件</button>
</div>
</div></div><div class="row"><div class="col-sm-12 col-md-5"><div class="dataTables_info" id="dataTable_info" role="status" aria-live="polite">展示第 {$pageNow} 页,共 {$pageTotal} 页 {$dataTotal} 条数据</div></div><div class="col-sm-12 col-md-7">
<div class="dataTables_paginate paging_simple_numbers" id="dataTable_paginate">
{$list->render()}
</div></div></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<div class="modal fade bd-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true" id="fileInfo">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">文件详情</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12"><strong>ID:</strong></div>
<div class="col-md-12 break" id="fileId">12</div>
</div>
<div class="row">
<div class="col-md-12"><strong>文件名:</strong></div>
<div class="col-md-12 break" id="fileName">5c06e532e7afdb4389e4ae846ea7876c_w - 副本 - 副本.jpg</div>
</div>
<div class="row">
<div class="col-md-12"><strong>源文件名:</strong></div>
<div class="col-md-12 break" id="fileOrigin">5c06e532e7afdb4389e4ae846ea7876c_w - 副本 - 副本.jpg</div>
</div>
<div class="row">
<div class="col-md-12"><strong>上传者UID:</strong></div>
<div class="col-md-12 break" id="fileUID">17</div>
</div>
<div class="row">
<div class="col-md-12"><strong>大小(字节):</strong></div>
<div class="col-md-12 break" id="fileSize">7481817</div>
</div>
<div class="row">
<div class="col-md-12"><strong>图像尺寸:</strong></div>
<div class="col-md-12 break" id="filePic">147,445</div>
</div>
<div class="row">
<div class="col-md-12"><strong>上传策略:</strong></div>
<div class="col-md-12 break" id="filePolicy">147,445</div>
</div>
<div class="row">
<div class="col-md-12"><strong>所属目录:</strong></div>
<div class="col-md-12 break" id="fileDir">147,445</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/group_list.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,206 @@
{extend name="header_admin" /}
{block name="title"}邮件收发- {$options.siteName}{/block}
{block name="content"}
<link href="/static/css/admin/summernote.css" rel="stylesheet">
<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>
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="pills-home-tab" data-toggle="pill" href="#basic" role="tab" aria-controls="pills-home" aria-selected="true">基本设置</a>
</li>
<li class="nav-item">
<a class="nav-link" id="pills-profile-tab" data-toggle="pill" href="#test" role="tab" aria-controls="pills-profile" aria-selected="false">发信测试</a>
</li>
<li class="nav-item">
<a class="nav-link" id="pills-contact-tab" data-toggle="pill" href="#template" role="tab" aria-controls="pills-contact" aria-selected="false">邮件模板</a>
</li>
</ul>
<div class="tab-content" >
<div class="tab-pane fade show active" id="basic" role="tabpanel" aria-labelledby="pills-home-tab">
<form id="basicOptionsForm">
<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="fromName" value="{$optionsForSet.fromName}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 显示在邮件发件邮箱旁的发件人姓名</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromAdress" class="col-form-label col-form-label-sm">发件人邮箱</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="fromAdress" value="{$optionsForSet.fromAdress}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 发件邮箱的地址</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="smtpHost" class="col-form-label col-form-label-sm">SMTP服务器</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="smtpHost" value="{$optionsForSet.smtpHost}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 发信邮箱的SMTP服务器地址</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="smtpPort" class="col-form-label col-form-label-sm">端口</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="smtpPort" value="{$optionsForSet.smtpPort}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 发信邮箱SMTP服务器的端口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="smtpPort" class="col-form-label col-form-label-sm">加密方式</label>
</div>
<div class="col-md-4">
<select class="form-control" name="encriptionType">
<option value="no" {eq name="$optionsForSet.encriptionType" value="no"}selected{/eq}>不启用</option>
<option value="tls" {eq name="$optionsForSet.encriptionType" value="tls"}selected{/eq}>TLS</option>
<option value="ssl" {eq name="$optionsForSet.encriptionType" value="ssl"}selected{/eq}>SSL</option>
</select>
</div>
<div class="col-md-4 option-des"> </div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="smtpUser" class="col-form-label col-form-label-sm">用户名</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="smtpUser" value="{$optionsForSet.smtpUser}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 发信邮箱用户名,一般与邮箱相同</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="smtpPass" class="col-form-label col-form-label-sm">密码</label>
</div>
<div class="col-md-4"> <input type="password" class="form-control" name="smtpPass" value="{$optionsForSet.smtpPass}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 发信邮箱密码</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="replyTo" class="col-form-label col-form-label-sm">回信邮箱</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="replyTo" value="{$optionsForSet.replyTo}" spellcheck="false"></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="button" class="btn btn-primary" id="saveMail">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</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">
<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="receiveMail" spellcheck="false"></div>
<div class="col-md-4 option-des"> 接受测试邮件的收件人邮箱</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="subject" spellcheck="false" value="这是一封测试邮件"></div>
<div class="col-md-4 option-des"> 测试邮件的主题</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-8"> <textarea class="form-control" name="content" spellcheck="false" id="content" > 这是一封由Cloudreve发出的测试邮件,用于测试邮件设置是否正常工作。</textarea></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="sendMail">发送测试邮件</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
<div class="tab-pane fade" id="template" role="tabpanel" aria-labelledby="pills-profile-tab">
<form id="mailTemplate">
<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-8"> <textarea class="form-control textEdit" name="mail_activation_template" spellcheck="false" > {$optionsForSet.mail_activation_template}</textarea>
</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-8"> <textarea class="form-control textEdit" name="mail_reset_pwd_template" spellcheck="false" > {$optionsForSet.mail_reset_pwd_template}</textarea>
</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="saveTemplate">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
</div>
</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/setting.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#content').summernote({
lang:"zh-CN",
height: 150,
});
});
$(document).ready(function() {
$('.textEdit').summernote({
lang:"zh-CN",
height: 300,
});
});
</script>
{/block}
@@ -0,0 +1,127 @@
{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>
<form id="mailTemplate">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="maxEditSize" class="col-form-label col-form-label-sm">文本编辑大小限制</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="maxEditSize" value="{$optionsForSet.maxEditSize}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 用户可在线编辑的文本类文件的最大尺寸。单位:字节</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="maxEditSize" class="col-form-label col-form-label-sm">允许游客下载</label>
</div>
<div class="col-md-4">
{eq name="$optionsForSet.allowdVisitorDownload" value="true"}
<input class="" type="radio" name="allowdVisitorDownload" id="inlineRadio1" value="true" checked>
<label class="" for="inlineRadio1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="allowdVisitorDownload" id="inlineRadio2" value="false">
<label class="" for="inlineRadio2">拒绝</label>
{else}
<input class="" type="radio" name="allowdVisitorDownload" id="inlineRadio1" value="true">
<label class="" for="inlineRadio1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="allowdVisitorDownload" id="inlineRadio2" value="false" checked>
<label class="" for="inlineRadio2">拒绝</label>
{/eq}
</div>
<div class="col-md-4 option-des"> 是否允许未登录用户下载被分享的文件</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="hot_share_num" class="col-form-label col-form-label-sm">热门分享展示数量</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="hot_share_num" value="{$optionsForSet.hot_share_num}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 在用户个人主页展示的热门分享的个数</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">Gravatar服务器</label>
</div>
<div class="col-md-4"> <input type="email" class="form-control" name="gravatar_server" value="{$optionsForSet.gravatar_server}" spellcheck="false"></div>
<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>
<div class="col-md-4"> <button type="button" class="btn btn-primary" id="saveTemplate">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/setting.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,176 @@
{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>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>上传策略</h2>
<br>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-tags"></i> 条件检索</div>
<div class="card-body">
<div class="row">
<div class="col-1 form-label-search mb-2">类型</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="policyType">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="">全部</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="local">本地</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="qiniu">七牛</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="oss">OSS</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="upyun">又拍云</a>
</li>
<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>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-table"></i> 上传策略列表</div>
<div class="card-body">
<div class="table-responsive">
<div id="dataTable_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"><div class="dataTables_length" ><label>每页展示 <select id="dataTable_length" aria-controls="dataTable" class="form-control form-control-sm"><option value="10">10</option><option value="25">25</option><option value="50">50</option><option value="100">100</option></select></label></div></div><div class="col-sm-12 col-md-6"><div id="dataTable_filter" class="dataTables_filter"><label>搜索:<input type="search" class="form-control form-control-sm" placeholder="" id="searchFrom"></label></div></div></div><div class="row"><div class="col-sm-12">
<div class="table-responsive">
<table class="table table-bordered dataTable" id="dataTable" width="100%" cellspacing="0" role="grid" aria-describedby="dataTable_info" style="width: 100%;">
<thead>
<tr role="row" class="textCenter"><th>#</th><th>名称</th><th class="textCenter">类型</th><th class="textCenter">下属文件数</th><th class="textCenter">数据量</th><th class="textCenter">操作</th></tr>
</thead>
<tfoot>
<tr><th rowspan="1" colspan="1" class="textCenter">#</th><th rowspan="1" colspan="1">名称</th><th rowspan="1" colspan="1" class="textCenter">类型</th><th rowspan="1" colspan="1" class="textCenter">下属文件数</th><th rowspan="1" colspan="1" class="textCenter">数据量</th><th rowspan="1" colspan="1" class="textCenter">操作</th></tr>
</tfoot>
<tbody>
<tr role="row" class="odd" id="fileList">
{volist name='list' id='policy'}
<tr role="row" class="odd">
<td class="sorting_1 textCenter">{$policy.id}</td>
<td>
{$policy.policy_name}
</td>
<td class="textCenter">
{switch name="policy.policy_type"}
{case value="local"}本地{/case}
{case value="qiniu"}七牛{/case}
{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>
<td class="textCenter">{$originList[$key]["file_num"]}</td>
<td class="textCenter">{:countSize($originList[$key]["file_size"])}</td>
<td class="textCenter"><button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="删除" data-action="delete" data-id="{$policy.id}" data-unable="{eq name="$originList[$key]['file_num']" value="0"}0{else}1{/eq}"><i class="fa fa-trash" aria-hidden="true"></i></button>
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="编辑" data-action="edit" data-id="{$policy.id}"><i class="fa fa-edit" aria-hidden="true"></i></button>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
<div class="col-md-5 mb-2 mt-2" style="display: none" id="del">
<button type="button" class="btn btn-danger" id="delAll"><i class="fa fa-trash" aria-hidden="true"></i> 删除选中文件</button>
</div>
</div></div><div class="row"><div class="col-sm-12 col-md-5"><div class="dataTables_info" id="dataTable_info" role="status" aria-live="polite">展示第 {$pageNow} 页,共 {$pageTotal} 页 {$dataTotal} 条数据</div></div><div class="col-sm-12 col-md-7">
<div class="dataTables_paginate paging_simple_numbers" id="dataTable_paginate">
{$list->render()}
</div></div></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<div class="modal fade bd-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true" id="fileInfo">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">文件详情</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12"><strong>ID:</strong></div>
<div class="col-md-12 break" id="fileId">12</div>
</div>
<div class="row">
<div class="col-md-12"><strong>文件名:</strong></div>
<div class="col-md-12 break" id="fileName">5c06e532e7afdb4389e4ae846ea7876c_w - 副本 - 副本.jpg</div>
</div>
<div class="row">
<div class="col-md-12"><strong>源文件名:</strong></div>
<div class="col-md-12 break" id="fileOrigin">5c06e532e7afdb4389e4ae846ea7876c_w - 副本 - 副本.jpg</div>
</div>
<div class="row">
<div class="col-md-12"><strong>上传者UID:</strong></div>
<div class="col-md-12 break" id="fileUID">17</div>
</div>
<div class="row">
<div class="col-md-12"><strong>大小(字节):</strong></div>
<div class="col-md-12 break" id="fileSize">7481817</div>
</div>
<div class="row">
<div class="col-md-12"><strong>图像尺寸:</strong></div>
<div class="col-md-12 break" id="filePic">147,445</div>
</div>
<div class="row">
<div class="col-md-12"><strong>上传策略:</strong></div>
<div class="col-md-12 break" id="filePolicy">147,445</div>
</div>
<div class="row">
<div class="col-md-12"><strong>所属目录:</strong></div>
<div class="col-md-12 break" id="fileDir">147,445</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/policy.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,134 @@
{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>
<form id="basicOptionsForm">
<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">
{eq name="$optionsForSet.regStatus" value="0"}
<input class="" type="radio" name="regStatus" id="inlineRadio1" value="0"checked>
<label class="" for="inlineRadio1" ></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="regStatus" id="inlineRadio2" value="1">
<label class="" for="inlineRadio2"></label>
{else}
<input class="" type="radio" name="regStatus" id="inlineRadio1" value="0">
<label class="" for="inlineRadio1" ></label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="regStatus" id="inlineRadio2" value="1" checked>
<label class="" for="inlineRadio2"></label>
{/eq}
</div>
<div class="col-md-4 option-des"> 是否允许新用户注册</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">
<select class="form-control" name="defaultGroup">
{volist name="groups" id="group"}
{eq name="$group.id" value="$optionsForSet.defaultGroup"}
<option value="{$group.id}" selected>{$group.group_name}</option>
{else}
<option value="{$group.id}">{$group.group_name}</option>
{/eq}
{/volist}
</select>
</div>
<div class="col-md-4 option-des"> 用户注册后默认属于的用户组</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 type="checkbox" id="inlineCheckbox3" name="email_active" value="1" {eq name="$optionsForSet.email_active" value="1" }checked{/eq}>
<label for="inlineCheckbox3">启用</label>
</div>
<div class="col-md-4 option-des"> 启用后,新用户注册需要点击邮件中的激活链接才能完成。请确认邮件发送设置是否正确,否则激活邮件无法送达</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 type="checkbox" id="login_captcha" name="login_captcha" value="1" {eq name="$optionsForSet.login_captcha" value="1" }checked{/eq}>
<label for="login_captcha">启用</label>
</div>
<div class="col-md-4 option-des"> 是否启用登录表单验证码</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 type="checkbox" id="reg_captcha" name="reg_captcha" value="1" {eq name="$optionsForSet.reg_captcha" value="1" }checked{/eq}>
<label for="reg_captcha">启用</label>
</div>
<div class="col-md-4 option-des"> 是否启用注册表单验证码</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 type="checkbox" id="forget_captcha" name="forget_captcha" value="1" {eq name="$optionsForSet.forget_captcha" value="1" }checked{/eq}>
<label for="forget_captcha">启用</label>
</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="button" class="btn btn-primary" id="saveReg">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/setting.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,110 @@
{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>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>分享</h2>
<br>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-tags"></i> 条件检索</div>
<div class="card-body">
<div class="row">
<div class="col-1 form-label-search mb-2">排序方式</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="order">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="id DESC">日期</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="download_num DESC">下载次数</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="view_num DESC">浏览次数</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-table"></i> 分享列表</div>
<div class="card-body">
<div class="table-responsive">
<div id="dataTable_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"><div class="dataTables_length" ><label>每页展示 <select id="dataTable_length" aria-controls="dataTable" class="form-control form-control-sm"><option value="10">10</option><option value="25">25</option><option value="50">50</option><option value="100">100</option></select></label></div></div><div class="col-sm-12 col-md-6"><div id="dataTable_filter" class="dataTables_filter"><label>搜索:<input type="search" class="form-control form-control-sm" placeholder="" id="searchFrom"></label></div></div></div><div class="row"><div class="col-sm-12">
<div class="table-responsive">
<table class="table table-bordered dataTable" id="dataTable" width="100%" cellspacing="0" role="grid" aria-describedby="dataTable_info" style="width: 100%;">
<thead>
<tr role="row" class="textCenter"><th><input type="checkbox" data-type="all"></th><th>分享名称</th><th class="textCenter">分享者</th><th class="textCenter">类型</th><th class="textCenter">日期</th><th class="textCenter">浏览次数</th><th class="textCenter">下载次数</th><th class="textCenter">操作</th></tr>
</thead>
<tfoot>
<tr><th rowspan="1" colspan="1" class="textCenter"><input type="checkbox" data-type="all"></th><th rowspan="1" colspan="1" class="textCenter">分享名称</th><th rowspan="1" colspan="1" class="textCenter">分享者</th><th rowspan="1" colspan="1" class="textCenter">类型</th><th rowspan="1" colspan="1" class="textCenter">日期</th><th rowspan="1" colspan="1" class="textCenter">浏览次数</th><th rowspan="1" colspan="1" class="textCenter">下载次数</th><th rowspan="1" colspan="1" class="textCenter">操作</th></tr>
</tfoot>
<tbody>
<tr role="row" class="odd" id="fileList">
{volist name='list' id='share'}
<tr role="row" class="odd">
<td class="sorting_1 textCenter"><input type="checkbox" data-type="mark" data-id="{$share.id}"></td>
<td>
{eq name="$share.source_type" value="dir"}
<i class="fa fa-folder-open-o" aria-hidden="true"></i> <a href="/S/{$share.share_key}?pwd={$share.share_pwd}" class="noAColor" target="_blank">{:htmlspecialchars(getDirName($share.source_name),ENT_NOQUOTES)}</a>
{else}
<i class="fa fa-file" aria-hidden="true"></i> <a href="/S/{$share.share_key}?pwd={$share.share_pwd}" class="noAColor" target="_blank">{$share.origin_name|htmlspecialchars=ENT_NOQUOTES}</a>
{/eq}
</td>
<td class="textCenter">{$originList[$key]["user"]["user_nick"]}</td>
<td class="textCenter">
{eq name="share.type" value="public"}
<i class="fa fa-eye" aria-hidden="true"></i> 公开
{else/}
<i class="fa fa-lock" aria-hidden="true"></i> 私密
{/eq}
</td>
<td class="textCenter">{$share.share_time}</td>
<td class="textCenter">{$share.view_num}</td>
<td class="textCenter">{$share.download_num}</td>
<td class="textCenter"><button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="删除" data-action="delete" data-id="{$share.id}"><i class="fa fa-trash" aria-hidden="true"></i></button> <button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="变更分享类型" data-action="change" data-id="{$share.share_key}"><i class="fa fa-lock" aria-hidden="true"></i></button></td>
</tr>
{/volist}
</tbody>
</table>
</div>
<div class="col-md-5 mb-2 mt-2" style="display: none" id="del">
<button type="button" class="btn btn-danger" id="delAll"><i class="fa fa-trash" aria-hidden="true"></i> 删除选中文件</button>
</div>
</div></div><div class="row"><div class="col-sm-12 col-md-5"><div class="dataTables_info" id="dataTable_info" role="status" aria-live="polite">展示第 {$pageNow} 页,共 {$pageTotal} 页 {$dataTotal} 条数据</div></div><div class="col-sm-12 col-md-7">
<div class="dataTables_paginate paging_simple_numbers" id="dataTable_paginate">
{$list->render()}
</div></div></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/shares.js"></script>
<script type="text/javascript">
</script>
{/block}
+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}
+51
View File
@@ -0,0 +1,51 @@
{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>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>编辑模板</h2>
application/index/view/{:rtrim($path,"/")}/{$name}.html
<br> <br>
<div class="row">
<div class="col-md-9">
<input type="text" id="fileName" value="{$name}" style="display: none">
<textarea rows="15" class="form-control" spellcheck="false" style=" font-family: 'Courier New';">{:htmlspecialchars($content)}</textarea>
<br>
<button class="btn btn-primary" id="saveTheme">保存</button>
<br><br>
</div>
<div class="col-md-3">
<div class="file_side">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
{volist name="list" id="file"}
<a class="nav-link" id="v-pills-home-tab" href="/Admin/Theme/name/{:str_replace(".html","",$file)}" role="tab" data-name="{:str_replace(".html","",$file)}">{$file}</a>
{/volist}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/config.js"></script>
<script type="text/javascript">
</script>
{/block}
+298
View File
@@ -0,0 +1,298 @@
{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>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>用户 <button class="btn btn-secondary btn-sm" data-toggle="modal" data-target="#addUser"><li class="fa fa-plus"></li></button></h2>
<br>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-tags"></i> 条件检索</div>
<div class="card-body">
<div class="row">
<div class="col-1 form-label-search mb-2">排序方式</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="order">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="user_nick ASC">用户名</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="id DESC">注册日期</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-method="used_storage DESC">已用空间</a>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-1 form-label-search mb-2">用户组</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="groupS">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-group="">全部</a>
</li>
{volist name="group" id="group"}
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-group="{$group.id}">{$group.group_name}</a>
</li>
{/volist}
</ul>
</div>
</div>
<div class="row">
<div class="col-1 form-label-search mb-2">用户状态</div>
<div class="col-md-10">
<ul class="nav nav-pills" id="status">
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-status="">全部</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-status="1">正常</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-status="2">封禁/未激活</a>
</li>
</ul>
</div>
</div>
<div class="row" style="margin-top: 5px;">
<div class="col-1 form-label-search mb-2">高级检索</div>
<div class="col-md-10">
<div class="row">
<div class="col-md-2">
<select class="form-control form-control-sm" id="searchColUser">
<option value="id">用户UID</option>
<option value="user_nick">用户昵称</option>
<option value="user_date">注册日期</option>
<option value="user_email">邮箱</option>
<option value="group_primary">初始用户组</option>
<option value="user_activation_key">邮箱激活密钥</option>
<option value="used_storage">已用空间</option>
<option value="delay_time">用户组过期日期</option>
<option value="avatar">头像标识</option>
<option value="profile">个人主页状态</option>
</select>
</div>
<div class="col-md-2">
<input type="text" class="form-control form-control-sm" placeholder="检索表达式" id="searchValueUser">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-primary btn-sm" id="applySearch">应用</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-table"></i> 用户列表 </div>
<div class="card-body">
<div class="table-responsive">
<div id="dataTable_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"><div class="dataTables_length" ><label>每页展示 <select id="dataTable_length" aria-controls="dataTable" class="form-control form-control-sm"><option value="10">10</option><option value="25">25</option><option value="50">50</option><option value="100">100</option></select></label></div></div><div class="col-sm-12 col-md-6"><div id="dataTable_filter" class="dataTables_filter"><label>搜索:<input type="search" class="form-control form-control-sm" placeholder="" id="searchFrom"></label></div></div></div><div class="row"><div class="col-sm-12">
<div class="table-responsive">
<table class="table table-bordered dataTable" id="dataTable" width="100%" cellspacing="0" role="grid" aria-describedby="dataTable_info" style="width: 100%;">
<thead>
<tr role="row" class="textCenter"><th><input type="checkbox" data-type="all"></th><th>UID</th><th>昵称</th><th class="textCenter">邮箱</th><th class="textCenter">用户组</th><th class="textCenter">空间用量</th><th class="textCenter">状态</th><th class="textCenter">操作</th></tr>
</thead>
<tfoot>
<tr><th rowspan="1" colspan="1" class="textCenter"><input type="checkbox" data-type="all"></th><th rowspan="1" colspan="1" class="textCenter">UID</th><th rowspan="1" colspan="1" class="textCenter">昵称</th><th rowspan="1" colspan="1" class="textCenter">邮箱</th><th rowspan="1" colspan="1" class="textCenter">用户组</th><th rowspan="1" colspan="1" class="textCenter">空间用量</th><th rowspan="1" colspan="1" class="textCenter">状态</th><th rowspan="1" colspan="1" class="textCenter">操作</th></tr>
</tfoot>
<tbody>
<tr role="row" class="odd" id="userList">
{volist name='list' id='user'}
<tr role="row" class="odd">
<td class="sorting_1 textCenter"><input type="checkbox" data-type="mark" data-id="{$user.id}"></td>
<td class="textCenter">{$user.id}</td>
<td class="textCenter">{$user.user_nick}</td>
<td class="textCenter">{$user.user_email}</td>
<td class="textCenter">{:$originList[$key]["group"]["group_name"]}</td>
<td class="textCenter">{:countSize($user.used_storage)}</td>
<td class="textCenter">
{eq name="user.user_status" value="0"}
<span class="order_success">正常</span>
{else/}
<span class="order_warning">封禁/未激活</span>
{/eq}
</td>
<td class="textCenter">
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="删除" data-action="delete" data-id="{$user.id}" data-unable="{eq name="user.used_storage" value="0"}0{else/}1{/eq}"><i class="fa fa-trash" aria-hidden="true"></i></button>
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="编辑" data-action="edit" data-id="{$user.id}"><i class="fa fa-id-card" aria-hidden="true"></i></button>
<button type="button" class="btn btn-secondary fix" data-toggle="tooltip" data-placement="top" title="封号/解封" data-action="ban" data-id="{$user.id}"><i class="fa fa-ban" aria-hidden="true"></i></button>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
<div class="col-md-5 mb-2 mt-2" style="display: none" id="del">
<button type="button" class="btn btn-danger" id="delAll"><i class="fa fa-trash" aria-hidden="true"></i> 删除选中用户</button>
</div>
</div></div><div class="row"><div class="col-sm-12 col-md-5"><div class="dataTables_info" id="dataTable_info" role="status" aria-live="polite">展示第 {$pageNow} 页,共 {$pageTotal} 页 {$dataTotal} 条数据</div></div><div class="col-sm-12 col-md-7">
<div class="dataTables_paginate paging_simple_numbers" id="dataTable_paginate">
{$list->render()}
</div></div></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
<div class="modal fade bd-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true" id="editUser">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">用户详情</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="editUserForm">
<div class="form-group">
<label>
头像
</label>
<img src="" id="user_avatar">
</div>
<div class="form-group">
<label for="name">UID</label>
<input type="text" name="uid" id="uid" style="display: none;">
<input class="form-control" id="id" type="text" value="17" readonly>
</div>
<div class="form-group">
<label for="lovelyname">昵称</label>
<input type="text " class="form-control" id="user_nick" name="user_nick" required>
</div>
<div class="form-group">
<label for="InputPassword1">密码</label>
<input type="password" class="form-control" id="user_pass" name="user_pass" placeholder="如果不修改密码则留空" >
</div>
<div class="form-group">
<label for="InputEmail1">邮箱</label>
<input type="email" class="form-control" id="user_email" name="user_email" placeholder="Email" required>
</div>
<div class="form-group">
<label for="date">注册日期</label>
<input class="form-control" id="user_date" type="text" value="2017.12.31" readonly>
</div>
<div class="form-group">
<label for="space">已用空间</label>
<input class="form-control" id="used_storage" type="text" value="500M" readonly>
</div>
<div class="form-group">
<label for="openid">二步验证秘钥</label>
<input type="text " class="form-control" id="two_step" name="two_step" placeholder="取消二步验证请填写0" required="">
</div>
<div class="form-group">
<label for="inputState">用户状态</label>
<select class="form-control" id="user_status" name="user_status">
<option value="0">正常</option>
<option value="1">封禁/未激活</option>
</select>
</div>
<div class="form-group">
<label for="inputState">用户组</label>
<select id="user_group" class="form-control" name="user_group">
{foreach $groups as $g}
<option value="{$g.id}">{$g.group_name}</option>
{/foreach}
</select>
</div>
<div class="form-group">
<label for="inputState">个人主页状态</label>
<div class="radio">
<label>
<input type="radio" name="profile" id="profile1" value="1">
开启
</label>
<label>
<input type="radio" name="profile" id="profile0" value="0">
关闭
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary" id="editUserSubmit">保存</button>
</div></form>
</div>
</div>
</div>
<div class="modal fade bd-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true" id="addUser">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">添加用户</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="addUserForm">
<div class="form-group">
<label for="lovelyname">昵称</label>
<input type="text " class="form-control" id="user_nick" name="user_nick" required>
</div>
<div class="form-group">
<label for="InputPassword1">密码</label>
<input type="password" class="form-control" id="user_pass" name="user_pass" required>
</div>
<div class="form-group">
<label for="InputEmail1">邮箱</label>
<input type="email" class="form-control" id="user_email" name="user_email" placeholder="Email" required>
</div>
<div class="form-group">
<label for="inputState">用户状态</label>
<select class="form-control" name="user_status">
<option value="0">正常</option>
<option value="1">封禁/未激活</option>
</select>
</div>
<div class="form-group">
<label for="inputState">用户组</label>
<select class="form-control" name="user_group">
{foreach $groups as $g}
<option value="{$g.id}">{$g.group_name}</option>
{/foreach}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary" id="addUserSubmit">保存</button>
</div></form>
</div>
</div>
</div>
<!-- /.content-wrapper-->
{/block}
{block name="js"}
<script src="/static/js/admin/users.js"></script>
<script type="text/javascript">
</script>
{/block}
@@ -0,0 +1,23 @@
{extend name="header_public" /}
{block name="title"}支付成功 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/error.css" />
</head>
<body data-ma-header="teal">
<div class="container" align="center">
<header align="left">
<br>
<img src="/img/logo_s.png" style="width:192px;">
</header>
<div class="error_content" >
<div class="jumbotron animated bounce" style="padding-left:30px;">
<h1>支付成功</h1>
<br>
<p> 付款成功,您所购买的服务已开通。</p>
<p><a class="btn btn-primary btn-lg" href="/Home/Quota">查看详情</a></p>
</div>
</div>
</div>
</body>
{/block}
+19
View File
@@ -0,0 +1,19 @@
<link rel="stylesheet" href="/static/css/error.css" />
</head>
<body data-ma-header="teal">
<div class="container" align="center">
<header align="left">
<br>
<img src="/static/img/logo_s.png" style="width:192px;">
</header>
<div class="error_content" >
<div class="jumbotron animated bounce" style="padding-left:30px;">
<h1>发生错误</h1>
<br>
<p> {$msg}</p>
<p><a class="btn btn-primary btn-lg" onclick="history.go(-1);"><< 返回</a></p>
</div>
</div>
</div>
</body>
+24
View File
@@ -0,0 +1,24 @@
{extend name="header_public" /}
{block name="title"}错误提示 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/error.css" />
</head>
<body data-ma-header="teal">
<div class="container" align="center">
<header align="left">
<br>
<img src="/static/img/logo_s.png" style="width:192px;">
</header>
<div class="error_content" >
<div class="jumbotron animated bounce" style="padding-left:30px;">
<h1>发生错误</h1>
<br>
<p> {$msg}</p>
<p><a class="btn btn-primary btn-lg" onclick="history.go(-1);"><< 返回</a></p>
</div>
</div>
</div>
{$options.js_code}
</body>
{/block}
@@ -0,0 +1,23 @@
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>“{$keyWords}”搜索结果 - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="shareLock";
list = {$list};
userInfo = {
uid: {$userData.uid},
nick: "{$userData.userNick}",
email: "{$userData.userMail}",
group: "{$userData.groupData.group_name}",
groupId: {$userData.groupData.id},
groupColor: "{$userData.groupData.color}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={21:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/2.chunk.js"></script><script src="/static/js/search.chunk.js"></script></body></html>
@@ -0,0 +1,5 @@
下载已开始...
<a href="{$url}" download="" id="downloadA">手动下载</a>
<script type="text/javascript">
document.getElementById("downloadA").click();
</script>
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html class="full-height">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<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 -->
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<!-- Bootstrap core CSS -->
<link href="/static/css/bootstrap4/bootstrap.min.css" rel="stylesheet">
<!-- Material Design Bootstrap -->
<link href="/static/css/mdb.css" rel="stylesheet">
</head>
<body class="grey-skin intro-page creative-lp">
<!--Navigation & Intro-->
<header>
<!--Navbar-->
<nav class="navbar fixed-top navbar-dark ">
<div class="container">
<a class="navbar-brand" href="#">
<img src="static/img/logo_s.png" style=" width: 192px;">
</a>
</div>
</nav>
<!--/Navbar-->

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