diff --git a/plugins/pm2/ico.png b/plugins/pm2/ico.png
new file mode 100644
index 000000000..14dc09706
Binary files /dev/null and b/plugins/pm2/ico.png differ
diff --git a/plugins/pm2/index.html b/plugins/pm2/index.html
new file mode 100755
index 000000000..834706386
--- /dev/null
+++ b/plugins/pm2/index.html
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/plugins/pm2/index.py b/plugins/pm2/index.py
new file mode 100755
index 000000000..461db59c8
--- /dev/null
+++ b/plugins/pm2/index.py
@@ -0,0 +1,474 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import shutil
+
+sys.path.append(os.getcwd() + "/class/core")
+import public
+
+app_debug = False
+if public.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'pureftp'
+
+
+def getPluginDir():
+ return public.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return public.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getConf():
+ path = getServerDir() + "/etc/pure-ftpd.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/pure-ftpd.tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ 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 status():
+ cmd = "ps -ef|grep pure-ftpd |grep -v grep | grep -v python | awk '{print $2}'"
+ data = public.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def contentReplace(content):
+ service_path = public.getServerDir()
+ content = content.replace('{$ROOT_PATH}', public.getRootDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ 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 = public.readFile(file_tpl)
+ content = contentReplace(content)
+ public.writeFile(file_bin, content)
+ public.execShell('chmod +x ' + file_bin)
+
+ pureSbinConfig = getServerDir() + "/sbin/pure-config.pl"
+ if not os.path.exists(pureSbinConfig):
+ pureTplConfig = getPluginDir() + "/init.d/pure-config.pl"
+ content = public.readFile(pureTplConfig)
+ content = contentReplace(content)
+ public.writeFile(pureSbinConfig, content)
+ public.execShell('chmod +x ' + pureSbinConfig)
+
+ pureFtpdConfig = getServerDir() + "/etc/pure-ftpd.conf"
+ pureFtpdConfigBak = getServerDir() + "/etc/pure-ftpd.bak.conf"
+ pureFtpdConfigTpl = getPluginDir() + "/conf/pure-ftpd.conf"
+
+ if not os.path.exists(pureFtpdConfigBak):
+ shutil.copyfile(pureFtpdConfig, pureFtpdConfigBak)
+ content = public.readFile(pureFtpdConfigTpl)
+ content = contentReplace(content)
+ public.writeFile(pureFtpdConfig, content)
+
+ return file_bin
+
+
+def start():
+ file = initDreplace()
+ data = public.execShell(file + ' start')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def stop():
+ file = initDreplace()
+ data = public.execShell(file + ' stop')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def restart():
+ file = initDreplace()
+ data = public.execShell(file + ' restart')
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+
+def reload():
+ file = initDreplace()
+ data = public.execShell(file + ' reload')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def initdStatus():
+ if not app_debug:
+ os_name = public.getOs()
+ if os_name == 'darwin':
+ return "Apple Computer does not support"
+ initd_bin = getInitDFile()
+ if os.path.exists(initd_bin):
+ return 'ok'
+ return 'fail'
+
+
+def initdInstall():
+ import shutil
+ if not app_debug:
+ os_name = public.getOs()
+ if os_name == 'darwin':
+ return "Apple Computer does not support"
+
+ source_bin = initDreplace()
+ initd_bin = getInitDFile()
+ shutil.copyfile(source_bin, initd_bin)
+ public.execShell('chmod +x ' + initd_bin)
+ return 'ok'
+
+
+def initdUinstall():
+ if not app_debug:
+ os_name = public.getOs()
+ if os_name == 'darwin':
+ return "Apple Computer does not support"
+ initd_bin = getInitDFile()
+ os.remove(initd_bin)
+ return 'ok'
+
+
+def pftpDB():
+ file = getServerDir() + '/ftps.db'
+ if not os.path.exists(file):
+ conn = public.M('ftps').dbPos(getServerDir(), 'ftps')
+ csql = public.readFile(getPluginDir() + '/conf/ftps.sql')
+ csql_list = csql.split(';')
+ for index in range(len(csql_list)):
+ conn.execute(csql_list[index], ())
+ else:
+ conn = public.M('ftps').dbPos(getServerDir(), 'ftps')
+ return conn
+
+
+def pftpUser():
+ if public.isAppleSystem():
+ user = public.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return user
+ return 'www'
+
+
+def pftpAdd(username, password, path):
+ user = pftpUser()
+
+ if not os.path.exists(path):
+ os.makedirs(path)
+ if public.isAppleSystem():
+ os.system('chown ' + user + '.staff ' + path)
+ else:
+ os.system('chown www.www ' + path)
+
+ cmd = getServerDir() + '/bin/pure-pw useradd ' + username + ' -u ' + user + ' -d ' + \
+ path + '< 65535:
+ return '端口范围不正确!'
+ file = file = getServerDir() + '/etc/pure-ftpd.conf'
+ conf = public.readFile(file)
+ rep = u"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)"
+ # preg_match(rep,conf,tmp)
+ conf = re.sub(
+ rep, "\nBind 0.0.0.0," + port, conf)
+ public.writeFile(file, conf)
+ restart()
+ return 'ok'
+ except Exception as ex:
+ return str(ex)
+
+
+def stopPort():
+ args = getArgs()
+ if not 'id' in args:
+ return 'id missing'
+
+ if not 'username' in args:
+ return 'username missing'
+
+ if not 'status' in args:
+ return 'status missing'
+
+ data = pftpStop(args['username'])
+ pftpReload()
+ conn = pftpDB()
+ conn.where('id=?', (int(args['id']),)).save(
+ 'status', (args['status'],))
+
+ if data[1] == '':
+ return 'ok'
+ return data[0]
+
+
+def startPort():
+ args = getArgs()
+ if not 'id' in args:
+ return 'id missing'
+
+ if not 'username' in args:
+ return 'username missing'
+
+ if not 'status' in args:
+ return 'status missing'
+
+ data = pftpStart(args['username'])
+ pftpReload()
+ conn = pftpDB()
+ conn.where('id=?', (int(args['id']),)).save(
+ 'status', (args['status'],))
+
+ if data[1] == '':
+ return 'ok'
+ return data[0]
+
+
+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 == 'get_www_dir':
+ print getWwwDir()
+ elif func == 'get_ftp_list':
+ print getFtpList()
+ elif func == 'add_ftp':
+ print addFtp()
+ elif func == 'del_ftp':
+ print delFtp()
+ elif func == 'mod_ftp':
+ print modFtp()
+ elif func == 'mod_ftp_port':
+ print modFtpPort()
+ elif func == 'stop_ftp':
+ print stopPort()
+ elif func == 'start_ftp':
+ print startPort()
+ else:
+ print 'error'
diff --git a/plugins/pm2/info.json b/plugins/pm2/info.json
new file mode 100755
index 000000000..5e4c37e4f
--- /dev/null
+++ b/plugins/pm2/info.json
@@ -0,0 +1,15 @@
+{
+ "title":"PM2管理器",
+ "tip":"soft",
+ "name":"pm2",
+ "type":"运行环境",
+ "ps":"node.js管理器,内置 node.js + npm + nvm + pm2.!",
+ "versions":["4.4.15","4.8.4"],
+ "updates":["4.4.15","4.8.4"],
+ "shell":"install.sh",
+ "checks":"server/pm2",
+ "author":"pm2",
+ "home":"http://pm2.keymetrics.io/",
+ "date":"2019-02-27",
+ "pid": "4"
+}
\ No newline at end of file
diff --git a/plugins/pm2/install.sh b/plugins/pm2/install.sh
new file mode 100755
index 000000000..6f16b3bf5
--- /dev/null
+++ b/plugins/pm2/install.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+
+install_tmp=${rootPath}/tmp/bt_install.pl
+
+npm install pm2 -g
+
+Install_pm2()
+{
+ echo '正在安装脚本文件...' > $install_tmp
+ mkdir -p $serverPath/pm2
+ echo '1.0' > $serverPath/pm2/version.pl
+ echo '安装完成' > $install_tmp
+}
+
+Uninstall_pm2()
+{
+ rm -rf $serverPath/pm2
+ echo "卸载完成" > $install_tmp
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_pm2
+else
+ Uninstall_pm2
+fi