diff --git a/plugins/cloudreve/ico.png b/plugins/cloudreve/ico.png
new file mode 100644
index 000000000..82bcc51f3
Binary files /dev/null and b/plugins/cloudreve/ico.png differ
diff --git a/plugins/cloudreve/index.html b/plugins/cloudreve/index.html
new file mode 100755
index 000000000..d50a2be40
--- /dev/null
+++ b/plugins/cloudreve/index.html
@@ -0,0 +1,31 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/cloudreve/index.py b/plugins/cloudreve/index.py
new file mode 100755
index 000000000..0197f519d
--- /dev/null
+++ b/plugins/cloudreve/index.py
@@ -0,0 +1,296 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'cloudreve'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return '/tmp/' + getPluginName()
+
+ if current_os.startswith('freebsd'):
+ return '/etc/rc.d/' + getPluginName()
+
+ return '/etc/init.d/' + getPluginName()
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ return tmp
+
+def checkArgs(data, ck=[]):
+ for i in range(len(ck)):
+ if not ck[i] in data:
+ return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
+ return (True, mw.returnJson(True, 'ok'))
+
+
+def getConf():
+ path = getServerDir() + "/conf.ini"
+ return path
+
+def configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+def status():
+ data = mw.execShell(
+ "ps aux|grep cloudreve |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
+
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
+ return content
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = os.path.dirname(os.getcwd())
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/' + getPluginName() + '.service'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
+ service_path = mw.getServerDir()
+ se_content = mw.readFile(systemServiceTpl)
+ se_content = se_content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, se_content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def alistOp(method):
+ file = initDreplace()
+
+ current_os = mw.getOs()
+ if current_os == "darwin":
+ data = mw.execShell(file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ if current_os.startswith("freebsd"):
+ data = mw.execShell('service ' + getPluginName() + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return alistOp('start')
+
+
+def stop():
+ return alistOp('stop')
+
+
+def restart():
+ status = alistOp('restart')
+ return status
+
+
+def reload():
+ return alistOp('reload')
+
+
+def initdStatus():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ if current_os.startswith('freebsd'):
+ initd_bin = getInitDFile()
+ if os.path.exists(initd_bin):
+ return 'ok'
+
+ shell_cmd = 'systemctl status ' + \
+ getPluginName() + ' | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ # freebsd initd install
+ if current_os.startswith('freebsd'):
+ import shutil
+ source_bin = initDreplace()
+ initd_bin = getInitDFile()
+ shutil.copyfile(source_bin, initd_bin)
+ mw.execShell('chmod +x ' + initd_bin)
+ mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
+ return 'ok'
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ return 'ok'
+
+
+def initdUinstall():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ if current_os.startswith('freebsd'):
+ initd_bin = getInitDFile()
+ os.remove(initd_bin)
+ mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
+ return 'ok'
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def runLog():
+ return getServerDir() + '/logs.pl'
+
+def pSqliteDb(dbname='databases'):
+ pos_file = getServerDir() + '/data/'
+ file = pos_file + '/data.db'
+ name = 'data'
+ conn = mw.M(dbname).dbPos(pos_file, name)
+ return conn
+
+def clearCopyTask():
+ conn = pSqliteDb('x_task_items')
+ conn.where('key=?', ('copy',)).setField('persist_data','[]')
+ restart()
+ return mw.returnJson(True, '清空成功并重启服务!')
+
+def getCloudrevePort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'Listen\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def homePage():
+ http_port = getCloudrevePort()
+ ip = mw.getLocalIp()
+ if mw.isAppleSystem():
+ ip = '127.0.0.1'
+ url = 'http://'+ip+""+str(http_port)
+ # print(url)
+ return mw.returnJson(True, 'ok!', url)
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'status':
+ print(status())
+ elif func == 'start':
+ print(start())
+ elif func == 'stop':
+ print(stop())
+ elif func == 'restart':
+ print(restart())
+ elif func == 'reload':
+ print(reload())
+ elif func == 'initd_status':
+ print(initdStatus())
+ elif func == 'initd_install':
+ print(initdInstall())
+ elif func == 'initd_uninstall':
+ print(initdUinstall())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ elif func == 'clear_copy_task':
+ print(clearCopyTask())
+ elif func == 'home_page':
+ print(homePage())
+ else:
+ print('error')
diff --git a/plugins/cloudreve/info.json b/plugins/cloudreve/info.json
new file mode 100755
index 000000000..f89aee960
--- /dev/null
+++ b/plugins/cloudreve/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 7,
+ "ps": "一个支持多种存储的文件列表程序",
+ "name": "cloudreve",
+ "title": "cloudreve",
+ "shell": "install.sh",
+ "versions":["3.8.3"],
+ "tip": "soft",
+ "checks": "server/cloudreve",
+ "path": "server/cloudreve",
+ "display": 1,
+ "author": "cloudreve",
+ "date": "2024-10-14",
+ "home": "https://cloudreve.org/",
+ "type": 0,
+ "pid": "5"
+}
diff --git a/plugins/cloudreve/init.d/cloudreve.service.tpl b/plugins/cloudreve/init.d/cloudreve.service.tpl
new file mode 100644
index 000000000..42c1fbc57
--- /dev/null
+++ b/plugins/cloudreve/init.d/cloudreve.service.tpl
@@ -0,0 +1,13 @@
+[Unit]
+Description=A file list program that supports multiple storage
+After=network.target
+
+[Service]
+Type=simple
+WorkingDirectory={$SERVER_PATH}/cloudreve
+ExecStart={$SERVER_PATH}/cloudreve/cloudreve
+ExecReload=/bin/kill -USR2 $MAINPID
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/cloudreve/init.d/cloudreve.tpl b/plugins/cloudreve/init.d/cloudreve.tpl
new file mode 100644
index 000000000..f740d1cd7
--- /dev/null
+++ b/plugins/cloudreve/init.d/cloudreve.tpl
@@ -0,0 +1,46 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: cloudreve Service
+
+### BEGIN INIT INFO
+# Provides: cloudreve
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts cloudreve
+# Description: starts the MDW-Web
+### END INIT INFO
+
+# Simple cloudreve init.d script conceived to work on Linux systems
+# as it does use of the /proc filesystem.
+
+app_start(){
+ echo "Starting cloudreve server..."
+ cd {$SERVER_PATH}/cloudreve
+ ./cloudreve >> {$SERVER_PATH}/cloudreve/logs.pl 2>&1 &
+}
+
+app_stop(){
+ echo "dztasks stopped"
+ ps -ef| grep cloudreve | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
+}
+
+
+case "$1" in
+ start)
+ app_start
+ ;;
+ stop)
+ app_stop
+ ;;
+ restart|reload)
+ app_stop
+ sleep 0.3
+ app_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/cloudreve/install.sh b/plugins/cloudreve/install.sh
new file mode 100755
index 000000000..0de5e44ef
--- /dev/null
+++ b/plugins/cloudreve/install.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
+# cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
+
+install_tmp=${rootPath}/tmp/mw_install.pl
+VERSION=$2
+
+sysArch=`arch`
+sysName=`uname`
+
+ALIST_ARCH_NAME=amd64
+if [ "$sysArch" == "arm64" ];then
+ ALIST_ARCH_NAME=arm64
+elif [ "$sysArch" == "x86_64" ]; then
+ ALIST_ARCH_NAME=amd64
+elif [ "$sysArch" == "aarch64" ]; then
+ ALIST_ARCH_NAME=aarch64
+fi
+
+ALIST_NAME=linux
+if [ "$sysName" == "Darwin" ];then
+ ALIST_NAME=darwin
+fi
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/source/cloudreve
+
+ FILE_TGZ=cloudreve_${VERSION}_${ALIST_NAME}_${ALIST_ARCH_NAME}.tar.gz
+ CLOUDREVE_DIR=$serverPath/source/cloudreve
+
+ if [ ! -f $CLOUDREVE_DIR/${FILE_TGZ} ];then
+ wget -O $CLOUDREVE_DIR/${FILE_TGZ} https://github.com/cloudreve/Cloudreve/releases/download/${VERSION}/${FILE_TGZ}
+ fi
+
+ mkdir -p $serverPath/cloudreve
+
+ cd $CLOUDREVE_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/cloudreve
+ echo "${VERSION}" > $serverPath/cloudreve/version.pl
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py initd_install
+
+ # cd $serverPath/cloudreve && ./alist admin set admin
+ echo '安装cloudreve完成'
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/cloudreve.service ];then
+ systemctl stop cloudreve
+ systemctl disable cloudreve
+ rm -rf /usr/lib/systemd/system/cloudreve.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/cloudreve.service ];then
+ systemctl stop cloudreve
+ systemctl disable cloudreve
+ rm -rf /lib/systemd/system/cloudreve.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/cloudreve/initd/cloudreve ];then
+ $serverPath/cloudreve/initd/cloudreve stop
+ fi
+
+ if [ -d $serverPath/cloudreve ];then
+ rm -rf $serverPath/cloudreve
+ fi
+
+ echo "卸载cloudreve成功"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/cloudreve/js/cloudreve.js b/plugins/cloudreve/js/cloudreve.js
new file mode 100755
index 000000000..c956ca952
--- /dev/null
+++ b/plugins/cloudreve/js/cloudreve.js
@@ -0,0 +1,92 @@
+function alistPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'cloudreve';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function alistPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'cloudreve';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function clearTaskCopy(){
+ layer.confirm('您真的要清空复制任务吗?', { icon: 3, closeBtn: 2 }, function() {
+ alistPost('clear_copy_task', '', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg, function(){},{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+}
+
+function commonHomePage(){
+ alistPost('home_page', '', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ window.open(rdata.data);
+ });
+}
+
+function alistCommonFunc(){
+ var con = '';
+ con += '
\
+ \
+ \
+
';
+
+ $(".soft-man-con").html(con);
+}
+
+function alistReadme(){
+
+
+ var readme = '';
+ readme += '- 默认admin:admin
';
+ readme += '- 手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD
';
+ readme += '
';
+
+ $('.soft-man-con').html(readme);
+}
+
diff --git a/web/readme.md b/web/readme.md
index a52e53bbe..25d6030be 100644
--- a/web/readme.md
+++ b/web/readme.md
@@ -4,5 +4,6 @@
```
gunicorn -b :7201 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
gunicorn -b :7201 -w 1 app:app
+gunicorn -c setting.py app:app
```
\ No newline at end of file