├── data └── README.md ├── upload └── README.md ├── templates ├── footer.html ├── show │ ├── any.html │ ├── audio.html │ ├── image.html │ ├── video.html │ ├── code.html │ └── video2.html ├── head.html ├── readme.html ├── password.html ├── admin │ ├── login.html │ ├── install_1.html │ ├── setpass.html │ ├── edit.html │ ├── cache.html │ ├── setting.html │ ├── upload.html │ ├── install_0.html │ ├── layout.html │ ├── manage.html │ └── upload_local.html ├── layout.html ├── header.html ├── _macro.html └── index.html ├── requirements.txt ├── supervisord.conf.sample ├── static ├── js │ └── hashmap.js ├── css │ └── theme.css └── img │ └── bg-bottom.svg ├── config.py.sample ├── README.md ├── .gitignore ├── admin.py ├── run.py └── function.py /data/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /upload/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/footer.html: -------------------------------------------------------------------------------- 1 |
2 | {{tj_code|safe}} 3 |
4 | -------------------------------------------------------------------------------- /templates/show/any.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 | {{content}} 5 | {%endblock content%} -------------------------------------------------------------------------------- /templates/head.html: -------------------------------------------------------------------------------- 1 | {%if head%} 2 |
3 | {{head|safe}} 4 |
5 | {%endif%} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask_sqlalchemy 3 | redis 4 | pymongo 5 | gunicorn 6 | humanize 7 | python_dateutil 8 | supervisor 9 | requests 10 | markdown 11 | shelljob 12 | eventlet 13 | flask_caching 14 | flask_limiter 15 | Flask-PyMongo 16 | -------------------------------------------------------------------------------- /templates/readme.html: -------------------------------------------------------------------------------- 1 | {%if readme%} 2 |
3 |
4 | face 5 | README.md 6 |
7 | {{readme|safe}} 8 |
9 | 10 | {%endif%} 11 | -------------------------------------------------------------------------------- /supervisord.conf.sample: -------------------------------------------------------------------------------- 1 | [unix_http_server] 2 | file=/tmp/supervisor.sock 3 | 4 | [supervisord] 5 | logfile=/tmp/supervisord.log 6 | logfile_maxbytes=50MB 7 | logfile_backups=10 8 | loglevel=info 9 | pidfile=/tmp/supervisord.pid 10 | nodaemon=false 11 | minfds=1024 12 | minprocs=200 13 | 14 | [inet_http_server] ; inet (TCP) server disabled by default 15 | port=*:9001 16 | 17 | [rpcinterface:supervisor] 18 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 19 | 20 | [supervisorctl] 21 | serverurl=unix:///tmp/supervisor.sock 22 | 23 | 24 | 25 | 26 | [program:pyone] 27 | command = gunicorn -k eventlet -b 0.0.0.0:34567 run:app 28 | directory = /root/PyOne 29 | autorestart = true 30 | 31 | 32 | -------------------------------------------------------------------------------- /static/js/hashmap.js: -------------------------------------------------------------------------------- 1 | function HashMap(){var length=0;var obj=new Object();this.isEmpty=function(){return length==0};this.containsKey=function(key){return(key)?(key in obj):false};this.containsValue=function(value){for(key in obj){if(obj[key]==value){return true}}return false};this.get=function(key){return(this.containsKey(key))?obj[key]:null};this.remove=function(key){if(this.containsKey(key)&&delete obj[key]){length--}};this.put=function(key,value){if(!this.containsKey(key)){length++}obj[key]=value};this.values=function(){var _values=new Array();for(key in obj){_values.push(obj[key])}return _values};this.keySet=function(){var ks=new Array();for(key in obj){ks.push(key)}return ks};this.size=function(){return this.length};this.clear=function(){this.length=0;this.obj=new Object()};this.toJSON=function(){return JSON.stringify(obj,null,2)}}; 2 | -------------------------------------------------------------------------------- /templates/show/audio.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 |
5 |
6 |
7 | 9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 |
17 | 18 | 19 |
20 |
21 | file_download 22 | {%endblock content%} -------------------------------------------------------------------------------- /templates/password.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 |
5 |
6 |

输入密码进行查看

7 |
8 |
9 | https 10 | 11 | 12 |
13 |
14 | 18 |
19 |
20 |
21 | {%endblock content%} 22 | -------------------------------------------------------------------------------- /templates/admin/login.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | 3 | {%block content%} 4 |
5 |
6 |

登录后台

7 |
8 |
9 | https 10 | 11 | 12 |
13 |
14 | 18 |
19 |
20 |
21 | {%endblock content%} 22 | -------------------------------------------------------------------------------- /templates/show/image.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 |
5 |
6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 | 14 | 15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 | file_download 23 | {%endblock content%} -------------------------------------------------------------------------------- /config.py.sample: -------------------------------------------------------------------------------- 1 | #-*- coding=utf-8 -*- 2 | import os 3 | 4 | #限制调用域名 5 | allow_site=[u'no-referrer'] 6 | 7 | #######源码目录 8 | config_dir="/root/PyOne" 9 | data_dir=os.path.join(config_dir,'data') 10 | 11 | #下载链接过期时间 12 | downloadUrl_timeout="300" 13 | 14 | #后台密码设置 15 | password="PyOne" 16 | 17 | #网站名称 18 | title="PyOne" 19 | 20 | tj_code="""""" 21 | 22 | #onedrive api设置 23 | redirect_uri='https://auth.pyone.me/' #不要修改! 24 | BaseAuthUrl='https://login.microsoftonline.com' 25 | app_url=u'https://graph.microsoft.com/' 26 | 27 | od_users={ 28 | "A":{ 29 | "client_id":"", 30 | "client_secret":"", 31 | "share_path":"/", 32 | "other_name":"网盘1区", 33 | "order":1 34 | }, 35 | "B":{ 36 | "client_id":"", 37 | "client_secret":"", 38 | "share_path":"/", 39 | "other_name":"网盘2区", 40 | "order":2 41 | }, 42 | "C":{ 43 | "client_id":"", 44 | "client_secret":"", 45 | "share_path":"/", 46 | "other_name":"网盘3区", 47 | "order":3 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /templates/show/video.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 | 6 |
7 |
8 |
9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 30 | file_download 31 | {%endblock content%} -------------------------------------------------------------------------------- /templates/show/code.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 9 |
10 |
{{content}}
11 |
12 |
13 | 14 | 15 |
16 | file_download 17 | 18 | 19 | 20 | 34 | {%endblock content%} 35 | -------------------------------------------------------------------------------- /templates/show/video2.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 | 6 | 7 |
8 |
9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 |
17 | 18 | 19 |
20 |
21 | 31 | file_download 32 | {%endblock content%} -------------------------------------------------------------------------------- /templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | index of {{path}} - {{title}} 8 | 9 | 10 | 11 | 12 | 29 | 30 | 31 | 32 |
33 | {%include 'header.html'%} 34 | {%block content%} 35 | {%endblock content%} 36 |
37 | {%include 'footer.html'%} 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /templates/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {%for user in get_od_user()%} 5 | {%if user[1]!='添加网盘'%} 6 | 7 | 8 | 9 | 10 | {%endif%} 11 | {%endfor%} 12 |
13 |
14 | {{cur_user}} 15 | {%if path%} 16 | {%for idx,p in enumerate(path_list(path))%} 17 | chevron_right 18 | {%if idx==len(path_list(path))-1%} 19 | {{p|replace(cur_user+':/','')|replace(cur_user+':','')}} 20 | {%else%} 21 | {{p|replace(cur_user+':/','')|replace(cur_user+':','')}} 22 | {%endif%} 23 | {%endfor%} 24 | {%endif%} 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyOne - 基于Python的onedrive文件本地化浏览系统,使用MongoDB缓存文件 2 | 3 | ## PyOne3.0版本更新!!!支持多网盘绑定 4 | 5 | ## 适用onedrive版本 ## 6 | 1. 个人版 7 | 2. onedrive商业版 8 | 3. onedrive教育版(需要学校管理员开启权限) 9 | 10 | ## 特性 ## 11 | 1. 简单易用。只需简单设置,即可做一个onedrive文件列表分享程序 12 | 2. 功能丰富。 13 | - 可设置文件夹密码。只需在文件夹添加`.password`文件,内容为密码内容,即可在该文件夹设置密码 14 | - 可设置README。 15 | 3. 后台强大。 16 | - **防盗链设置**。 17 | - **后台上传文件**。 18 | - **后台更新文件**。 19 | - **后台设置统计代码** 20 | - **后台管理onedrive文件**。 21 | - **删除onedrive文件** 22 | - **直接在后台给文件夹添加`.password`和`README`和`HEAD`** 23 | - **直接在后台编辑文本文件**。 24 | - **上传本地文件至onedrive**(2018.10.18更新) 25 | - **支持创建文件夹**(2018.10.19更新) 26 | - **支持移动文件(仅限单文件)**(2018.10.19更新) 27 | 4. **支持绑定多网盘!!!**(2018.11.15更新) 28 | 29 | ## 适用环境 ## 30 | 1. linux环境(推荐centos7) 31 | 2. Python2.7 32 | 3. **需要安装redis,MongoDB** 33 | 34 | **推荐预先安装宝塔,再进行安装** 35 | 36 | ## 安装教程 ## 37 | 请转移到我的博客查看[安装教程](https://www.abbeyok.com/?p=174) 38 | 39 | ## 更新源码步骤 ## 40 | - PyOne旧版本升级到3.0:**请重装!!!** 41 | - PyOne3.0内小版本更新: 42 | 1. 拉最新代码 43 | ``` 44 | git pull 45 | ``` 46 | 5. 重新安装依赖,看是否有新增的依赖包: 47 | ``` 48 | pip install -r requirements.txt 49 | ``` 50 | 6. 重启网站: 51 | ``` 52 | supervisorctl -c supervisord.conf restart pyone 53 | ``` 54 | 7. 最好更新一下文件缓存: 55 | ``` 56 | python function.py UpdateFile 57 | ``` 58 | 59 | ## 提供安装服务 ## 60 | [点击购买](https://iofaka.com/?gid=4) 61 | 62 | -------------------------------------------------------------------------------- /templates/admin/install_1.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | {% import '_macro.html' as macros %} 3 | 4 | {%block content%} 5 |
6 |
7 |

系统安装 绑定账号

8 |
9 | 10 |
11 |

步骤1:绑定账号,获取code

12 |
13 | 14 |
15 |

步骤2:输入code,结束绑定

16 | 1. 绑定账号 17 |
18 |
19 | https 20 | 21 | 22 |
code不能为空
23 |
24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 |
34 |

示例,如何操作

35 | 36 |
37 | 38 |
39 | {%endblock content%} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | 113 | # PyOne config_file 114 | config.py 115 | AppUrl 116 | *.json 117 | supervisord.conf 118 | -------------------------------------------------------------------------------- /templates/admin/setpass.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | 3 | {%block content%} 4 | 9 |
10 |

添加文件 - {{filename}}

11 |

12 | 
15 | 
16 | 17 | 18 | 19 | 20 | 67 | {%endblock content%} 68 | -------------------------------------------------------------------------------- /templates/admin/edit.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | 3 | {%block content%} 4 | 9 |
10 |
{{content}}
11 | 14 |
15 | 16 | 17 | 18 | 19 | 66 | {%endblock content%} 67 | -------------------------------------------------------------------------------- /templates/admin/cache.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} {%block content%} 2 | 11 |
12 |
13 |

更新缓存 更新本地文件列表

14 |
15 |
16 |
17 |
18 | 19 | 23 |
24 | 28 |
29 | 33 |
34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 59 | {%endblock content%} 60 | -------------------------------------------------------------------------------- /templates/admin/setting.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | 3 | {%block content%} 4 |
5 | 6 |
7 |

基本设置 设置PyOne基本参数

8 |
9 |
10 |
11 |

网站名称

12 | 13 |
14 | 15 |
16 |

缓存过期时间(秒)

17 | 18 |
19 | 20 |
21 |

防盗链设置(不限制,则设置:no-referrer;仅限3pp.me调用,则设置:3pp.me;可调用域名通过英文逗号分隔)

22 | 23 |
24 | 25 |
26 |

统计代码

27 | 28 |
29 | 30 |
31 |

修改后台密码(如果不修改,则无需输入)

32 | 33 | 34 |
35 | 36 |
37 |

网盘信息编辑

38 | {%for user in get_od_user()%} 39 | {%if user[1]!='添加网盘'%} 40 |
41 |
42 |
{{user[1]}}
43 |
44 |
显示名称
45 | 46 |
分享目录
47 | 48 |
49 |
50 |
51 | {%endif%} 52 | {%endfor%} 53 |
54 | 55 | 58 |
59 |
60 | {%endblock content%} 61 | -------------------------------------------------------------------------------- /static/css/theme.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f2f5fa; 3 | padding-bottom: 60px; 4 | background-image: url(../img/bg-bottom.svg); 5 | background-position: 50% 100%; 6 | background-repeat: no-repeat; 7 | background-attachment: fixed 8 | } 9 | 10 | .nexmoe-item { 11 | /*margin: 20px -8px 0 !important;*/ 12 | padding: 15px !important; 13 | border-radius: 5px; 14 | background-color: #fff; 15 | -webkit-box-shadow: 0 .5em 3em rgba(161, 177, 204, .4); 16 | box-shadow: 0 .5em 3em rgba(161, 177, 204, .4); 17 | background-color: #fff 18 | } 19 | 20 | .mdui-img-fluid, 21 | .mdui-video-fluid { 22 | border-radius: 5px; 23 | border: 1px solid #eee 24 | } 25 | 26 | .mdui-list { 27 | padding: 0 28 | } 29 | 30 | .mdui-list-item { 31 | margin: 0 !important; 32 | border-radius: 5px; 33 | padding: 0 10px 0 5px !important; 34 | border: 1px solid #eee; 35 | margin-bottom: 10px !important 36 | } 37 | 38 | .mdui-list-item:last-child { 39 | margin-bottom: 0 !important 40 | } 41 | 42 | .mdui-list-item:first-child { 43 | border: none 44 | } 45 | 46 | .mdui-toolbar { 47 | width: auto; 48 | /*margin-top: 60px !important*/ 49 | } 50 | 51 | .mdui-appbar .mdui-toolbar { 52 | height: 56px; 53 | font-size: 16px 54 | } 55 | 56 | .mdui-toolbar>* { 57 | padding: 0 6px; 58 | margin: 0 2px; 59 | opacity: .5 60 | } 61 | 62 | .mdui-toolbar>.mdui-typo-headline { 63 | padding: 0 16px 0 0 64 | } 65 | 66 | .mdui-toolbar>i { 67 | padding: 0 68 | } 69 | 70 | .mdui-toolbar>a:hover, 71 | a.mdui-typo-headline, 72 | a.active { 73 | opacity: 1 74 | } 75 | 76 | .mdui-container { 77 | max-width: 980px 78 | } 79 | 80 | .mdui-list>.th { 81 | background-color: initial 82 | } 83 | 84 | .mdui-list-item>a { 85 | width: 100%; 86 | line-height: 48px 87 | } 88 | 89 | .mdui-toolbar>a { 90 | padding: 0 16px; 91 | line-height: 30px; 92 | border-radius: 30px; 93 | border: 1px solid #eee 94 | } 95 | 96 | .mdui-toolbar>a:last-child { 97 | opacity: 1; 98 | background-color: #1e89f2; 99 | color: #ffff 100 | } 101 | 102 | @media screen and (max-width:980px) { 103 | .mdui-list-item .mdui-text-right { 104 | display: none 105 | } 106 | 107 | .mdui-container { 108 | width: 100% !important; 109 | margin: 0 110 | } 111 | 112 | .mdui-toolbar>* { 113 | display: none 114 | } 115 | 116 | .mdui-toolbar>a:last-child, 117 | .mdui-toolbar>.mdui-typo-headline, 118 | .mdui-toolbar>i:first-child { 119 | display: block 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /templates/admin/upload.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} {%block content%} 2 | 11 |
12 |
13 |

上传管理 文件上传添加和管理

14 |
15 |
16 |
17 |
18 | 19 | 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 | 34 |
35 |
36 | 39 |
40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 | 64 | {%endblock content%} 65 | -------------------------------------------------------------------------------- /templates/admin/install_0.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | {% import '_macro.html' as macros %} 3 | 4 | {%block content%} 5 |
6 |
7 |

系统安装 应用ID和机密

8 |
9 | 10 |
11 |

12 | 填入client_idclient_secret, 13 | 获取应用ID和机密(分两个页面显示,请注意保存) 14 |

15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 |
应用 ID不能为空
23 |
24 |
25 |
26 | https 27 | 28 | 29 |
应用机密不能为空
30 |
31 |
32 | 33 | 34 |
35 |
36 | 37 |
38 |
39 |

示例,分别获取client_secret和client_id

40 |

1. 首先获取应用ID(client_id)

41 |

2. 然后登陆:https://apps.dev.microsoft.com/,查到第一步获取的client_id,并进入相关应用

42 |

3. 点击生成新密码,获取一个新client_secret

43 | 44 | 45 | 46 |
47 | 48 | 49 |
50 | {%endblock content%} 51 | -------------------------------------------------------------------------------- /templates/_macro.html: -------------------------------------------------------------------------------- 1 | {% macro pagination_widget(pagination, endpoint) %} 2 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% endmacro %} 39 | 40 | {% macro Arraw(name,cur_type,sortby,order) %} 41 | 42 | {{ name }} 43 | {% if cur_type==sortby %} 44 | {% if order=='asc' %} 45 | arrow_upward 46 | {% else %} 47 | arrow_downward 48 | {% endif %} 49 | {% endif %} 50 | 51 | {% endmacro %} 52 | 53 | 54 | {% macro Admin_Arraw(name,cur_type,sortby,order) %} 55 | 56 | {{ name }} 57 | {% if cur_type==sortby %} 58 | {% if order=='asc' %} 59 | arrow_upward 60 | {% else %} 61 | arrow_downward 62 | {% endif %} 63 | {% endif %} 64 | 65 | {% endmacro %} 66 | -------------------------------------------------------------------------------- /templates/admin/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PyOne系统管理 7 | 8 | 9 | 10 | 11 | 12 | 29 | 30 | 31 |
32 |
33 | menu 34 | PyOne 35 |
36 | 登出 37 | 重启网站 38 |
39 |
40 |
41 |
42 |

43 | 44 | 45 |
基本设置
46 |
47 | 48 | 49 | 50 |
上传文件
51 |
52 | 53 | 54 | 55 |
页面缓存
56 |
57 | 58 | 59 | 60 |
文件管理
61 |
62 |
63 |
64 | 65 | 66 |
67 |
68 | {% for message in get_flashed_messages() %} 69 |

{{message}}

70 | {% endfor %} 71 |
72 | {%block content%} 73 | {%endblock content%} 74 |
75 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {%extends 'layout.html'%} 2 | {% import '_macro.html' as macros %} 3 | 4 | {%block content%} 5 |
6 |
7 | 12 |
13 | {%include 'head.html'%} 14 |
15 |
16 | 79 | {%include 'readme.html'%} 80 |
81 |
82 |
83 | 84 |
85 | {% if pagination %} 86 | {{ macros.pagination_widget(pagination, endpoint,path=path,sortby=sortby,order=order) }} 87 | {% endif %} 88 |
89 | 90 | 122 | {%endblock content%} 123 | -------------------------------------------------------------------------------- /templates/admin/manage.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} 2 | {% import '_macro.html' as macros %} 3 | {%block content%} 4 | 24 |
25 |

文件管理 仅支持更新/编辑文编;支持删除文件

26 |
27 |
28 |
29 |
30 | {%for user in get_od_user()%} 31 | {%if user[1]=='添加网盘'%} 32 | 33 | 34 | 35 | 36 | {%else%} 37 | 38 | 39 | 40 | 41 | {%endif%} 42 | {%endfor%} 43 |
44 |
45 | {{cur_user}} 46 | {%if path%} 47 | {%for idx,p in enumerate(path_list(path))%} 48 | chevron_right 49 | {%if idx==len(path_list(path))-1%} 50 | {{p|replace(cur_user+':/','')|replace(cur_user+':','')}} 51 | {%else%} 52 | {{p|replace(cur_user+':/','')|replace(cur_user+':','')}} 53 | {%endif%} 54 | {%endfor%} 55 | {%endif%} 56 |
57 |
58 | 59 | 60 | 61 | 66 | 67 | 68 | 76 | 77 | 78 | 79 | {%if path.split(':')[-1]!=''%} 80 | 81 | 82 | 93 | 94 | 95 | 96 | {%endif%} 97 | {%for data in items%} 98 | {%if data['type']=='folder' %} 99 | 100 | 105 | 110 | 111 | 116 | 117 | {%else%} 118 | 119 | 124 | 129 | 130 | 143 | 144 | {%endif%} 145 | {%endfor%} 146 | 147 |
62 | 65 | {{ macros.Admin_Arraw(name='文件',cur_type='name',sortby=sortby,order=order,path=path) }}{{ macros.Admin_Arraw(name='修改时间',cur_type='lastModtime',sortby=sortby,order=order,path=path) }} 69 | 70 | 71 | 72 | 73 | 74 | 75 |
83 | {%if '/'.join(path_list(path)[:-1])==''%} 84 | 85 | arrow_upward 返回上一层 86 | 87 | {%else%} 88 | 89 | arrow_upward 返回上一层 90 | 91 | {%endif%} 92 |
101 | 104 | 106 | 107 | folder_open {{data['name']}} 108 | 109 | {{data['lastModtime']}} 112 | 115 |
120 | 123 | 125 | 126 | {{file_ico(data)}} {{data['name']}} 127 | 128 | {{data['lastModtime']}} 131 | 134 | 137 | {%if CanEdit(data['name'])%} 138 | 141 | {%endif%} 142 |
148 |
149 |
150 | {% if pagination %} {{ macros.pagination_widget(pagination, endpoint,path=path,sortby=sortby,order=order) }} {% endif %} 151 |
152 | 334 | {%endblock content%} 335 | -------------------------------------------------------------------------------- /templates/admin/upload_local.html: -------------------------------------------------------------------------------- 1 | {%extends 'admin/layout.html'%} {%block content%} 2 | 3 | 4 | 5 | 50 |
51 |
52 |

本地文件上传至onedrive 本地文件->服务器->onedrive

53 |
54 |
55 |
56 |
57 |
上传列表
58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
序号文件名称文件大小本地->服务器上传状态服务器->onedrive上传状态操作
74 |
75 |
76 | 77 | 78 |
79 |
80 |

选择文件或将文件拖到这里

81 |
82 |
83 |
84 |
85 |
86 |
87 | 368 | {%endblock content%} 369 | -------------------------------------------------------------------------------- /admin.py: -------------------------------------------------------------------------------- 1 | #-*- coding=utf-8 -*- 2 | from flask import Blueprint,redirect,url_for,request,render_template,flash,session,jsonify,Response,make_response 3 | from flask_sqlalchemy import Pagination 4 | from function import * 5 | from config import * 6 | from run import FetchData,path_list,GetName,CodeType,_remote_content,rd,has_item,AddResource 7 | import os 8 | import io 9 | import re 10 | import subprocess 11 | import random 12 | import urllib 13 | from shelljob import proc 14 | import eventlet 15 | 16 | eventlet.monkey_patch() 17 | 18 | 19 | admin = Blueprint('admin', __name__,url_prefix='/admin') 20 | 21 | ############功能函数 22 | def set(key,value,user='A'): 23 | allow_key=['title','downloadUrl_timeout','allow_site','password','client_secret','client_id','share_path','other_name','tj_code'] 24 | if key not in allow_key: 25 | return u'禁止修改' 26 | print 'set {}:{}'.format(key,value) 27 | config_path=os.path.join(config_dir,'config.py') 28 | with open(config_path,'r') as f: 29 | old_text=f.read() 30 | with open(config_path,'w') as f: 31 | if key in ['client_secret','client_id','share_path','other_name']: 32 | old_kv=re.findall('"{}":{{[\w\W]*}}'.format(user),old_text)[0] 33 | new_kv=re.sub('"{}":.*?,'.format(key),'"{}":"{}",'.format(key,value),old_kv,1) 34 | new_text=old_text.replace(old_kv,new_kv,1) 35 | elif key=='allow_site': 36 | value=value.split(',') 37 | new_text=re.sub('{}=.*'.format(key),'{}={}'.format(key,value),old_text) 38 | elif key=='tj_code': 39 | new_text=re.sub('{}=.*'.format(key),'{}="""{}"""'.format(key,value),old_text) 40 | else: 41 | new_text=re.sub('{}=.*'.format(key),'{}="{}"'.format(key,value),old_text) 42 | f.write(new_text) 43 | 44 | 45 | ############视图函数 46 | @admin.before_request 47 | def before_request(): 48 | if request.endpoint.startswith('admin') and request.endpoint!='admin.login' and session.get('login') is None: #and request.endpoint!='admin.install' 49 | return redirect(url_for('admin.login')) 50 | 51 | 52 | ########web console 53 | @admin.route('/web_console') 54 | def web_console(): 55 | g = proc.Group() 56 | action=request.args.get('action') 57 | allow_action=['UpdateFile','UploadDir','Upload'] 58 | if action not in allow_action: 59 | return make_response('error') 60 | if action in ['UploadDir','Upload']: 61 | local=urllib.unquote(request.args.get('local')) 62 | remote=urllib.unquote(request.args.get('remote')) 63 | user=urllib.unquote(request.args.get('user')) 64 | cmd=["python","-u",os.path.join(config_dir,'function.py'),action,local,remote,user] 65 | elif action=='UpdateFile': 66 | type_=request.args.get('type') 67 | cmd=["python","-u",os.path.join(config_dir,'function.py'),'UpdateFile',type_] 68 | else: 69 | cmd=["python","-u",os.path.join(config_dir,'function.py'),action] 70 | p = g.run(cmd) 71 | def read_process(): 72 | while g.is_pending(): 73 | lines = g.readlines() 74 | for proc, line in lines: 75 | yield "data:" + line + "\n\n" 76 | yield "data:end\n\n" 77 | return Response(read_process(), mimetype= 'text/event-stream') 78 | 79 | ########admin 80 | @admin.route('/',methods=['GET','POST']) 81 | @admin.route('/setting',methods=['GET','POST']) 82 | def setting(): 83 | if request.method=='POST': 84 | title=request.form.get('title','PyOne') 85 | downloadUrl_timeout=request.form.get('downloadUrl_timeout',5*60) 86 | allow_site=request.form.get('allow_site','no-referrer') 87 | tj_code=request.form.get('tj_code','') 88 | password1=request.form.get('password1') 89 | password2=request.form.get('password2') 90 | new_password=password 91 | if ((password1 is not None and password2 is None) or (password1 is None and password2 is not None)): 92 | flash(u'请输入新密码或者二次确认新密码') 93 | elif password1 is not None and password2 is not None and password1!=password2: 94 | flash(u'两次输入密码不相同') 95 | elif password1 is not None and password2 is not None and password1==password2 and password1!='': 96 | new_password=password1 97 | set('title',title) 98 | set('downloadUrl_timeout',downloadUrl_timeout) 99 | set('allow_site',allow_site) 100 | set('tj_code',tj_code) 101 | set('password',new_password) 102 | ####网盘信息处理 103 | for k,v in request.form.to_dict().items(): 104 | if 'share_path' in k or 'other_name' in k: 105 | user=re.findall('\[(.*?)\]',k)[0] 106 | key=re.findall('(.*)\[',k)[0] 107 | print('setting {}\'s {}\'s value {}'.format(user,key,v)) 108 | set(key,v,user) 109 | reload() 110 | return render_template('admin/setting.html') 111 | return render_template('admin/setting.html') 112 | 113 | 114 | 115 | 116 | @admin.route('/upload',methods=["POST","GET"]) 117 | def upload(): 118 | if request.method=='POST': 119 | user=request.form.get('user').encode('utf-8') 120 | local=request.form.get('local').encode('utf-8') 121 | remote=request.form.get('remote').encode('utf-8') 122 | if not os.path.exists(local): 123 | flash('本地目录/文件不存在') 124 | return redirect(url_for('admin.upload')) 125 | if os.path.isfile(local): 126 | action='Upload' 127 | else: 128 | action='UploadDir' 129 | return render_template('admin/upload.html',remote=remote,local=local,action=action,user=user) 130 | return render_template('admin/upload.html') 131 | 132 | 133 | 134 | @admin.route('/cache',methods=["POST","GET"]) 135 | def cache(): 136 | if request.method=='POST': 137 | type=request.form.get('type') 138 | return render_template('admin/cache.html',type=type,action='UpdateFile') 139 | return render_template('admin/cache.html') 140 | 141 | 142 | @admin.route('/manage',methods=["POST","GET"]) 143 | def manage(): 144 | if request.method=='POST': 145 | pass 146 | path=urllib.unquote(request.args.get('path','A:/')) 147 | user,n_path=path.split(':') 148 | if n_path=='': 149 | path=':'.join([user,'/']) 150 | page=request.args.get('page',1,type=int) 151 | image_mode=request.args.get('image_mode') 152 | sortby=request.args.get('sortby') 153 | order=request.args.get('order') 154 | if sortby: 155 | sortby=request.args.get('sortby') 156 | else: 157 | sortby=request.cookies.get('admin_sortby') if request.cookies.get('admin_sortby') is not None else 'lastModtime' 158 | sortby=sortby 159 | if order: 160 | order=request.args.get('order') 161 | else: 162 | order=request.cookies.get('admin_order') if request.cookies.get('admin_order') is not None else 'desc' 163 | order=order 164 | resp,total = FetchData(path=path,page=page,per_page=50,sortby=sortby,order=order) 165 | pagination=Pagination(query=None,page=page, per_page=50, total=total, items=None) 166 | if path.split(':',1)[-1]=='/': 167 | path=':'.join([path.split(':',1)[0],'']) 168 | resp=make_response(render_template('admin/manage.html',pagination=pagination,items=resp,path=path,sortby=sortby,order=order,cur_user=user,endpoint='admin.manage')) 169 | resp.set_cookie('admin_sortby',str(sortby)) 170 | resp.set_cookie('admin_order',str(order)) 171 | return resp 172 | 173 | 174 | @admin.route('/edit',methods=["GET","POST"]) 175 | def edit(): 176 | if request.method=='POST': 177 | fileid=request.form.get('fileid') 178 | user=request.form.get('user') 179 | content=request.form.get('content').encode('utf-8') 180 | info={} 181 | token=GetToken(user=user) 182 | app_url=GetAppUrl() 183 | headers={'Authorization':'bearer {}'.format(token)} 184 | url=app_url+'v1.0/me/drive/items/{}/content'.format(fileid) 185 | try: 186 | r=requests.put(url,headers=headers,data=content,timeout=10) 187 | data=json.loads(r.content) 188 | if data.get('id'): 189 | info['status']=0 190 | info['msg']='修改成功' 191 | rd.delete('{}:content'.format(fileid)) 192 | file=items.find_one({'id':fileid}) 193 | name=file['name'] 194 | path=file['path'].replace('/'+name,'').replace(name,'') 195 | if path=='': 196 | path='/' 197 | if not path.startswith('/'): 198 | path='/'+path 199 | path='{}:{}'.format(user,path) 200 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,name) 201 | rd.delete(key) 202 | else: 203 | info['status']=0 204 | info['msg']=data.get('error').get('message') 205 | except Exception as e: 206 | print e 207 | info['status']=0 208 | info['msg']='修改超时' 209 | return jsonify(info) 210 | fileid=request.args.get('fileid') 211 | user=request.args.get('user') 212 | name=GetName(fileid) 213 | ext=name.split('.')[-1] 214 | language=CodeType(ext) 215 | if language is None: 216 | language='Text' 217 | content=_remote_content(fileid,user) 218 | return render_template('admin/edit.html',content=content,fileid=fileid,name=name,language=language,cur_user=user) 219 | 220 | ###本地上传文件只onedrive,通过服务器中转 221 | @admin.route('/upload_local',methods=['POST','GET']) 222 | def upload_local(): 223 | user,remote_folder=request.args.get('path').split(':') 224 | return render_template('admin/upload_local.html',remote_folder=remote_folder,cur_user=user) 225 | 226 | @admin.route('/checkChunk', methods=['POST']) 227 | def checkChunk(): 228 | md5=request.form.get('fileMd5') 229 | fileName=request.form.get('name').encode('utf-8') 230 | chunk=request.form.get('chunk',0,type=int) 231 | filename = u'./upload/{}-{}'.format(fileName, chunk) 232 | if os.path.exists(filename): 233 | exists=True 234 | else: 235 | exists=False 236 | return jsonify({'ifExist':exists}) 237 | 238 | 239 | @admin.route('/mergeChunks', methods=['POST']) 240 | def mergeChunks(): 241 | fileName=request.form.get('fileName').encode('utf-8') 242 | md5=request.form.get('fileMd5') 243 | chunk = 0 # 分片序号 244 | with open(u'./upload/{}'.format(fileName), 'wb') as target_file: # 创建新文件 245 | while True: 246 | try: 247 | filename = u'./upload/{}-{}'.format(fileName, chunk) 248 | source_file = open(filename, 'rb') # 按序打开每个分片 249 | target_file.write(source_file.read()) # 读取分片内容写入新文件 250 | source_file.close() 251 | except IOError as msg: 252 | break 253 | chunk += 1 254 | os.remove(filename) # 删除该分片,节约空间 255 | return jsonify({'upload':True}) 256 | 257 | 258 | @admin.route('/recv_upload', methods=['POST']) 259 | def recv_upload(): # 接收前端上传的一个分片 260 | md5=request.form.get('fileMd5') 261 | name=request.form.get('name').encode('utf-8') 262 | chunk_id=request.form.get('chunk',0,type=int) 263 | filename = '{}-{}'.format(name,chunk_id) 264 | upload_file = request.files['file'] 265 | upload_file.save(u'./upload/{}'.format(filename)) 266 | return jsonify({'upload_part':True}) 267 | 268 | 269 | @admin.route('/to_one',methods=['GET']) 270 | def server_to_one(): 271 | user=request.args.get('user') 272 | filename=request.args.get('filename').encode('utf-8') 273 | remote_folder=request.args.get('remote_folder').encode('utf-8') 274 | if remote_folder!='/': 275 | remote_folder=remote_folder+'/' 276 | local_dir=os.path.join(config_dir,'upload') 277 | filepath=urllib.unquote(os.path.join(local_dir,filename)) 278 | _upload_session=Upload_for_server(filepath,remote_folder,user) 279 | def read_status(): 280 | while 1: 281 | try: 282 | msg=_upload_session.next()['status'] 283 | yield "data:" + msg + "\n\n" 284 | except Exception as e: 285 | msg='end' 286 | yield "data:" + msg + "\n\n" 287 | os.remove(filepath) 288 | break 289 | return Response(read_status(), mimetype= 'text/event-stream') 290 | 291 | 292 | 293 | ###本地上传文件只onedrive,通过服务器中转 294 | @admin.route('/setFile',methods=["GET","POST"]) 295 | @admin.route('/setFile/',methods=["GET","POST"]) 296 | def setFile(filename=None): 297 | if request.method=='POST': 298 | path=request.form.get('path') 299 | user,n_path=path.split(':') 300 | filename=request.form.get('filename') 301 | if not n_path.startswith('/'): 302 | n_path='/'+n_path 303 | remote_file=os.path.join(n_path,filename) 304 | content=request.form.get('content').encode('utf-8') 305 | info={} 306 | token=GetToken(user=user) 307 | app_url=GetAppUrl() 308 | headers={'Authorization':'bearer {}'.format(token)} 309 | url=app_url+'v1.0/me/drive/items/root:{}:/content'.format(remote_file) 310 | try: 311 | r=requests.put(url,headers=headers,data=content,timeout=10) 312 | data=json.loads(r.content) 313 | if data.get('id'): 314 | AddResource(data,user) 315 | info['status']=0 316 | info['msg']='添加成功' 317 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,filename) 318 | rd.delete(key) 319 | else: 320 | info['status']=0 321 | info['msg']=data.get('error').get('message') 322 | except Exception as e: 323 | info['status']=0 324 | info['msg']='超时' 325 | return jsonify(info) 326 | path=urllib.unquote(request.args.get('path')) 327 | user,n_path=path.split(':') 328 | _,fid,i=has_item(path,filename) 329 | if fid!=False: 330 | return redirect(url_for('admin.edit',fileid=fid,user=user)) 331 | return render_template('admin/setpass.html',path=path,filename=filename,cur_user=user) 332 | 333 | 334 | @admin.route('/delete',methods=["POST"]) 335 | def delete(): 336 | ids=request.form.get('id') 337 | user=request.form.get('user') 338 | if ids is None: 339 | return jsonify({'msg':u'请选择要删除的文件','status':0}) 340 | ids=ids.split('##') 341 | infos={} 342 | infos['status']=1 343 | infos['delete']=0 344 | infos['fail']=0 345 | for id in ids: 346 | file=items.find_one({'id':id}) 347 | name=file['name'] 348 | # path=file['path'].replace('/'+name,'').replace(name,'') 349 | if file['parent']=='': 350 | path='/' 351 | else: 352 | path=items.find_one({'id':file['parent']})['path'] 353 | if not path.startswith('/'): 354 | path='/'+path 355 | path='{}:{}'.format(user,path) 356 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,name) 357 | rd.delete(key) 358 | kc='{}:content'.format(id) 359 | rd.delete(kc) 360 | status=DeleteRemoteFile(id,user) 361 | if status: 362 | infos['delete']+=1 363 | else: 364 | infos['fail']+=1 365 | return jsonify(infos) 366 | 367 | 368 | @admin.route('/add_folder',methods=['POST']) 369 | def AddFolder(): 370 | folder_name=request.form.get('folder_name') 371 | path=request.args.get('path') 372 | user,grand_path=path.split(':') 373 | if grand_path=='' or grand_path is None: 374 | grand_path='/' 375 | else: 376 | if grand_path.startswith('/'): 377 | grand_path=grand_path[1:] 378 | result=CreateFolder(folder_name,grand_path,user) 379 | return jsonify({'result':result}) 380 | 381 | @admin.route('/move_file',methods=['POST']) 382 | def MoveFileToNewFolder(): 383 | fileid=request.form.get('fileid') 384 | user=request.form.get('user') 385 | new_folder_path=request.form.get('new_folder_path') 386 | if new_folder_path=='' or new_folder_path is None: 387 | new_folder_path='/' 388 | else: 389 | if new_folder_path.startswith('/'): 390 | new_folder_path=new_folder_path[1:] 391 | result=MoveFile(fileid,new_folder_path,user) 392 | return jsonify({'result':result}) 393 | 394 | 395 | 396 | 397 | 398 | @admin.route('/login',methods=["POST","GET"]) 399 | def login(): 400 | if request.method=='POST': 401 | password1=request.form.get('password') 402 | if password1==password: 403 | session['login']='true' 404 | if len(os.listdir(os.path.join(config_dir,'data')))<=1: 405 | return redirect(url_for('admin.install',step=0,user='A')) 406 | return redirect(url_for('admin.setting')) 407 | else: 408 | return render_template('admin/login.html') 409 | return render_template('admin/login.html') 410 | 411 | 412 | @admin.route('/logout',methods=['GET','POST']) 413 | def logout(): 414 | session.pop('login',None) 415 | return redirect('/') 416 | 417 | @admin.route('/reload',methods=['GET','POST']) 418 | def reload(): 419 | cmd='supervisorctl -c {} restart pyone'.format(os.path.join(config_dir,'supervisord.conf')) 420 | subprocess.Popen(cmd,shell=True) 421 | flash('正在重启网站...如果更改了分享目录,请更新缓存') 422 | return redirect(url_for('admin.setting')) 423 | 424 | ###########################################安装 425 | @admin.route('/install',methods=['POST','GET']) 426 | def install(): 427 | if request.method=='POST': 428 | step=request.form.get('step',type=int) 429 | user=request.form.get('user') 430 | if step==1: 431 | client_secret=request.form.get('client_secret') 432 | client_id=request.form.get('client_id') 433 | set('client_secret',client_secret,user) 434 | set('client_id',client_id,user) 435 | login_url=LoginUrl.format(client_id=client_id,redirect_uri=redirect_uri) 436 | return render_template('admin/install_1.html',client_secret=client_secret,client_id=client_id,login_url=login_url,cur_user=user) 437 | else: 438 | client_secret=request.form.get('client_secret') 439 | client_id=request.form.get('client_id') 440 | code=request.form.get('code') 441 | #授权 442 | headers['Content-Type']='application/x-www-form-urlencoded' 443 | data=AuthData.format(client_id=client_id,redirect_uri=urllib.quote(redirect_uri),client_secret=client_secret,code=code) 444 | url=OAuthUrl 445 | r=requests.post(url,data=data,headers=headers) 446 | Atoken=json.loads(r.text) 447 | if Atoken.get('access_token'): 448 | with open(os.path.join(config_dir,'data/{}_Atoken.json'.format(user)),'w') as f: 449 | json.dump(Atoken,f,ensure_ascii=False) 450 | refresh_token=Atoken.get('refresh_token') 451 | token=ReFreshToken(refresh_token,user) 452 | with open(os.path.join(config_dir,'data/{}_token.json'.format(user)),'w') as f: 453 | json.dump(token,f,ensure_ascii=False) 454 | return make_response('

授权成功!点击进入首页
请在后台另开一个ssh窗口,运行:
python function.py UpdateFile
进行更新数据操作

') 455 | else: 456 | return jsonify(Atoken) 457 | step=request.args.get('step',type=int) 458 | user=request.args.get('user','A') 459 | resp=render_template('admin/install_0.html',step=step,cur_user=user) 460 | return resp 461 | 462 | 463 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | 2 | #-*- coding=utf-8 -*- 3 | from flask import Flask,render_template,redirect,abort,make_response,jsonify,request,url_for,Response 4 | from flask_sqlalchemy import Pagination 5 | import json 6 | from collections import OrderedDict 7 | import subprocess 8 | import hashlib 9 | import random 10 | import markdown 11 | from function import * 12 | from config import * 13 | from flask_caching import Cache 14 | from flask_limiter import Limiter 15 | from flask_limiter.util import get_remote_address 16 | from shelljob import proc 17 | import time 18 | import os 19 | import sys 20 | import eventlet 21 | 22 | eventlet.monkey_patch() 23 | 24 | reload(sys) 25 | sys.setdefaultencoding("utf-8") 26 | 27 | 28 | #######flask 29 | app=Flask(__name__) 30 | app.secret_key=os.path.join(config_dir,'PyOne'+password) 31 | cache = Cache(app, config={'CACHE_TYPE': 'redis'}) 32 | limiter = Limiter( 33 | app, 34 | key_func=get_remote_address, 35 | default_limits=["200/minute", "50/second"], 36 | ) 37 | 38 | 39 | ################################################################################ 40 | ###################################功能函数##################################### 41 | ################################################################################ 42 | def md5(string): 43 | a=hashlib.md5() 44 | a.update(string.encode(encoding='utf-8')) 45 | return a.hexdigest() 46 | 47 | def GetTotal(path='A:/'): 48 | key='total:{}'.format(path) 49 | if rd.exists(key): 50 | return int(rd.get(key)) 51 | else: 52 | user,n_path=path.split(':') 53 | if n_path=='/': 54 | total=items.find({'grandid':0}).count() 55 | else: 56 | f=items.find_one({'path':path}) 57 | pid=f['id'] 58 | total=items.find({'parent':pid}).count() 59 | rd.set(key,total,300) 60 | return total 61 | 62 | 63 | # @cache.memoize(timeout=60*5) 64 | def FetchData(path='A:/',page=1,per_page=50,sortby='lastModtime',order='desc',dismiss=False): 65 | path=urllib.unquote(path) 66 | resp=[] 67 | if sortby not in ['lastModtime','type','size','name']: 68 | sortby='lastModtime' 69 | if sortby=='size': 70 | sortby='size_order' 71 | if order=='desc': 72 | order=DESCENDING 73 | else: 74 | order=ASCENDING 75 | try: 76 | user,n_path=path.split(':') 77 | if n_path=='/': 78 | data=items.find({'grandid':0,'user':user}).collation({"locale": "zh", 'numericOrdering':True})\ 79 | .sort([('order',ASCENDING),(sortby,order)])\ 80 | .limit(per_page).skip((page-1)*per_page) 81 | for d in data: 82 | item={} 83 | item['name']=d['name'] 84 | item['id']=d['id'] 85 | item['lastModtime']=d['lastModtime'] 86 | item['size']=d['size'] 87 | item['type']=d['type'] 88 | if dismiss: 89 | if d['name'] not in ('README.md','README.txt','readme.md','readme.txt','.password','HEAD.md','HEAD.txt','head.md','head.txt'): 90 | resp.append(item) 91 | else: 92 | resp.append(item) 93 | total=GetTotal(path) 94 | else: 95 | f=items.find_one({'path':path}) 96 | pid=f['id'] 97 | if f['type']!='folder': 98 | return f,'files' 99 | data=items.find({'parent':pid}).collation({"locale": "zh", 'numericOrdering':True})\ 100 | .sort([('order',ASCENDING),(sortby,order)])\ 101 | .limit(per_page).skip((page-1)*per_page) 102 | for d in data: 103 | item={} 104 | item['name']=d['name'] 105 | item['id']=d['id'] 106 | item['lastModtime']=d['lastModtime'] 107 | item['size']=d['size'] 108 | item['type']=d['type'] 109 | if dismiss: 110 | if d['name'] not in ('README.md','README.txt','readme.md','readme.txt','.password','HEAD.md','HEAD.txt','head.md','head.txt'): 111 | resp.append(item) 112 | else: 113 | resp.append(item) 114 | total=GetTotal(path) 115 | except: 116 | resp=[] 117 | total=0 118 | return resp,total 119 | 120 | @cache.memoize(timeout=60*5) 121 | def _thunbnail(id,user): 122 | app_url=GetAppUrl() 123 | token=GetToken(user=user) 124 | headers={'Authorization':'bearer {}'.format(token),'Content-type':'application/json'} 125 | url=app_url+'v1.0/me/drive/items/{}/thumbnails/0?select=large'.format(id) 126 | r=requests.get(url,headers=headers) 127 | data=json.loads(r.content) 128 | if data.get('large').get('url'): 129 | return data.get('large').get('url') 130 | else: 131 | return False 132 | 133 | @cache.memoize(timeout=60*5) 134 | def _getdownloadurl(id,user): 135 | app_url=GetAppUrl() 136 | token=GetToken(user=user) 137 | filename=GetName(id) 138 | ext=filename.split('.')[-1] 139 | if ext in ['webm','avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']: 140 | downloadUrl=_thunbnail(id,user) 141 | downloadUrl=downloadUrl.replace('thumbnail','videomanifest')+'&part=index&format=dash&useScf=True&pretranscode=0&transcodeahead=0' 142 | return downloadUrl 143 | else: 144 | headers={'Authorization':'bearer {}'.format(token),'Content-type':'application/json'} 145 | url=app_url+'v1.0/me/drive/items/'+id 146 | r=requests.get(url,headers=headers) 147 | data=json.loads(r.content) 148 | if data.get('@microsoft.graph.downloadUrl'): 149 | return data.get('@microsoft.graph.downloadUrl') 150 | else: 151 | return False 152 | 153 | def GetDownloadUrl(id,user): 154 | if rd.exists('downloadUrl2:{}'.format(id)): 155 | downloadUrl,ftime=rd.get('downloadUrl2:{}'.format(id)).split('####') 156 | if time.time()-int(ftime)>=600: 157 | # print('{} downloadUrl expired!'.format(id)) 158 | downloadUrl=_getdownloadurl(id,user) 159 | ftime=int(time.time()) 160 | k='####'.join([downloadUrl,str(ftime)]) 161 | rd.set('downloadUrl2:{}'.format(id),k) 162 | else: 163 | # print('get {}\'s downloadUrl from cache'.format(id)) 164 | downloadUrl=downloadUrl 165 | else: 166 | # print('first time get downloadUrl from {}'.format(id)) 167 | downloadUrl=_getdownloadurl(id,user) 168 | ftime=int(time.time()) 169 | k='####'.join([downloadUrl,str(ftime)]) 170 | rd.set('downloadUrl2:{}'.format(id),k) 171 | return downloadUrl 172 | 173 | 174 | 175 | # @cache.memoize(timeout=60*5) 176 | def GetReadMe(path): 177 | # README 178 | ext='Markdown' 179 | readme,_,i=has_item(path,'README.md') 180 | if readme==False: 181 | readme,_,i=has_item(path,'readme.md') 182 | if readme==False: 183 | ext='Text' 184 | readme,_,i=has_item(path,'readme.txt') 185 | if readme==False: 186 | ext='Text' 187 | readme,_,i=has_item(path,'README.txt') 188 | if readme!=False: 189 | readme=markdown.markdown(readme) 190 | return readme,ext 191 | 192 | 193 | # @cache.memoize(timeout=60*5) 194 | def GetHead(path): 195 | # README 196 | ext='Markdown' 197 | head,_,i=has_item(path,'HEAD.md') 198 | if head==False: 199 | head,_,i=has_item(path,'head.md') 200 | if head==False: 201 | ext='Text' 202 | head,_,i=has_item(path,'head.txt') 203 | if head==False: 204 | ext='Text' 205 | head,_,i=has_item(path,'HEAD.txt') 206 | if head!=False: 207 | head=markdown.markdown(head) 208 | return head,ext 209 | 210 | 211 | def CanEdit(filename): 212 | ext=filename.split('.')[-1] 213 | if ext in ["html","htm","php","css","go","java","js","json","txt","sh","md",".password"]: 214 | return True 215 | else: 216 | return False 217 | 218 | def CodeType(ext): 219 | code_type={} 220 | code_type['html'] = 'html'; 221 | code_type['htm'] = 'html'; 222 | code_type['php'] = 'php'; 223 | code_type['css'] = 'css'; 224 | code_type['go'] = 'golang'; 225 | code_type['java'] = 'java'; 226 | code_type['js'] = 'javascript'; 227 | code_type['json'] = 'json'; 228 | code_type['txt'] = 'Text'; 229 | code_type['sh'] = 'sh'; 230 | code_type['md'] = 'Markdown'; 231 | return code_type.get(ext.lower()) 232 | 233 | def file_ico(item): 234 | ext = item['name'].split('.')[-1].lower() 235 | if ext in ['bmp','jpg','jpeg','png','gif']: 236 | return "image"; 237 | 238 | if ext in ['mp4','mkv','webm','avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']: 239 | return "ondemand_video"; 240 | 241 | if ext in ['ogg','mp3','wav']: 242 | return "audiotrack"; 243 | 244 | return "insert_drive_file"; 245 | 246 | def _remote_content(fileid,user): 247 | kc='{}:content'.format(fileid) 248 | if rd.exists(kc): 249 | return rd.get(kc) 250 | else: 251 | downloadUrl=GetDownloadUrl(fileid,user) 252 | if downloadUrl: 253 | r=requests.get(downloadUrl) 254 | r.encoding='utf-8' 255 | content=r.content 256 | rd.set(kc,content) 257 | return content 258 | else: 259 | return False 260 | 261 | # @cache.memoize(timeout=60) 262 | def has_item(path,name): 263 | if items.count()==0: 264 | return False,False,False 265 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,name) 266 | if rd.exists(key): 267 | values=rd.get(key) 268 | item,fid,cur=values.split('########') 269 | if item=='False': 270 | item=False 271 | if cur=='False': 272 | cur=False 273 | else: 274 | cur=True 275 | if fid=='False': 276 | fid=False 277 | return item,fid,cur 278 | else: 279 | item=False 280 | fid=False 281 | dz=False 282 | cur=False 283 | if name=='.password': 284 | dz=True 285 | try: 286 | user,n_path=path.split(':') 287 | if n_path=='/': 288 | if items.find_one({'grandid':0,'name':name,'user':user}): 289 | fid=items.find_one({'grandid':0,'name':name,'user':user})['id'] 290 | item=_remote_content(fid,user).strip() 291 | else: 292 | route=n_path[1:].split('/') 293 | if name=='.password': 294 | for idx,r in enumerate(route): 295 | p=user+':/'+'/'.join(route[:idx+1]) 296 | f=items.find_one({'path':p}) 297 | pid=f['id'] 298 | data=items.find_one({'name':name,'parent':pid}) 299 | if data: 300 | fid=data['id'] 301 | item=_remote_content(fid,user).strip() 302 | if idx==len(route)-1: 303 | cur=True 304 | else: 305 | f=items.find_one({'path':path}) 306 | pid=f['id'] 307 | data=items.find_one({'name':name,'parent':pid}) 308 | if data: 309 | fid=data['id'] 310 | item=_remote_content(fid,user).strip() 311 | except: 312 | item=False 313 | rd.set(key,'{}########{}########{}'.format(item,fid,cur)) 314 | return item,fid,cur 315 | 316 | 317 | 318 | def verify_pass_before(path): 319 | plist=path_list(path) 320 | for i in [i for i in range(len(plist))]: 321 | n='/'.join(plist[:-i]) 322 | yield n 323 | 324 | def has_verify(path): 325 | verify=False 326 | md5_p=md5(path) 327 | passwd,fid,cur=has_item(path,'.password') 328 | if fid and cur: 329 | vp=request.cookies.get(md5_p) 330 | if passwd==vp: 331 | verify=True 332 | else: 333 | for last in verify_pass_before(path): 334 | if last=='': 335 | last='/' 336 | passwd,fid,cur=has_item(last,'.password') 337 | md5_p=md5(last) 338 | vp=request.cookies.get(md5_p) 339 | if passwd==vp: 340 | verify=True 341 | return verify 342 | 343 | 344 | def path_list(path): 345 | path=urllib.unquote(path) 346 | if path.split(':',1)=='': 347 | plist=[path+'/'] 348 | else: 349 | user,n_path=path.split(':',1) 350 | if n_path.startswith('/'): 351 | n_path=n_path[1:] 352 | if n_path.endswith('/'): 353 | n_path=n_path[:-1] 354 | plist=n_path.split('/') 355 | plist=['{}:/{}'.format(user,plist[0])]+plist[1:] 356 | return plist 357 | 358 | def get_od_user(): 359 | config_path=os.path.join(config_dir,'config.py') 360 | with open(config_path,'r') as f: 361 | text=f.read() 362 | users=json.loads(re.findall('od_users=([\w\W]*})',text)[0]) 363 | ret=[] 364 | for user,value in users.items(): 365 | if value.get('client_id')!='': 366 | #userid,username,endpoint,sharepath,order, 367 | ret.append( 368 | ( 369 | user, 370 | value.get('other_name'), 371 | '/{}:'.format(user), 372 | value.get('share_path'), 373 | value.get('order') 374 | ) 375 | ) 376 | else: 377 | ret.append( 378 | ( 379 | user, 380 | '添加网盘', 381 | url_for('admin.install',step=0,user=user), 382 | value.get('share_path'), 383 | value.get('order') 384 | ) 385 | ) 386 | ret=sorted(ret,key=lambda x:x[-1],reverse=False) 387 | return ret 388 | 389 | 390 | 391 | 392 | ################################################################################ 393 | ###################################试图函数##################################### 394 | ################################################################################ 395 | @app.before_request 396 | def before_request(): 397 | bad_ua=['Googlebot-Image','FeedDemon ','BOT/0.1 (BOT for JCE)','CrawlDaddy ','Java','Feedly','UniversalFeedParser','ApacheBench','Swiftbot','ZmEu','Indy Library','oBot','jaunty','YandexBot','AhrefsBot','MJ12bot','WinHttp','EasouSpider','HttpClient','Microsoft URL Control','YYSpider','jaunty','Python-urllib','lightDeckReports Bot','PHP','vxiaotou-spider','spider'] 398 | global referrer 399 | try: 400 | ip = request.headers['X-Forwarded-For'].split(',')[0] 401 | except: 402 | ip = request.remote_addr 403 | try: 404 | ua = request.headers.get('User-Agent') 405 | except: 406 | ua="null" 407 | if sum([i.lower() in ua.lower() for i in bad_ua])>0: 408 | return redirect('http://www.baidu.com') 409 | # print '{}:{}:{}'.format(request.endpoint,ip,ua) 410 | referrer=request.referrer if request.referrer is not None else 'no-referrer' 411 | 412 | @app.route('/',methods=['POST','GET']) 413 | @app.route('/',methods=['POST','GET']) 414 | @limiter.limit("200/minute;50/second") 415 | def index(path='A:/'): 416 | if path=='favicon.ico': 417 | return redirect('https://onedrive.live.com/favicon.ico') 418 | if items.count()==0: 419 | if not os.path.exists(os.path.join(config_dir,'data/.install')): 420 | return redirect(url_for('admin.install',step=0,user='A')) 421 | else: 422 | #subprocess.Popen('python {} UpdateFile'.format(os.path.join(config_dir,'function.py')),shell=True) 423 | return make_response('

正在更新数据!如果您是网站管理员,请在后台运行命令:python function.py UpdateFile

') 424 | #参数 425 | user,n_path=path.split(':') 426 | if n_path=='': 427 | path=':'.join([user,'/']) 428 | page=request.args.get('page',1,type=int) 429 | image_mode=request.args.get('image_mode') 430 | sortby=request.args.get('sortby') 431 | order=request.args.get('order') 432 | resp,total = FetchData(path=path,page=page,per_page=50,sortby=sortby,order=order,dismiss=True) 433 | if total=='files': 434 | return show(resp['id'],user) 435 | #是否有密码 436 | password,_,cur=has_item(path,'.password') 437 | md5_p=md5(path) 438 | has_verify_=has_verify(path) 439 | if request.method=="POST": 440 | password1=request.form.get('password') 441 | if password1==password: 442 | resp=make_response(redirect(url_for('.index',path=path))) 443 | resp.delete_cookie(md5_p) 444 | resp.set_cookie(md5_p,password) 445 | return resp 446 | if password!=False: 447 | if (not request.cookies.get(md5_p) or request.cookies.get(md5_p)!=password) and has_verify_==False: 448 | return render_template('password.html',path=path,cur_user=user) 449 | readme,ext_r=GetReadMe(path) 450 | head,ext_d=GetHead(path) 451 | #设置cookies 452 | if image_mode: 453 | image_mode=request.args.get('image_mode',type=int) 454 | else: 455 | image_mode=request.cookies.get('image_mode') if request.cookies.get('image_mode') is not None else 0 456 | image_mode=int(image_mode) 457 | if sortby: 458 | sortby=request.args.get('sortby') 459 | else: 460 | sortby=request.cookies.get('sortby') if request.cookies.get('sortby') is not None else 'lastModtime' 461 | sortby=sortby 462 | if order: 463 | order=request.args.get('order') 464 | else: 465 | order=request.cookies.get('order') if request.cookies.get('order') is not None else 'desc' 466 | order=order 467 | #参数 468 | resp,total = FetchData(path=path,page=page,per_page=50,sortby=sortby,order=order,dismiss=True) 469 | pagination=Pagination(query=None,page=page, per_page=50, total=total, items=None) 470 | if path.split(':',1)[-1]=='/': 471 | path=':'.join([path.split(':',1)[0],'']) 472 | resp=make_response(render_template('index.html' 473 | ,pagination=pagination 474 | ,items=resp 475 | ,path=path 476 | ,image_mode=image_mode 477 | ,readme=readme 478 | ,ext_r=ext_r 479 | ,head=head 480 | ,ext_d=ext_d 481 | ,sortby=sortby 482 | ,order=order 483 | ,cur_user=user 484 | ,endpoint='.index')) 485 | resp.set_cookie('image_mode',str(image_mode)) 486 | resp.set_cookie('sortby',str(sortby)) 487 | resp.set_cookie('order',str(order)) 488 | return resp 489 | 490 | @app.route('/file//') 491 | def show(fileid,user): 492 | name=GetName(fileid) 493 | ext=name.split('.')[-1].lower() 494 | path=GetPath(fileid) 495 | if request.method=='POST': 496 | url=request.url.replace(':80','').replace(':443','') 497 | if ext in ['csv','doc','docx','odp','ods','odt','pot','potm','potx','pps','ppsx','ppsxm','ppt','pptm','pptx','rtf','xls','xlsx']: 498 | downloadUrl=GetDownloadUrl(fileid,user) 499 | url = 'https://view.officeapps.live.com/op/view.aspx?src='+urllib.quote(downloadUrl) 500 | return redirect(url) 501 | elif ext in ['bmp','jpg','jpeg','png','gif']: 502 | return render_template('show/image.html',url=url,path=path,cur_user=user) 503 | elif ext in ['mp4','webm']: 504 | return render_template('show/video.html',url=url,path=path,cur_user=user) 505 | elif ext in ['mp4','webm','avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']: 506 | return render_template('show/video2.html',url=url,path=path,cur_user=user) 507 | elif ext in ['avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']: 508 | return render_template('show/video2.html',url=url,path=path,cur_user=user) 509 | elif ext in ['ogg','mp3','wav']: 510 | return render_template('show/audio.html',url=url,path=path,cur_user=user) 511 | elif CodeType(ext) is not None: 512 | content=_remote_content(fileid,user) 513 | return render_template('show/code.html',content=content,url=url,language=CodeType(ext),path=path,cur_user=user) 514 | else: 515 | downloadUrl=GetDownloadUrl(fileid,user) 516 | return redirect(downloadUrl) 517 | else: 518 | if 'no-referrer' in allow_site: 519 | downloadUrl=GetDownloadUrl(fileid,user) 520 | resp=redirect(downloadUrl) 521 | return resp 522 | elif sum([i in referrer for i in allow_site])>0: 523 | downloadUrl=GetDownloadUrl(fileid,user) 524 | return redirect(downloadUrl) 525 | else: 526 | return abort(404) 527 | 528 | @app.route('/robot.txt') 529 | def robot(): 530 | resp=""" 531 | User-agent: * 532 | Disallow: / 533 | """ 534 | resp=make_response(resp) 535 | resp.headers['Content-Type'] = 'text/javascript; charset=utf-8' 536 | return resp 537 | 538 | 539 | ######################注册应用 540 | from admin import admin as admin_blueprint 541 | app.register_blueprint(admin_blueprint) 542 | 543 | 544 | ######################函数 545 | app.jinja_env.globals['FetchData']=FetchData 546 | app.jinja_env.globals['path_list']=path_list 547 | app.jinja_env.globals['CanEdit']=CanEdit 548 | app.jinja_env.globals['len']=len 549 | app.jinja_env.globals['enumerate']=enumerate 550 | app.jinja_env.globals['os']=os 551 | app.jinja_env.globals['re']=re 552 | app.jinja_env.globals['file_ico']=file_ico 553 | app.jinja_env.globals['title']=title 554 | app.jinja_env.globals['tj_code']=tj_code if tj_code is not None else '' 555 | app.jinja_env.globals['get_od_user']=get_od_user 556 | app.jinja_env.globals['allow_site']=','.join(allow_site) 557 | # app.jinja_env.globals['share_path']=od_users.get('A').get('share_path') 558 | app.jinja_env.globals['downloadUrl_timeout']=downloadUrl_timeout 559 | ################################################################################ 560 | #####################################启动####################################### 561 | ################################################################################ 562 | if __name__=='__main__': 563 | app.run(port=58693,debug=True) 564 | 565 | 566 | 567 | -------------------------------------------------------------------------------- /function.py: -------------------------------------------------------------------------------- 1 | #-*- coding=utf-8 -*- 2 | import json 3 | import requests 4 | import collections 5 | import sys 6 | reload(sys) 7 | sys.setdefaultencoding('utf-8') 8 | if sys.version_info[0]==3: 9 | import urllib.parse as urllib 10 | else: 11 | import urllib 12 | import os 13 | import re 14 | import time 15 | import shutil 16 | import base64 17 | import humanize 18 | import StringIO 19 | from dateutil.parser import parse 20 | from Queue import Queue 21 | from threading import Thread 22 | from redis import Redis 23 | from config import * 24 | from pymongo import MongoClient,ASCENDING,DESCENDING 25 | ######mongodb 26 | client = MongoClient('localhost',27017) 27 | db=client.three 28 | items=db.items 29 | rd=Redis(host='localhost',port=6379) 30 | 31 | #######授权链接 32 | LoginUrl=BaseAuthUrl+'/common/oauth2/v2.0/authorize?response_type=code\ 33 | &client_id={client_id}&redirect_uri={redirect_uri}&scope=offline_access%20files.readwrite.all' 34 | OAuthUrl=BaseAuthUrl+'/common/oauth2/v2.0/token' 35 | AuthData='client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}&code={code}&grant_type=authorization_code' 36 | ReFreshData='client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}&refresh_token={refresh_token}&grant_type=refresh_token' 37 | 38 | headers={'User-Agent':'ISV|PyOne|PyOne/2.0'} 39 | 40 | def convert2unicode(string): 41 | return string.encode('utf-8') 42 | 43 | def get_value(key,user='A'): 44 | allow_key=['client_secret','client_id'] 45 | print('get user {}\'s {}'.format(user,key)) 46 | if key not in allow_key: 47 | return u'禁止获取' 48 | config_path=os.path.join(config_dir,'config.py') 49 | with open(config_path,'r') as f: 50 | text=f.read() 51 | kv=re.findall('"{}":{{[\w\W]*}}'.format(user),text)[0] 52 | value=re.findall('"{}":"(.*?)"'.format(key),kv)[0] 53 | return value 54 | 55 | def GetName(id): 56 | key='name:{}'.format(id) 57 | if rd.exists(key): 58 | return rd.get(key) 59 | else: 60 | item=items.find_one({'id':id}) 61 | rd.set(key,item['name']) 62 | return item['name'] 63 | 64 | def GetPath(id): 65 | key='path:{}'.format(id) 66 | if rd.exists(key): 67 | return rd.get(key) 68 | else: 69 | item=items.find_one({'id':id}) 70 | rd.set(key,item['path']) 71 | return item['path'] 72 | 73 | 74 | ################################################################################ 75 | ###################################授权函数##################################### 76 | ################################################################################ 77 | def open_json(filepath): 78 | token=False 79 | with open(filepath,'r') as f: 80 | try: 81 | token=json.load(f) 82 | except: 83 | for i in range(1,10): 84 | try: 85 | token=json.loads(f.read()[:-i]) 86 | except: 87 | token=False 88 | if token!=False: 89 | return token 90 | return token 91 | 92 | def ReFreshToken(refresh_token,user='A'): 93 | client_id=get_value('client_id',user) 94 | client_secret=get_value('client_secret',user) 95 | headers['Content-Type']='application/x-www-form-urlencoded' 96 | data=ReFreshData.format(client_id=client_id,redirect_uri=urllib.quote(redirect_uri),client_secret=client_secret,refresh_token=refresh_token) 97 | url=OAuthUrl 98 | r=requests.post(url,data=data,headers=headers) 99 | return json.loads(r.text) 100 | 101 | 102 | def GetToken(Token_file='token.json',user='A'): 103 | Token_file='{}_{}'.format(user,Token_file) 104 | if os.path.exists(os.path.join(data_dir,Token_file)): 105 | token=open_json(os.path.join(data_dir,Token_file)) 106 | try: 107 | if time.time()>int(token.get('expires_on')): 108 | print 'token timeout' 109 | refresh_token=token.get('refresh_token') 110 | token=ReFreshToken(refresh_token,user) 111 | if token.get('access_token'): 112 | with open(os.path.join(data_dir,Token_file),'w') as f: 113 | json.dump(token,f,ensure_ascii=False) 114 | except: 115 | with open(os.path.join(data_dir,'{}_Atoken.json'.format(user)),'r') as f: 116 | Atoken=json.load(f) 117 | refresh_token=Atoken.get('refresh_token') 118 | token=ReFreshToken(refresh_token,user) 119 | token['expires_on']=str(time.time()+3599) 120 | if token.get('access_token'): 121 | with open(os.path.join(data_dir,Token_file),'w') as f: 122 | json.dump(token,f,ensure_ascii=False) 123 | return token.get('access_token') 124 | else: 125 | return False 126 | 127 | 128 | def GetAppUrl(): 129 | return 'https://graph.microsoft.com/' 130 | 131 | ################################################################################ 132 | ###############################onedrive操作函数################################# 133 | ################################################################################ 134 | def GetExt(name): 135 | try: 136 | return name.split('.')[-1] 137 | except: 138 | return 'file' 139 | 140 | def date_to_char(date): 141 | return date.strftime('%Y/%m/%d') 142 | 143 | def Dir(path=u'A:/'): 144 | app_url=GetAppUrl() 145 | user,n_path=path.split(':') 146 | print('update {}\'s file'.format(user)) 147 | if n_path=='/': 148 | BaseUrl=app_url+u'v1.0/me/drive/root/children?expand=thumbnails' 149 | # items.remove() 150 | queue=Queue() 151 | # queue.put(dict(url=BaseUrl,grandid=grandid,parent=parent,trytime=1)) 152 | g=GetItemThread(queue,user) 153 | g.GetItem(BaseUrl) 154 | queue=g.queue 155 | if queue.qsize()==0: 156 | return 157 | tasks=[] 158 | for i in range(min(5,queue.qsize())): 159 | t=GetItemThread(queue,user) 160 | t.start() 161 | tasks.append(t) 162 | for t in tasks: 163 | t.join() 164 | RemoveRepeatFile() 165 | else: 166 | grandid=0 167 | parent='' 168 | if n_path.endswith('/'): 169 | n_path=n_path[:-1] 170 | if not n_path.startswith('/'): 171 | n_path='/'+n_path 172 | n_path=urllib.quote(n_path) 173 | BaseUrl=app_url+u'v1.0/me/drive/root:{}:/children?expand=thumbnails'.format(n_path) 174 | queue=Queue() 175 | # queue.put(dict(url=BaseUrl,grandid=grandid,parent=parent,trytime=1)) 176 | g=GetItemThread(queue,user) 177 | g.GetItem(BaseUrl,grandid,parent,1) 178 | queue=g.queue 179 | if queue.qsize()==0: 180 | return 181 | tasks=[] 182 | for i in range(min(10,queue.qsize())): 183 | t=GetItemThread(queue,user) 184 | t.start() 185 | tasks.append(t) 186 | for t in tasks: 187 | t.join() 188 | RemoveRepeatFile() 189 | 190 | def Dir_all(path=u'A:/'): 191 | app_url=GetAppUrl() 192 | user,n_path=path.split(':') 193 | print('update {}\'s {} file'.format(user,n_path)) 194 | if n_path=='/': 195 | BaseUrl=app_url+u'v1.0/me/drive/root/children?expand=thumbnails' 196 | items.remove({'user':user}) 197 | queue=Queue() 198 | g=GetItemThread(queue,user) 199 | g.GetItem(BaseUrl) 200 | queue=g.queue 201 | if queue.qsize()==0: 202 | return 203 | tasks=[] 204 | for i in range(min(5,queue.qsize())): 205 | t=GetItemThread(queue,user) 206 | t.start() 207 | tasks.append(t) 208 | for t in tasks: 209 | t.join() 210 | RemoveRepeatFile() 211 | else: 212 | grandid=0 213 | parent='' 214 | if n_path.endswith('/'): 215 | n_path=n_path[:-1] 216 | if not n_path.startswith('/'): 217 | n_path='/'+n_path 218 | if items.find_one({'grandid':0,'type':'folder','user':user}): 219 | parent_id=0 220 | for idx,p in enumerate(n_path[1:].split('/')): 221 | if parent_id==0: 222 | parent_id=items.find_one({'name':p,'grandid':idx,'user':user})['id'] 223 | else: 224 | parent_id=items.find_one({'name':p,'grandid':idx,'parent':parent_id})['id'] 225 | items.delete_many({'parent':parent_id}) 226 | grandid=idx+1 227 | parent=parent_id 228 | n_path=urllib.quote(n_path) 229 | BaseUrl=app_url+u'v1.0/me/drive/root:{}:/children?expand=thumbnails'.format(n_path) 230 | queue=Queue() 231 | g=GetItemThread(queue,user) 232 | g.GetItem(BaseUrl,grandid,parent,1) 233 | queue=g.queue 234 | if queue.qsize()==0: 235 | return 236 | tasks=[] 237 | for i in range(min(10,queue.qsize())): 238 | t=GetItemThread(queue,user) 239 | t.start() 240 | tasks.append(t) 241 | for t in tasks: 242 | t.join() 243 | RemoveRepeatFile() 244 | 245 | class GetItemThread(Thread): 246 | def __init__(self,queue,user): 247 | super(GetItemThread,self).__init__() 248 | self.queue=queue 249 | self.user=user 250 | share_path=od_users.get(user).get('share_path') 251 | if share_path=='/': 252 | self.share_path=share_path 253 | else: 254 | sp=share_path 255 | if not sp.startswith('/'): 256 | sp='/'+share_path 257 | if sp.endswith('/') and sp!='/': 258 | sp=sp[:-1] 259 | self.share_path=sp 260 | 261 | def run(self): 262 | while 1: 263 | time.sleep(0.5) #避免过快 264 | info=self.queue.get() 265 | url=info['url'] 266 | grandid=info['grandid'] 267 | parent=info['parent'] 268 | trytime=info['trytime'] 269 | self.GetItem(url,grandid,parent,trytime) 270 | if self.queue.empty(): 271 | time.sleep(5) #再等5s 272 | print('waiting 5s if queue is not empty') 273 | if self.queue.empty(): 274 | break 275 | 276 | def GetItem(self,url,grandid=0,parent='',trytime=1): 277 | app_url=GetAppUrl() 278 | token=GetToken(user=self.user) 279 | print(u'getting files from url {}'.format(url)) 280 | header={'Authorization': 'Bearer {}'.format(token)} 281 | try: 282 | r=requests.get(url,headers=header) 283 | data=json.loads(r.content) 284 | if data.get('error'): 285 | print('error:{}! waiting 180s'.format(data.get('error').get('message'))) 286 | time.sleep(180) 287 | self.queue.put(dict(url=url,grandid=grandid,parent=parent,trytime=trytime)) 288 | return 289 | values=data.get('value') 290 | if len(values)>0: 291 | for value in values: 292 | item={} 293 | if value.get('folder'): 294 | folder=items.find_one({'id':value['id']}) 295 | if folder is not None: 296 | if folder['size_order']==value['size']: #文件夹大小未变化,不更新 297 | print(u'path:{},origin size:{},current size:{}'.format(value['name'],folder['size_order'],value['size'])) 298 | else: 299 | items.delete_one({'id':value['id']}) 300 | item['type']='folder' 301 | item['user']=self.user 302 | item['order']=0 303 | item['name']=convert2unicode(value['name']) 304 | item['id']=convert2unicode(value['id']) 305 | item['size']=humanize.naturalsize(value['size'], gnu=True) 306 | item['size_order']=int(value['size']) 307 | item['lastModtime']=date_to_char(parse(value['lastModifiedDateTime'])) 308 | item['grandid']=grandid 309 | item['parent']=parent 310 | grand_path=value.get('parentReference').get('path').replace('/drive/root:','') 311 | if grand_path=='': 312 | path=convert2unicode(value['name']) 313 | else: 314 | path=grand_path.replace(self.share_path,'',1)+'/'+convert2unicode(value['name']) 315 | if path.startswith('/') and path!='/': 316 | path=path[1:] 317 | if path=='': 318 | path=convert2unicode(value['name']) 319 | path='{}:/{}'.format(self.user,path) 320 | item['path']=path 321 | subfodler=items.insert_one(item) 322 | if value.get('folder').get('childCount')==0: 323 | continue 324 | else: 325 | url=app_url+'v1.0/me'+value.get('parentReference').get('path')+'/'+value.get('name')+':/children?expand=thumbnails' 326 | self.queue.put(dict(url=url,grandid=grandid+1,parent=item['id'],trytime=1)) 327 | else: 328 | if items.find_one({'id':value['id']}) is not None: #文件存在 329 | continue 330 | else: 331 | item['type']=GetExt(value['name']) 332 | grand_path=value.get('parentReference').get('path').replace('/drive/root:','') 333 | if grand_path=='': 334 | path=convert2unicode(value['name']) 335 | else: 336 | path=grand_path.replace(self.share_path,'',1)+'/'+convert2unicode(value['name']) 337 | if path.startswith('/') and path!='/': 338 | path=path[1:] 339 | if path=='': 340 | path=convert2unicode(value['name']) 341 | path='{}:/{}'.format(self.user,path) 342 | item['path']=path 343 | item['user']=self.user 344 | item['name']=convert2unicode(value['name']) 345 | item['id']=convert2unicode(value['id']) 346 | item['size']=humanize.naturalsize(value['size'], gnu=True) 347 | item['size_order']=int(value['size']) 348 | item['lastModtime']=date_to_char(parse(value['lastModifiedDateTime'])) 349 | item['grandid']=grandid 350 | item['parent']=parent 351 | if GetExt(value['name']) in ['bmp','jpg','jpeg','png','gif']: 352 | item['order']=3 353 | key1='name:{}'.format(value['id']) 354 | key2='path:{}'.format(value['id']) 355 | rd.set(key1,value['name']) 356 | rd.set(key2,path) 357 | elif value['name']=='.password': 358 | item['order']=1 359 | else: 360 | item['order']=2 361 | items.insert_one(item) 362 | if data.get('@odata.nextLink'): 363 | self.queue.put(dict(url=data.get('@odata.nextLink'),grandid=grandid,parent=parent,trytime=1)) 364 | except Exception as e: 365 | trytime+=1 366 | print(u'error to opreate GetItem("{}","{}","{}"),try times :{}, reason: {}'.format(url,grandid,parent,trytime,e)) 367 | if trytime<=3: 368 | self.queue.put(dict(url=url,grandid=grandid,parent=parent,trytime=trytime)) 369 | 370 | 371 | def GetItemByPath(self,path): 372 | app_url=GetAppUrl() 373 | token=GetToken(user=self.user) 374 | if path=='' or path=='/': 375 | url=app_url+u'v1.0/me/drive/root/' 376 | if path=='/': 377 | url=app_url+u'v1.0/me/drive/root:{}:/'.format(path) 378 | header={'Authorization': 'Bearer {}'.format(token)} 379 | url=app_url+u'v1.0/me/drive/root:{}:/'.format(path) 380 | r=requests.get(url,headers=header) 381 | data=json.loads(r.content) 382 | return data 383 | 384 | def GetItemByUrl(self,url): 385 | app_url=GetAppUrl() 386 | token=GetToken(user=self.user) 387 | header={'Authorization': 'Bearer {}'.format(token)} 388 | r=requests.get(url,headers=header) 389 | data=json.loads(r.content) 390 | return data 391 | 392 | def GetRootid(user='A'): 393 | key='{}:rootid'.format(user) 394 | if rd.exists(key): 395 | return rd.get(key) 396 | else: 397 | app_url=GetAppUrl() 398 | token=GetToken(user=user) 399 | url=app_url+u'v1.0/me/drive/root/' 400 | header={'Authorization': 'Bearer {}'.format(token)} 401 | r=requests.get(url,headers=header) 402 | data=json.loads(r.content) 403 | rd.set(key,data['id'],3600) 404 | return data['id'] 405 | 406 | def UpdateFile(renew='all'): 407 | if renew=='all': 408 | items.remove() 409 | rd.flushdb() 410 | for user,item in od_users.items(): 411 | if item.get('client_id')!='': 412 | share_path='{}:{}'.format(user,item['share_path']) 413 | Dir_all(share_path) 414 | else: 415 | for user,item in od_users.items(): 416 | if item.get('client_id')!='': 417 | share_path='{}:{}'.format(user,item['share_path']) 418 | Dir(share_path) 419 | print('update file success!') 420 | 421 | 422 | def FileExists(filename,user='A'): 423 | token=GetToken(user=user) 424 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'} 425 | search_url=app_url+"v1.0/me/drive/root/search(q='{}')".format(filename) 426 | r=requests.get(search_url,headers=headers) 427 | jsondata=json.loads(r.text) 428 | if len(jsondata['value'])==0: 429 | return False 430 | else: 431 | return True 432 | 433 | def FileInfo(fileid,user='A'): 434 | token=GetToken(user=user) 435 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'} 436 | search_url=app_url+"v1.0/me/drive/items/{}".format(fileid) 437 | r=requests.get(search_url,headers=headers) 438 | jsondata=json.loads(r.text) 439 | return jsondata 440 | 441 | 442 | ################################################上传文件 443 | def list_all_files(rootdir): 444 | import os 445 | _files = [] 446 | if len(re.findall('[:#\|\?]+',rootdir))>0: 447 | newf=re.sub('[:#\|\?]+','',rootdir) 448 | shutil.move(rootdir,newf) 449 | rootdir=newf 450 | if rootdir.endswith(' '): 451 | shutil.move(rootdir,rootdir.rstrip()) 452 | rootdir=rootdir.rstrip() 453 | if len(re.findall('/ ',rootdir))>0: 454 | newf=re.sub('/ ','/',rootdir) 455 | shutil.move(rootdir,newf) 456 | rootdir=newf 457 | flist = os.listdir(rootdir) #列出文件夹下所有的目录与文件 458 | for f in flist: 459 | path = os.path.join(rootdir,f) 460 | if os.path.isdir(path): 461 | _files.extend(list_all_files(path)) 462 | if os.path.isfile(path): 463 | _files.append(path) 464 | return _files 465 | 466 | def _filesize(path): 467 | size=os.path.getsize(path) 468 | # print('{}\'s size {}'.format(path,size)) 469 | return size 470 | 471 | def _file_content(path,offset,length): 472 | size=_filesize(path) 473 | offset,length=map(int,(offset,length)) 474 | if offset>size: 475 | print('offset must smaller than file size') 476 | return False 477 | length=length if offset+length3: 514 | yield {'status':'upload fail! touch max retry time(3)'} 515 | break 516 | 517 | def _upload_part(uploadUrl, filepath, offset, length,trytime=1): 518 | size=_filesize(filepath) 519 | offset,length=map(int,(offset,length)) 520 | if offset>size: 521 | print('offset must smaller than file size') 522 | return {'status':'fail','msg':'params mistake','code':1} 523 | length=length if offset+length0: 770 | newf=os.path.join(dir_,re.sub('[:/#\|]+','',fname)) 771 | shutil.move(f,newf) 772 | localfiles=list_all_files(local_dir) 773 | check_file_list=[] 774 | if local_dir.endswith('/'): 775 | local_dir=local_dir[:-1] 776 | for file in localfiles: 777 | dir_,fname=os.path.dirname(file),os.path.basename(file) 778 | remote_path=remote_dir+'/'+dir_.replace(local_dir,'')+'/'+fname 779 | remote_path=remote_path.replace('//','/') 780 | check_file_list.append((remote_path,file)) 781 | print(u'check repeat file') 782 | if remote_dir=='/': 783 | cloud_files=_GetAllFile() 784 | else: 785 | if remote_dir.startswith('/'): 786 | remote_dir=remote_dir[1:] 787 | if items.find_one({'grandid':0,'type':'folder','name':remote_dir.split('/')[0]}): 788 | parent_id=0 789 | parent_path='' 790 | for idx,p in enumerate(remote_dir.split('/')): 791 | if parent_id==0: 792 | parent=items.find_one({'name':p,'grandid':idx}) 793 | parent_id=parent['id'] 794 | parent_path='/'.join([parent_path,parent['name']]) 795 | else: 796 | parent=items.find_one({'name':p,'grandid':idx,'parent':parent_id}) 797 | parent_id=parent['id'] 798 | parent_path='/'.join([parent_path,parent['name']]) 799 | grandid=idx+1 800 | cloud_files=_GetAllFile(parent_id,parent_path) 801 | try: 802 | cloud_files=dict([(i,i) for i in cloud_files]) 803 | except: 804 | cloud_files={} 805 | queue=Queue() 806 | tasks=[] 807 | for remote_path,file in check_file_list: 808 | if not cloud_files.get(base64.b64encode(remote_path)): 809 | queue.put((file,remote_path)) 810 | print "check_file_list {},cloud_files {},queue {}".format(len(check_file_list),len(cloud_files),queue.qsize()) 811 | print "start upload files 5s later" 812 | time.sleep(5) 813 | for i in range(min(threads,queue.qsize())): 814 | t=MultiUpload(queue,user) 815 | t.start() 816 | tasks.append(t) 817 | for t in tasks: 818 | t.join() 819 | #删除错误数据 820 | RemoveRepeatFile() 821 | 822 | 823 | 824 | ########################删除文件 825 | def DeleteLocalFile(fileid): 826 | items.remove({'id':fileid}) 827 | 828 | def DeleteRemoteFile(fileid,user='A'): 829 | app_url=GetAppUrl() 830 | token=GetToken(user=user) 831 | headers={'Authorization':'bearer {}'.format(token)} 832 | url=app_url+'v1.0/me/drive/items/'+fileid 833 | r=requests.delete(url,headers=headers) 834 | if r.status_code==204: 835 | DeleteLocalFile(fileid) 836 | return True 837 | else: 838 | DeleteLocalFile(fileid) 839 | return False 840 | 841 | ######################## 842 | def CreateFolder(folder_name,grand_path,user='A'): 843 | app_url=GetAppUrl() 844 | token=GetToken(user=user) 845 | if grand_path=='' or grand_path is None or grand_path=='/': 846 | url=app_url+'v1.0/me/drive/root/children' 847 | parent_id='' 848 | grandid=0 849 | else: 850 | path='{}:/{}'.format(user,grand_path) 851 | parent=items.find_one({'path':path}) 852 | parent_id=parent['id'] 853 | grandid=parent['grandid']+1 854 | url=app_url+'v1.0/me/drive/items/{}/children'.format(parent['id']) 855 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'} 856 | payload={ 857 | "name": folder_name, 858 | "folder": {}, 859 | "@microsoft.graph.conflictBehavior": "rename" 860 | } 861 | r=requests.post(url,headers=headers,data=json.dumps(payload)) 862 | data=json.loads(r.content) 863 | if data.get('id'): 864 | #插入数据 865 | share_path=od_users.get(user).get('share_path') 866 | item={} 867 | item['type']='folder' 868 | item['user']=user 869 | item['name']=data.get('name') 870 | item['id']=data.get('id') 871 | item['size']=humanize.naturalsize(data.get('size'), gnu=True) 872 | item['size_order']=data.get('size') 873 | item['lastModtime']=date_to_char(parse(data.get('lastModifiedDateTime'))) 874 | item['grandid']=grandid 875 | item['parent']=parent_id 876 | if grand_path=='' or grand_path is None or grand_path=='/': 877 | path=convert2unicode(data['name']) 878 | else: 879 | path=grand_path.replace(share_path,'',1)+'/'+convert2unicode(data['name']) 880 | if not path.startswith('/'): 881 | path='/'+path 882 | path='{}:{}'.format(user,path) 883 | item['path']=path 884 | item['order']=0 885 | items.insert_one(item) 886 | return True 887 | else: 888 | print(data.get('error').get('msg')) 889 | return False 890 | 891 | def MoveFile(fileid,new_folder_path,user='A'): 892 | app_url=GetAppUrl() 893 | token=GetToken(user=user) 894 | #GetRootid 895 | if new_folder_path=='' or new_folder_path is None or new_folder_path=='/': 896 | folder_id=GetRootid(user) 897 | parent='' 898 | grandid=0 899 | path=GetName(fileid) 900 | else: 901 | path='{}:/{}'.format(user,new_folder_path) 902 | parent_item=items.find_one({'path':path}) 903 | folder_id=parent_item['id'] 904 | parent=parent_item['id'] 905 | grandid=parent_item['grandid']+1 906 | path=parent_item['path']+'/'+GetName(fileid) 907 | url=app_url+'v1.0/me/drive/items/{}'.format(fileid) 908 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'} 909 | payload={ 910 | "parentReference": { 911 | "id": folder_id 912 | }, 913 | "name": GetName(fileid) 914 | } 915 | r=requests.patch(url,headers=headers,data=json.dumps(payload)) 916 | data=json.loads(r.content) 917 | if data.get('id'): 918 | new_value={'parent':parent,'grandid':grandid,'path':path} 919 | items.find_one_and_update({'id':fileid},{'$set':new_value}) 920 | file=items.find_one({'id':fileid}) 921 | filename=file['name'] 922 | if file['parent']=='': 923 | path='/' 924 | else: 925 | path=items.find_one({'id':file['parent']})['path'] 926 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,filename) 927 | rd.delete(key) 928 | return True 929 | else: 930 | print(data.get('error').get('msg')) 931 | return False 932 | 933 | def CheckTimeOut(fileid): 934 | app_url=GetAppUrl() 935 | token=GetToken() 936 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'} 937 | url=app_url+'v1.0/me/drive/items/'+fileid 938 | r=requests.get(url,headers=headers) 939 | data=json.loads(r.content) 940 | if data.get('@microsoft.graph.downloadUrl'): 941 | downloadUrl=data.get('@microsoft.graph.downloadUrl') 942 | start_time=time.time() 943 | for i in range(10000): 944 | r=requests.head(downloadUrl) 945 | print '{}\'s gone, status:{}'.format(time.time()-start_time,r.status_code) 946 | if r.status_code==404: 947 | break 948 | 949 | def RemoveRepeatFile(): 950 | """ 951 | db.items.aggregate([ 952 | { 953 | $group:{_id:{id:'$id'},count:{$sum:1},dups:{$addToSet:'$_id'}} 954 | }, 955 | { 956 | $match:{count:{$gt:1}} 957 | } 958 | 959 | ]).forEach(function(it){ 960 | 961 | it.dups.shift(); 962 | db.items.remove({_id: {$in: it.dups}}); 963 | 964 | }); 965 | """ 966 | deleteData=items.aggregate([ 967 | {'$group': { 968 | '_id': { 'id': "$id"}, 969 | 'uniqueIds': { '$addToSet': "$_id" }, 970 | 'count': { '$sum': 1 } 971 | }}, 972 | { '$match': { 973 | 'count': { '$gt': 1 } 974 | }} 975 | ]); 976 | first=True 977 | try: 978 | for d in deleteData: 979 | first=True 980 | for did in d['uniqueIds']: 981 | if not first: 982 | items.delete_one({'_id':did}); 983 | first=False 984 | except Exception as e: 985 | print(e) 986 | return 987 | 988 | 989 | if __name__=='__main__': 990 | func=sys.argv[1] 991 | if len(sys.argv)>2: 992 | args=sys.argv[2:] 993 | eval(func+str(tuple(args))) 994 | else: 995 | eval(func+'()') 996 | -------------------------------------------------------------------------------- /static/img/bg-bottom.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------