Merge pull request #160 from midoks/dev

0.9.4
pull/163/head
Mr Chen 3 years ago committed by GitHub
commit 1a5c7b9a3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      README.md
  2. 4
      class/core/site_api.py
  3. 8
      class/plugin/orm.py
  4. 5
      mw-cli
  5. 2
      plugins/mariadb/conf/my.cnf
  6. 58
      plugins/mariadb/index.py
  7. 9
      plugins/mariadb/js/mariadb.js
  8. 8
      plugins/mysql/js/mysql.js
  9. 32
      plugins/op_waf/waf/lua/init.lua
  10. 3
      plugins/phpmyadmin/conf/config.inc.php
  11. 7
      plugins/phpmyadmin/conf/phpmyadmin.conf
  12. 143
      plugins/phpmyadmin/index.py
  13. 68
      plugins/phpmyadmin/js/phpmyadmin.js
  14. 4
      plugins/redis/info.json
  15. 1
      scripts/install/debian.sh
  16. 1
      scripts/install/ubuntu.sh

@ -1,4 +1,4 @@
### mdserver-web 0.9.3
### mdserver-web 0.9.4
简单的Linux面板,感谢BT.CN写出如此好的web管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
复制了后台管理界面,按照自己想要的方式写了一版。
@ -71,13 +71,12 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
### 版本更新 0.9.3
### 版本更新 0.9.4
* 修复maridb的用户管理。
* OP防火墙优化。
* phpmyadmin插件优化。
* 批量域名添加修复。
* 常用命令说明。
* 重写页面更新功能。
* MySQL8.0安装地址修改。
* 删除MySQL[Yum]插件。
### JSDelivr安装地址

@ -1140,10 +1140,10 @@ class site_api:
if conf.find(rep) == -1:
rep = '#error_page 404/404.html;'
data = '''
# AUTH_START
#AUTH_START
auth_basic "Authorization";
auth_basic_user_file %s;
# AUTH_END''' % (filename,)
#AUTH_END''' % (filename,)
conf = conf.replace(rep, rep + data)
mw.writeFile(configFile, conf)
# 写密码配置

@ -11,6 +11,7 @@ class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_NAME = ''
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
@ -27,21 +28,25 @@ class ORM:
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
else:
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
@ -69,6 +74,9 @@ class ORM:
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():

@ -48,6 +48,11 @@ def mw_reset_pwd():
if __name__ == '__main__':
if os.geteuid() != 0:
print("请使用root账户执行该命令.")
sys.exit(1)
os.chdir("/www/server/mdserver-web")
sys.path.append(os.getcwd() + "/class/core")

@ -45,11 +45,11 @@ skip-name-resolve = on
log-bin=mysql-bin
binlog_format=mixed
expire_logs_days = 10
slow_query_log=1
slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log
long_query_time=3
#log_queries_not_using_indexes=on
binlog-expire-logs-seconds = 2592000
relay-log=mdserver
relay-log-index=mdserver

@ -150,7 +150,7 @@ def pSqliteDb(dbname='databases'):
return conn
def pMysqlDb():
def pMysqlDb(name=''):
# pymysql
db = mw.getMyORM()
# MySQLdb |
@ -159,6 +159,7 @@ def pMysqlDb():
db.setDbConf(getConf())
db.setPort(getDbPort())
db.setSocket(getSocketFile())
db.setDbName(name)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
db.setPwd(pwd)
@ -797,7 +798,7 @@ def syncGetDatabases():
if isError != None:
return isError
users = pdb.query(
"select User,Host from mysql.user where User!='root' AND Host!='localhost' AND Host!=''")
"select User,Host from user where User!='root' AND Host!='localhost' AND Host!=''")
nameArr = ['information_schema', 'performance_schema', 'mysql', 'sys']
n = 0
@ -893,16 +894,19 @@ def setRootPwd(version=''):
password = args['password']
try:
pdb = pMysqlDb()
pdb = pMysqlDb('mysql')
result = pdb.query("show databases")
isError = isSqlError(result)
if isError != None:
return isError
result = pdb.execute(
"update mysql.user set Password=password('" + password + "') where User='root'")
pdb.execute("flush privileges")
cmd = "ALTER USER 'root'@'localhost' IDENTIFIED BY '" + password + "';"
r = pdb.execute(cmd)
# print(r)
pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (password,))
orm = pMysqlDb()
orm.execute("flush privileges")
return mw.returnJson(True, '数据库root密码修改成功!')
except Exception as ex:
return mw.returnJson(False, '修改错误:' + str(ex))
@ -910,7 +914,7 @@ def setRootPwd(version=''):
def setUserPwd(version=''):
args = getArgs()
data = checkArgs(args, ['password', 'name'])
data = checkArgs(args, ['password', 'name', 'id'])
if not data[0]:
return data[1]
@ -920,16 +924,28 @@ def setUserPwd(version=''):
try:
pdb = pMysqlDb()
psdb = pSqliteDb('databases')
name = psdb.where('id=?', (uid,)).getField('name')
data = psdb.field('id,name,accept').where('id=?', (uid,)).find()
result = pdb.execute("update mysql.user set Password=password('" +
newpassword + "') where User='" + username + "'")
cmd = "SET PASSWORD FOR '" + username + \
"'@'localhost' = PASSWORD('" + newpassword + "')"
r = pdb.execute(cmd)
# print(cmd, r)
accept = data['accept']
alist = accept.split(',')
for x in alist:
cmd = "SET PASSWORD FOR '" + username + \
"'@'" + x + "' = PASSWORD('" + newpassword + "')"
r = pdb.execute(cmd)
# print(cmd, r)
pdb.execute("flush privileges")
psdb.where("id=?", (uid,)).setField('password', newpassword)
return mw.returnJson(True, mw.getInfo('修改数据库[{1}]密码成功!', (name,)))
orm = pMysqlDb()
orm.execute("flush privileges")
return mw.returnJson(True, mw.getInfo('修改数据库[{1}]密码成功!', (data['name'],)))
except Exception as ex:
return mw.returnJson(False, mw.getInfo('修改数据库[{1}]密码失败[{2}]!', (name, str(ex),)))
return mw.returnJson(False, mw.getInfo('修改数据库[{1}]密码失败[{2}]!', (data['name'], str(ex),)))
def setDbPs():
@ -1031,7 +1047,7 @@ def delDb():
# 删除MYSQL
result = pdb.execute("drop database `" + name + "`")
users = pdb.query("select Host from mysql.user where User='" +
users = pdb.query("select Host from user where User='" +
username + "' AND Host!='localhost'")
pdb.execute("drop user '" + username + "'@'localhost'")
for us in users:
@ -1051,9 +1067,9 @@ def getDbAccess():
if not data[0]:
return data[1]
username = args['username']
pdb = pMysqlDb()
pdb = pMysqlDb('mysql')
users = pdb.query("select Host from mysql.user where User='" +
users = pdb.query("select Host from user where User='" +
username + "' AND Host!='localhost'")
if len(users) < 1:
@ -1083,7 +1099,7 @@ def setDbAccess():
else:
password = psdb.where("username=?", (name,)).getField('password')
users = pdb.query("select Host from mysql.user where User='" +
users = pdb.query("select Host from user where User='" +
name + "' AND Host!='localhost'")
for us in users:
@ -1105,11 +1121,11 @@ def setDbRw(version=''):
uid = args['id']
rw = args['rw']
pdb = pMysqlDb()
pdb = pMysqlDb('mysql')
psdb = pSqliteDb('databases')
dbname = psdb.where("id=?", (uid,)).getField('name')
users = pdb.query(
"select Host from mysql.user where User='" + username + "'")
"select Host from user where User='" + username + "'")
# show grants for demo@"127.0.0.1";
for x in users:
@ -1374,7 +1390,7 @@ def setDbMasterAccess():
pdb = pMysqlDb()
psdb = pSqliteDb('master_replication_user')
password = psdb.where("username=?", (username,)).getField('password')
users = pdb.query("select Host from mysql.user where User='" +
users = pdb.query("select Host from user where User='" +
username + "' AND Host!='localhost'")
for us in users:
pdb.execute("drop user '" + username + "'@'" + us["Host"] + "'")
@ -1706,7 +1722,7 @@ def delMasterRepSlaveUser(version=''):
pdb.execute("drop user '" + name + "'@'%'")
pdb.execute("drop user '" + name + "'@'localhost'")
users = pdb.query("select Host from mysql.user where User='" +
users = pdb.query("select Host from user where User='" +
name + "' AND Host!='localhost'")
for us in users:
pdb.execute("drop user '" + name + "'@'" + us["Host"] + "'")

@ -809,7 +809,14 @@ function openPhpmyadmin(name,username,password){
layer.msg('phpMyAdmin未启动',{icon:2,shade: [0.3, '#000']});
return;
}
// console.log(data);
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data);
if (rdata.choose != 'mariadb'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
return;
}
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_home_page'});
var rdata = $.parseJSON(data.data);
if (!rdata.status){

@ -809,6 +809,14 @@ function openPhpmyadmin(name,username,password){
layer.msg('phpMyAdmin未启动',{icon:2,shade: [0.3, '#000']});
return;
}
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data);
if (rdata.choose == 'mariadb'){
layer.msg('当前为[mariadb]模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
return;
}
// console.log(data);
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_home_page'});
var rdata = $.parseJSON(data.data);

@ -12,6 +12,37 @@ config = C:read_file_body_decode(cpath .. 'config.json')
local site_config = C:read_file_body_decode(cpath .. 'site.json')
C:setConfData(config, site_config)
-- D func
local function D(msg)
local _msg = ''
if type(msg) == 'table' then
for key, val in pairs(msg) do
_msg = key..':'..val.."\n"
end
elseif type(msg) == 'string' then
_msg = msg
elseif type(msg) == 'nil' then
_msg = 'nil'
else
_msg = msg
end
if not debug_mode then return true end
local fp = io.open(cpath..'debug.log', 'ab')
if fp == nil then
return nil
end
local localtime = os.date("%Y-%m-%d %H:%M:%S")
if server_name then
fp:write(tostring(_msg) .. "\n")
else
fp:write(localtime..":"..tostring(_msg) .. "\n")
end
fp:flush()
fp:close()
return true
end
function initParams()
local data = {}
data['ip'] = C:get_client_ip()
@ -314,6 +345,7 @@ end
function post_data_chekc()
if params['method'] =="POST" then
if C:return_post_data() then return false end
ngx.req.read_body()
request_args = ngx.req.get_post_args()
if not request_args then return false end

@ -2,10 +2,11 @@
$cfg['blowfish_secret'] = '{$BLOWFISH_SECRET}';
$i = 0;
$i++;
$cfg['Servers'][$i]['verbose'] = '{$CHOOSE_DB}';
$cfg['Servers'][$i]['auth_type'] = 'cookie';
// $cfg['Servers'][$i]['host'] = '127.0.0.1';
// $cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['socket'] = '{$SERVER_PATH}/mysql/mysql.sock';
$cfg['Servers'][$i]['socket'] = '{$SERVER_PATH}/{$CHOOSE_DB_DIR}/mysql.sock';
$cfg['Servers'][$i]['connect_type'] = 'socket';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;

@ -8,6 +8,11 @@ server
#error_page 404 /404.html;
include {$PHP_CONF_PATH}/enable-php-{$PHP_VER}.conf;
#AUTH_START
auth_basic "Authorization";
auth_basic_user_file {$SERVER_PATH}/phpmyadmin/pma.pass;
#AUTH_END
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
@ -18,7 +23,7 @@ server
expires 12h;
}
location ~ /.*\.log$ {
location ~ /.*\.(log|pass|json|pl)$ {
deny all;
}

@ -5,6 +5,7 @@ import io
import os
import time
import re
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
@ -44,6 +45,13 @@ def getArgs():
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():
return mw.getServerDir() + '/web_conf/nginx/vhost/phpmyadmin.conf'
@ -107,9 +115,50 @@ def contentReplace(content):
content = content.replace('{$PHP_CONF_PATH}', php_conf_dir)
content = content.replace('{$PHP_VER}', php_ver)
content = content.replace('{$BLOWFISH_SECRET}', blowfish_secret)
cfg = getCfg()
if (cfg['choose'] == ""):
content = content.replace('{$CHOOSE_DB}', 'mysql')
content = content.replace('{$CHOOSE_DB_DIR}', 'mysql')
else:
content = content.replace('{$CHOOSE_DB}', 'MariaDB')
content = content.replace('{$CHOOSE_DB_DIR}', 'mariadb')
return content
def initCfg():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
data = {}
data['port'] = '888'
data['choose'] = ''
data['username'] = 'admin'
data['password'] = 'admin'
mw.writeFile(cfg, json.dumps(data))
def setCfg(key, val):
cfg = getServerDir() + "/cfg.json"
data = mw.readFile(cfg)
data = json.loads(data)
data[key] = val
mw.writeFile(cfg, json.dumps(data))
def getCfg():
cfg = getServerDir() + "/cfg.json"
data = mw.readFile(cfg)
data = json.loads(data)
return data
def returnCfg():
cfg = getServerDir() + "/cfg.json"
data = mw.readFile(cfg)
return data
def status():
conf = getConf()
conf_inc = getServerDir() + '/phpmyadmin/config.inc.php'
@ -120,6 +169,8 @@ def status():
def start():
initCfg()
file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
file_run = getConf()
@ -128,6 +179,14 @@ def start():
centent = contentReplace(centent)
mw.writeFile(file_run, centent)
pma_path = getServerDir() + '/pma.pass'
if not os.path.exists(pma_path):
username = mw.getRandomString(10)
pass_cmd = username + ':' + mw.hasPwd(username)
setCfg('username', username)
setCfg('password', username)
mw.writeFile(pma_path, pass_cmd)
tmp = getServerDir() + '/phpmyadmin/tmp'
if not os.path.exists(tmp):
os.mkdir(tmp)
@ -179,12 +238,11 @@ def setPhpVer():
file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
file_run = getConf()
centent = mw.readFile(file_tpl)
centent = contentReplace(centent)
mw.writeFile(file_run, centent)
content = mw.readFile(file_tpl)
content = contentReplace(content)
mw.writeFile(file_run, content)
mw.restartWeb()
return 'ok'
@ -195,6 +253,11 @@ def getSetPhpVer():
return ''
def getPmaOption():
data = getCfg()
return mw.returnJson(True, 'ok', data)
def getPmaPort():
try:
port = getPort()
@ -206,8 +269,9 @@ def getPmaPort():
def setPmaPort():
args = getArgs()
if not 'port' in args:
return mw.returnJson(False, 'port missing!')
data = checkArgs(args, ['port'])
if not data[0]:
return data[1]
port = args['port']
if port == '80':
@ -224,6 +288,63 @@ def setPmaPort():
return mw.returnJson(True, '修改成功!')
def setPmaChoose():
args = getArgs()
data = checkArgs(args, ['choose'])
if not data[0]:
return data[1]
choose = args['choose']
setCfg('choose', choose)
conf_run = getServerDir() + '/phpmyadmin/config.inc.php'
conf_tpl = getPluginDir() + '/conf/config.inc.php'
centent = mw.readFile(conf_tpl)
centent = contentReplace(centent)
mw.writeFile(conf_run, centent)
mw.restartWeb()
return mw.returnJson(True, '修改成功!')
def setPmaUsername():
args = getArgs()
data = checkArgs(args, ['username'])
if not data[0]:
return data[1]
username = args['username']
setCfg('username', username)
cfg = getCfg()
pma_path = getServerDir() + '/pma.pass'
username = mw.getRandomString(10)
pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
mw.writeFile(pma_path, pass_cmd)
mw.restartWeb()
return mw.returnJson(True, '修改成功!')
def setPmaPassword():
args = getArgs()
data = checkArgs(args, ['password'])
if not data[0]:
return data[1]
password = args['password']
setCfg('password', password)
cfg = getCfg()
pma_path = getServerDir() + '/pma.pass'
username = mw.getRandomString(10)
pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
mw.writeFile(pma_path, pass_cmd)
mw.restartWeb()
return mw.returnJson(True, '修改成功!')
def accessLog():
return getServerDir() + '/access.log'
@ -252,6 +373,8 @@ if __name__ == "__main__":
print(getConf())
elif func == 'version':
print(Version())
elif func == 'get_cfg':
print(returnCfg())
elif func == 'config_inc':
print(getConfInc())
elif func == 'get_home_page':
@ -264,6 +387,14 @@ if __name__ == "__main__":
print(getPmaPort())
elif func == 'set_pma_port':
print(setPmaPort())
elif func == 'get_pma_option':
print(getPmaOption())
elif func == 'set_pma_choose':
print(setPmaChoose())
elif func == 'set_pma_username':
print(setPmaUsername())
elif func == 'set_pma_password':
print(setPmaPassword())
elif func == 'access_log':
print(accessLog())
elif func == 'error_log':

@ -90,24 +90,68 @@ function phpVerChange(type, msg) {
//phpmyadmin安全设置
function safeConf() {
var data = pmaAsyncPost('get_pma_port');
var rdata = $.parseJSON(data.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
return;
}
var con = '<div class="ver line">\
<span style="margin-right:10px">访问端口</span>\
<input class="bt-input-text phpmyadmindk mr20" name="Name" id="pmport" value="' + rdata['data'] + '" placeholder="phpmyadmin访问端口" maxlength="5" type="number">\
<button class="btn btn-success btn-sm" onclick="phpmyadminPort()">保存</button>\
pmaPost('get_pma_option', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
return;
}
var cfg = rdata.data;
var con = '<div class="ver line">\
<span class="tname">访问端口</span>\
<input style="width:110px" class="bt-input-text phpmyadmindk mr20" name="Name" id="pmport" value="' + cfg['port'] + '" placeholder="phpmyadmin访问端口" maxlength="5" type="number">\
<button class="btn btn-success btn-sm" onclick="setPamPort()">保存</button>\
</div>\
<div class="ver line">\
<span class="tname">访问切换</span>\
<select id="access_choose" class="bt-input-text mr20" name="choose" style="width:110px">\
<option value="mariadb" '+(cfg['choose']=="mariadb"?"selected='selected'":"")+'>MariaDB</option>\
<option value="mysql" '+ (cfg['choose']==""?"selected='selected'":"")+'>MySQL</option>\
</select>\
<button class="btn btn-success btn-sm" onclick="setPmaChoose()">保存</button>\
</div>\
<div class="ver line">\
<span class="tname">用户名</span>\
<input style="width:110px" class="bt-input-text mr20" name="username" id="pmport" value="' + cfg['username'] + '" placeholder="认证用户名" type="text">\
<button class="btn btn-success btn-sm" onclick="setPmaUsername()">保存</button>\
</div>\
<div class="ver line">\
<span class="tname">密码</span>\
<input style="width:110px" class="bt-input-text mr20" name="password" id="pmport" value="' + cfg['password'] + '" placeholder="密码" type="text">\
<button class="btn btn-success btn-sm" onclick="setPmaPassword()">保存</button>\
</div>';
$(".soft-man-con").html(con);
$(".soft-man-con").html(con);
});
}
function setPmaChoose(){
var choose = $("#access_choose").val();
pmaPost('set_pma_choose',{'choose':choose}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
}
function setPmaUsername(){
var username = $("input[name=username]").val();
pmaPost('set_pma_username',{'username':username}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
}
function setPmaPassword(){
var password = $("input[name=password]").val();
pmaPost('set_pma_password',{'password':password}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
}
//修改phpmyadmin端口
function phpmyadminPort() {
function setPamPort() {
var pmport = $("#pmport").val();
if (pmport < 80 || pmport > 65535) {
layer.msg('端口范围不合法!', { icon: 2 });

@ -4,8 +4,8 @@
"name": "redis",
"title": "Redis",
"shell": "install.sh",
"versions":["4.0.11","5.0.8","6.0.4","6.2.7"],
"updates":["4.0.11","5.0.8","6.0.4","6.2.7"],
"versions":["4.0.11","5.0.8","6.0.4","6.2.7","7.0.4"],
"updates":["4.0.11","5.0.8","6.0.4","6.2.7","7.0.4"],
"tip": "soft",
"checks": "server/redis",
"path": "server/redis",

@ -17,6 +17,7 @@ if grep -Eq "Debian" /etc/*-release; then
fi
apt update -y
apt-get update -y
apt install -y wget curl lsof unzip

@ -10,6 +10,7 @@ fi
apt update -y
apt-get update -y
apt install -y wget curl lsof unzip
apt install -y python3-pip

Loading…
Cancel
Save