diff --git a/.gitignore b/.gitignore
index a18bf53ac..21c659353 100644
--- a/.gitignore
+++ b/.gitignore
@@ -188,7 +188,6 @@ plugins/tidb
plugins/goedge-admin
plugins/goedge-node
plugins/goedge-happy
-plugins/doh
/logs
/data
diff --git a/plugins/doh/config/config.toml b/plugins/doh/config/config.toml
new file mode 100644
index 000000000..86d02101d
--- /dev/null
+++ b/plugins/doh/config/config.toml
@@ -0,0 +1,48 @@
+[general]
+# 监听地址和端口
+listen_addr = "0.0.0.0:8080"
+
+# DoH 路径
+path = "/dns-query"
+
+# TLS 配置(如果需要 HTTPS)
+# tls_cert_path = "/etc/doh-proxy/cert.pem"
+# tls_key_path = "/etc/doh-proxy/key.pem"
+
+# 并发请求限制
+max_concurrent_requests = 512
+
+# 日志文件
+log_file = "{$SERVER_PATH}/doh-proxy/doh-proxy.log"
+
+[upstream]
+# 上游 DNS 服务器
+upstream_addr = "1.1.1.1:443"
+bootstrap_addr = "1.1.1.1:53"
+
+# 上游 DoH 服务器 URL(如果使用标准的 DoH 端点)
+# upstream_url = "https://cloudflare-dns.com/dns-query"
+
+# 超时设置
+timeout = 10
+
+# 重试次数
+tries = 3
+
+# 启用 TCP 保活
+tcp_keepalive = 30
+
+[cache]
+# 缓存设置
+max_entries = 65536
+min_ttl = 60
+max_ttl = 3600
+
+[network]
+# 网络设置
+tcp_fastopen = true
+reuse_port = true
+
+[log]
+# 日志级别:debug, info, warn, error
+level = "info"
\ No newline at end of file
diff --git a/plugins/doh/ico.png b/plugins/doh/ico.png
new file mode 100644
index 000000000..2260f976a
Binary files /dev/null and b/plugins/doh/ico.png differ
diff --git a/plugins/doh/index.html b/plugins/doh/index.html
new file mode 100755
index 000000000..491120e44
--- /dev/null
+++ b/plugins/doh/index.html
@@ -0,0 +1,28 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/doh/index.py b/plugins/doh/index.py
new file mode 100755
index 000000000..10e915fd8
--- /dev/null
+++ b/plugins/doh/index.py
@@ -0,0 +1,1154 @@
+# coding: utf-8
+
+
+import time
+import os
+import sys
+import re
+import subprocess
+
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'doh'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':', 1)
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':', 1)
+ 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 getInitdConfTpl():
+ path = getPluginDir() + "/init.d/gitea.tpl"
+ return path
+
+
+def getInitdConf():
+ path = getServerDir() + "/init.d/doh"
+ return path
+
+
+def getConf():
+ path = getServerDir() + "/custom/conf/app.ini"
+
+ if not os.path.exists(path):
+ return mw.returnJson(False, "请先安装初始化!
默认地址:http://" + mw.getLocalIp() + ":3000")
+ return path
+
+
+def getConfTpl():
+ path = getPluginDir() + "/conf/app.ini"
+ return path
+
+
+def status():
+ data = mw.execShell(
+ "ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def getHomeDir():
+ if mw.isAppleSystem():
+ user = mw.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return '/Users/' + user
+ else:
+ return 'www'
+
+
+def getRunUser():
+ if mw.isAppleSystem():
+ user = mw.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return user
+ else:
+ return 'www'
+
+__SR = '''#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+export USER=%s
+export HOME=%s && ''' % ( getRunUser(), getHomeDir())
+
+
+def contentReplace(content):
+
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$RUN_USER}', getRunUser())
+ content = content.replace('{$HOME_DIR}', getHomeDir())
+
+ return content
+
+
+def initDreplace():
+
+ file_tpl = getInitdConfTpl()
+ service_path = mw.getServerDir()
+
+ git_dir = mw.getServerDir() + '/git'
+ if not os.path.exists(git_dir):
+ mw.execShell('mkdir -p ' + git_dir)
+ mw.execShell('chown -R www:www ' + git_dir)
+
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/doh.service'
+ systemServiceTpl = getPluginDir() + '/init.d/doh.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ 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')
+
+ log_path = getServerDir() + '/log'
+ if not os.path.exists(log_path):
+ os.mkdir(log_path)
+
+ return file_bin
+
+
+def getRootUrl():
+ content = mw.readFile(getConf())
+ rep = r'ROOT_URL\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ if tmp:
+ return tmp.groups()[0]
+
+ rep = r'EXTERNAL_URL\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ if tmp:
+ return tmp.groups()[0]
+ return ''
+
+
+def getSshPort():
+ content = mw.readFile(getConf())
+ rep = r'SSH_PORT\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ if not tmp:
+ return ''
+ return tmp.groups()[0]
+
+
+def getHttpPort():
+ content = mw.readFile(getConf())
+ rep = r'HTTP_PORT\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ if not tmp:
+ return ''
+ return tmp.groups()[0]
+
+
+def getRootPath():
+ content = mw.readFile(getConf())
+ rep = r'ROOT\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ if not tmp:
+ return ''
+ return tmp.groups()[0]
+
+
+def getDbConfValue():
+ conf = getConf()
+ if not os.path.exists(conf):
+ return {}
+
+ content = mw.readFile(conf)
+ rep_scope = r"\[database\](.*?)\["
+ tmp = re.findall(rep_scope, content, re.S)
+
+ rep = '(\\w*)\\s*=\\s*(.*)'
+ tmp = re.findall(rep, tmp[0])
+ r = {}
+ for x in range(len(tmp)):
+ k = tmp[x][0]
+ v = tmp[x][1]
+ r[k] = v
+ return r
+
+
+def pMysqlDb(conf):
+ host = conf['HOST'].split(':')
+ # pymysql
+ db = mw.getMyORM()
+ # MySQLdb |
+ # db = mw.getMyORMDb()
+
+ db.setPort(int(host[1]))
+ db.setUser(conf['USER'])
+
+ if 'PASSWD' in conf:
+ db.setPwd(conf['PASSWD'])
+ else:
+ db.setPwd(conf['PASSWORD'])
+
+ db.setDbName(conf['NAME'])
+ # db.setSocket(getSocketFile())
+ db.setCharset("utf8")
+ return db
+
+
+def pSqliteDb(conf):
+ # print(conf)
+ import db
+ psDb = db.Sql()
+
+ # 默认
+ gsdir = getServerDir() + '/data'
+ dbname = 'gitea'
+ if conf['PATH'][0] == '/':
+ # 绝对路径
+ pass
+ else:
+ path = conf['PATH'].split('/')
+ gsdir = getServerDir() + '/' + path[0]
+ dbname = path[1].split('.')[0]
+
+ # print(gsdir, dbname)
+ psDb.dbPos(gsdir, dbname)
+ return psDb
+
+
+def getGiteaDbType(conf):
+
+ if 'DB_TYPE' in conf:
+ return conf['DB_TYPE']
+
+ if 'TYPE' in conf:
+ return conf['TYPE']
+
+ return 'NONE'
+
+
+def pQuery(sql):
+ conf = getDbConfValue()
+ gtype = getGiteaDbType(conf)
+ if gtype == 'sqlite3':
+ db = pSqliteDb(conf)
+ data = db.query(sql, []).fetchall()
+ return data
+ elif gtype == 'mysql':
+ db = pMysqlDb(conf)
+ return db.query(sql)
+
+ print("仅支持mysql|sqlite3配置")
+ exit(0)
+
+
+def isSqlError(mysqlMsg):
+ # 检测数据库执行错误
+ _mysqlMsg = str(mysqlMsg)
+ # print _mysqlMsg
+ if "MySQLdb" in _mysqlMsg:
+ return mw.returnData(False, 'MySQLdb组件缺失!
进入SSH命令行输入: pip install mysql-python')
+ if "2002," in _mysqlMsg:
+ return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
+ if "using password:" in _mysqlMsg:
+ return mw.returnData(False, '数据库管理密码错误!')
+ if "Connection refused" in _mysqlMsg:
+ return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
+ if "1133," in _mysqlMsg:
+ return mw.returnData(False, '数据库用户不存在!')
+ if "1007," in _mysqlMsg:
+ return mw.returnData(False, '数据库已经存在!')
+ if "1044," in _mysqlMsg:
+ return mw.returnData(False, mysqlMsg[1])
+ if "2003," in _mysqlMsg:
+ return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
+ return mw.returnData(True, 'OK')
+
+
+def appOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ data = mw.execShell(__SR + file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[0]
+
+
+def start():
+ return appOp('start')
+
+
+def stop():
+ return appOp('stop')
+
+
+def restart():
+ return appOp('restart')
+
+
+def reload():
+ return appOp('reload')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status ' + getPluginName() + ' | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def runLog():
+ log_path = getServerDir() + '/log/gitea.log'
+ return log_path
+
+
+def postReceiveLog():
+ log_path = getServerDir() + '/log/hooks/post-receive.log'
+ return log_path
+
+
+def getGogsConf():
+ conf = getConf()
+ if not os.path.exists(conf):
+ return mw.returnJson(False, "请先安装初始化!
默认地址:http://" + mw.getLocalIp() + ":3000")
+
+ gets = [
+ {'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
+ {'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
+ {'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
+ {'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
+
+ {'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
+ {'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
+
+ {'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
+ {'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
+ {'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
+ {'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
+
+ {'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
+
+ {'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
+ {'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
+ {'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
+ ]
+ conf = mw.readFile(conf)
+ result = []
+
+ for g in gets:
+ rep = g['name'] + '\\s*=\\s*(.*)'
+ tmp = re.search(rep, conf)
+ if not tmp:
+ continue
+ g['value'] = tmp.groups()[0]
+ result.append(g)
+ return mw.returnJson(True, 'OK', result)
+
+
+def submitGogsConf():
+ gets = ['DOMAIN',
+ 'ROOT_URL',
+ 'HTTP_ADDR',
+ 'HTTP_PORT',
+ 'START_SSH_SERVER',
+ 'SSH_PORT',
+ 'REQUIRE_SIGNIN_VIEW',
+ 'FORCE_PRIVATE',
+ 'ENABLE_CAPTCHA',
+ 'DISABLE_REGISTRATION',
+ 'ENABLE_NOTIFY_MAIL',
+ 'SHOW_FOOTER_BRANDING',
+ 'SHOW_FOOTER_VERSION',
+ 'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
+ args = getArgs()
+ filename = getConf()
+ conf = mw.readFile(filename)
+ for g in gets:
+ if g in args:
+ rep = g + '\\s*=\\s*(.*)'
+ val = g + ' = ' + args[g]
+ conf = re.sub(rep, val, conf)
+ mw.writeFile(filename, conf)
+ restart()
+ return mw.returnJson(True, '设置成功')
+
+
+def gogsEditTpl():
+ data = {}
+ data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
+ data['commit'] = getPluginDir() + '/hook/commit.tpl'
+ return mw.getJson(data)
+
+
+def userList():
+
+ conf = getConf()
+ if not os.path.exists(conf):
+ return mw.returnJson(False, "请先安装初始化!
默认地址:http://" + mw.getLocalIp() + ":3000")
+
+ conf = getDbConfValue()
+ gtype = getGiteaDbType(conf)
+ if gtype != 'mysql':
+ return mw.returnJson(False, "仅支持mysql数据操作!")
+
+ import math
+ args = getArgs()
+
+ data = checkArgs(args, ['page', 'page_size'])
+ if not data[0]:
+ return data[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ search = ''
+ if 'search' in args:
+ search = args['search']
+
+ user_where1 = ''
+ user_where2 = ''
+ if search != '':
+ user_where1 = ' where name like "%' + search + '%"'
+ user_where2 = ' where name like "%' + search + '%"'
+
+ data = {}
+
+ data['root_url'] = getRootUrl()
+
+ start = (page - 1) * page_size
+ list_count = pQuery('select count(id) as num from user' + user_where1)
+ count = list_count[0]["num"]
+ list_data = pQuery(
+ 'select id,name,email from user ' + user_where2 + ' order by id desc limit ' + str(start) + ',' + str(page_size))
+ data['list'] = mw.getPage({'count': count, 'p': page,
+ 'row': page_size, 'tojs': 'gogsUserList'})
+ data['page'] = page
+ data['page_size'] = page_size
+ data['page_count'] = int(math.ceil(count / page_size))
+ data['data'] = list_data
+ return mw.returnJson(True, 'OK', data)
+
+
+def checkRepoListIsHasScript(data):
+ path = getRootPath()
+ for x in range(len(data)):
+ name = data[x]['name'] + '/' + data[x]['repo'] + '.git'
+ path_tmp = path + '/' + name + '/custom_hooks/commit'
+ if os.path.exists(path_tmp):
+ data[x]['has_hook'] = True
+ else:
+ data[x]['has_hook'] = False
+ return data
+
+
+def repoList():
+
+ conf = getConf()
+ if not os.path.exists(conf):
+ return mw.returnJson(False, "请先安装初始化!
默认地址:http://" + mw.getLocalIp() + ":3000")
+
+ conf = getDbConfValue()
+ gtype = getGiteaDbType(conf)
+ if gtype != 'mysql':
+ return mw.returnJson(False, "仅支持mysql数据操作!")
+
+ import math
+ args = getArgs()
+
+ data = checkArgs(args, ['page', 'page_size'])
+ if not data[0]:
+ return data[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ search = ''
+ if 'search' in args:
+ search = args['search']
+
+ data = {}
+
+ data['root_url'] = getRootUrl()
+
+ repo_where1 = ''
+ repo_where2 = ''
+ if search != '':
+ repo_where1 = ' where name like "%' + search + '%"'
+ repo_where2 = ' where r.name like "%' + search + '%"'
+
+ start = (page - 1) * page_size
+ list_count = pQuery(
+ 'select count(id) as num from repository' + repo_where1)
+ count = list_count[0]["num"]
+ sql = 'select r.id,r.owner_id,r.name as repo, u.name from repository r left join user u on r.owner_id=u.id ' + repo_where2 + ' order by r.id desc limit ' + \
+ str(start) + ',' + str(page_size)
+ # print(sql)
+ list_data = pQuery(sql)
+ # print(list_data)
+ list_data = checkRepoListIsHasScript(list_data)
+
+ data['list'] = mw.getPage({'count': count, 'p': page,
+ 'row': page_size, 'tojs': 'gogsRepoListPage'})
+ data['page'] = page
+ data['page_size'] = page_size
+ data['page_count'] = int(math.ceil(count / page_size))
+ data['data'] = list_data
+ return mw.returnJson(True, 'OK', data)
+
+
+def getAllUserProject(user, search=''):
+ path = getRootPath() + '/' + user
+ dlist = []
+ if os.path.exists(path):
+ for filename in os.listdir(path):
+ tmp = {}
+ filePath = path + '/' + filename
+ if os.path.isdir(filePath):
+ if search == '':
+ tmp['name'] = filename.replace('.git', '')
+ dlist.append(tmp)
+ else:
+ if filename.find(search) != -1:
+ tmp['name'] = filename.replace('.git', '')
+ dlist.append(tmp)
+ return dlist
+
+
+def checkProjectListIsHasScript(user, data):
+ path = getRootPath() + '/' + user
+ for x in range(len(data)):
+ name = data[x]['name'] + '.git'
+ path_tmp = path + '/' + name + '/hooks/post-receive.d/post-receive'
+ if os.path.exists(path_tmp):
+ data[x]['has_hook'] = True
+ else:
+ data[x]['has_hook'] = False
+ return data
+
+
+def userProjectList():
+ import math
+ args = getArgs()
+ # print args
+
+ page = 1
+ page_size = 5
+ search = ''
+
+ if not 'name' in args:
+ return mw.returnJson(False, '缺少参数name')
+ if 'page' in args:
+ page = int(args['page'])
+
+ if 'page_size' in args:
+ page_size = int(args['page_size'])
+
+ if 'search' in args:
+ search = args['search']
+
+ data = {}
+
+ ulist = getAllUserProject(args['name'])
+ dlist_sum = len(ulist)
+
+ start = (page - 1) * page_size
+ ret_data = ulist[start:start + page_size]
+ ret_data = checkProjectListIsHasScript(args['name'], ret_data)
+
+ data['root_url'] = getRootUrl()
+ data['data'] = ret_data
+ data['args'] = args
+ data['list'] = mw.getPage(
+ {'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectListPost'})
+
+ return mw.returnJson(True, 'OK', data)
+
+
+def projectScriptEdit():
+ args = getArgs()
+
+ if not 'user' in args:
+ return mw.returnJson(True, 'username missing')
+
+ if not 'name' in args:
+ return mw.returnJson(True, 'project name missing')
+
+ user = args['user']
+ name = args['name'] + '.git'
+ post_receive = getRootPath() + '/' + user + '/' + name + \
+ '/custom_hooks/commit'
+ if os.path.exists(post_receive):
+ return mw.returnJson(True, 'OK', {'path': post_receive})
+ else:
+ return mw.returnJson(False, 'file does not exist')
+
+
+def projectScriptLoad():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+
+ path = getRootPath() + '/' + user + '/' + name
+ post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
+ post_receive = path + '/hooks/post-receive.d/post-receive'
+
+ if not os.path.exists(path + '/custom_hooks'):
+ mw.execShell('mkdir -p ' + path + '/custom_hooks')
+ mw.execShell('chown -R www:www ' + path + '/custom_hooks')
+
+ pct_content = mw.readFile(post_receive_tpl)
+ pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
+ mw.writeFile(post_receive, pct_content)
+ mw.execShell('chmod 777 ' + post_receive)
+ mw.execShell('chown -R www:www ' + post_receive)
+
+ commit_tpl = getPluginDir() + '/hook/commit.tpl'
+ commit = path + '/custom_hooks/commit'
+
+ codeDir = mw.getFatherDir() + '/git'
+
+ cc_content = mw.readFile(commit_tpl)
+
+ gitPath = getRootPath()
+ cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
+ cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
+ cc_content = cc_content.replace('{$USERNAME}', user)
+ cc_content = cc_content.replace('{$PROJECT}', args['name'])
+ cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
+ mw.writeFile(commit, cc_content)
+ mw.execShell('chmod 777 ' + commit)
+ mw.execShell('chown -R www:www ' + commit)
+
+ return 'ok'
+
+
+def projectScriptUnload():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+
+ post_receive = getRootPath() + '/' + user + '/' + name + \
+ '/hooks/post-receive.d/post-receive'
+ mw.execShell('rm -f ' + post_receive)
+
+ commit = getRootPath() + '/' + user + '/' + name + \
+ '/custom_hooks/commit'
+ mw.execShell('rm -f ' + commit)
+ return 'ok'
+
+
+def projectScriptDebug():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ commit_log = getRootPath() + '/' + user + '/' + name + \
+ '/custom_hooks/sh.log'
+
+ data = {}
+ if os.path.exists(commit_log):
+ data['status'] = True
+ data['path'] = commit_log
+ else:
+ data['status'] = False
+ data['msg'] = '没有日志文件'
+
+ return mw.getJson(data)
+
+
+def projectScriptRun():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+
+ path = getRootPath() + '/' + user + '/' + name
+ commit_sh = path + '/custom_hooks/commit'
+ commit_log = path + '/custom_hooks/sh.log'
+ script_run = 'sh -x ' + commit_sh + ' 2>' + commit_log
+
+ if not os.path.exists(commit_sh):
+ return mw.returnJson(False, '脚本文件不存在!')
+
+ repo_dir = mw.getServerDir()+'/git/'+ args['name']
+
+ # mw.execShell(script_run)
+ subprocess.Popen(script_run, stdout=subprocess.PIPE, shell=True,
+ bufsize=4096, stderr=subprocess.PIPE)
+ subprocess.Popen('chown -R www:www ' + repo_dir, stdout=subprocess.PIPE, shell=True,
+ bufsize=4096, stderr=subprocess.PIPE)
+ return mw.returnJson(True, '脚本文件执行成功,观察日志!')
+
+
+def projectScriptSelf():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+
+ custom_hooks = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks'
+
+ self_path = custom_hooks + '/self'
+ if not os.path.exists(self_path):
+ os.mkdir(self_path)
+ mw.execShell("chown -R www:www " + self_path)
+
+ self_logs_path = custom_hooks + '/self_logs'
+ if not os.path.exists(self_logs_path):
+ os.mkdir(self_logs_path)
+ mw.execShell("chown -R www:www " + self_logs_path)
+
+ self_hook_file = custom_hooks + '/self_hook.sh'
+ self_hook_exist = False
+ if os.path.exists(self_hook_file):
+ self_hook_exist = True
+
+ dlist = []
+ if os.path.exists(self_path):
+ for filename in os.listdir(self_path):
+ tmp = {}
+ filePath = self_path + '/' + filename
+ if os.path.isfile(filePath):
+ tmp['path'] = filePath
+ tmp['name'] = os.path.basename(filePath)
+ tmp['is_hidden'] = False
+ if tmp['name'].endswith('.txt'):
+ tmp['is_hidden'] = True
+
+ dlist.append(tmp)
+
+ dlist_sum = len(dlist)
+ # print(dlist)
+ rdata = {}
+ rdata['data'] = dlist
+ rdata['self_hook'] = self_hook_exist
+ rdata['list'] = mw.getPage(
+ {'count': dlist_sum, 'p': 1, 'row': 100, 'tojs': 'self_page'})
+
+ return mw.returnJson(True, 'ok', rdata)
+
+
+def projectScriptSelf_Create():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'file'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ file = args['file']
+
+ self_path = path = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks/self'
+
+ if not os.path.exists(self_path):
+ os.mkdir(self_path)
+
+ abs_file = self_path + '/' + file + '.sh'
+ if os.path.exists(abs_file):
+ return mw.returnJson(False, '脚本已经存在!')
+
+ mw.writeFile(abs_file, "#!/bin/bash\necho `date +'%Y-%m-%d %H:%M:%S'`\n")
+ mw.execShell('chown -R www:www ' + abs_file)
+ rdata = {}
+ rdata['abs_file'] = abs_file
+ return mw.returnJson(True, '创建文件成功!', rdata)
+
+
+def projectScriptSelf_Del():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'file'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ file = args['file']
+
+ custom_hooks = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks'
+ self_path = custom_hooks + '/self'
+
+ if not os.path.exists(self_path):
+ os.mkdir(self_path)
+
+ abs_file = self_path + '/' + file
+ # print(abs_file)
+ if not os.path.exists(abs_file):
+ return mw.returnJson(False, '脚本已经删除!')
+
+ os.remove(abs_file)
+
+ # 日志也删除
+ log_file = custom_hooks + '/self_logs/' + file + '.log'
+ if os.path.exists(log_file):
+ os.remove(log_file)
+
+ return mw.returnJson(True, '脚本删除成功!')
+
+
+def projectScriptSelf_Logs():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'file'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ file = args['file']
+
+ self_path = path = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks/self_logs'
+
+ if not os.path.exists(self_path):
+ os.mkdir(self_path)
+
+ logs_file = self_path + '/' + file + '.log'
+ if os.path.exists(logs_file):
+ rdata = {}
+ rdata['path'] = logs_file
+ return mw.returnJson(True, 'ok', rdata)
+
+ return mw.returnJson(False, '日志不存在!')
+
+
+def projectScriptSelf_Run():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'file'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ file = args['file']
+
+ custom_hooks = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks'
+ self_path = custom_hooks + '/self/' + file
+ self_logs_path = custom_hooks + '/self_logs/' + file + '.log'
+
+ shell = "sh -x " + self_path + " 2>" + self_logs_path + ' &'
+ mw.execShell(shell)
+ mw.execShell("chown -R www:www " + self_logs_path)
+ return mw.returnJson(True, '执行成功!')
+
+
+def projectScriptSelf_Rename():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'o_file', 'n_file'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ o_file = args['o_file']
+ n_file = args['n_file']
+
+ custom_hooks = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks'
+ self_path = custom_hooks + '/self'
+
+ if not os.path.exists(self_path):
+ os.mkdir(self_path)
+
+ o_file_abs = self_path + '/' + o_file + '.sh'
+ if not os.path.exists(o_file_abs):
+ return mw.returnJson(False, '原文件已经不存在了!')
+
+ n_file_abs = self_path + '/' + n_file + '.sh'
+
+ os.rename(o_file_abs, n_file_abs)
+
+ # 日志也删除
+ log_file = custom_hooks + '/self_logs/' + o_file + '.sh.log'
+ if os.path.exists(log_file):
+ os.remove(log_file)
+
+ return mw.returnJson(True, '重命名成功!')
+
+
+def projectScriptSelf_Enable():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'enable'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ enable = args['enable']
+
+ custom_path = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks'
+
+ # 替换commit配置
+ commit_path = custom_path + '/commit'
+ note = '#Gogs Script Don`t Remove and Change'
+
+ self_file = custom_path + '/self_hook.sh'
+ self_hook_tpl = getPluginDir() + '/hook/self_hook.tpl'
+
+ if enable == '1':
+ content = mw.readFile(self_hook_tpl)
+ content = content.replace('{$HOOK_DIR}', custom_path + '/self')
+ content = content.replace(
+ '{$HOOK_LOGS_DIR}', custom_path + '/self_logs')
+ mw.writeFile(self_file, content)
+ mw.execShell("chmod 777 " + self_file)
+ mw.execShell("chown -R www:www " + self_file)
+
+ commit_content = mw.readFile(commit_path)
+ commit_content += "\n\n" + "bash " + self_file + " " + note
+ mw.writeFile(commit_path, commit_content)
+
+ return mw.returnJson(True, '开启成功!')
+ else:
+ commit_content = mw.readFile(commit_path)
+ rep = ".*" + note
+ commit_content = re.sub(rep, '', commit_content, re.M)
+ commit_content = commit_content.strip()
+ mw.writeFile(commit_path, commit_content)
+ if os.path.exists(self_file):
+ os.remove(self_file)
+ return mw.returnJson(True, '关闭成功!')
+
+
+def projectScriptSelf_Status():
+ args = getArgs()
+ data = checkArgs(args, ['user', 'name', 'file', 'status'])
+ if not data[0]:
+ return data[1]
+
+ user = args['user']
+ name = args['name'] + '.git'
+ file = args['file']
+ status = args['status']
+
+ custom_hooks = getRootPath() + '/' + user + '/' + \
+ name + '/custom_hooks'
+ self_path = custom_hooks + '/self'
+
+ if not os.path.exists(self_path):
+ os.mkdir(self_path)
+
+ # 日志也删除
+ log_file = custom_hooks + '/self_logs/' + file + '.log'
+ if os.path.exists(log_file):
+ os.remove(log_file)
+
+ if status == '1':
+ file_abs = self_path + '/' + file
+ file_text_abs = self_path + '/' + file + '.txt'
+ os.rename(file_abs, file_text_abs)
+ return mw.returnJson(True, '开始禁用成功!')
+ else:
+ file_abs = self_path + '/' + file.strip('.txt')
+ file_text_abs = self_path + '/' + file
+ os.rename(file_text_abs, file_abs)
+ return mw.returnJson(True, '开始使用成功!')
+
+ return mw.returnJson(True, '禁用成功!')
+
+
+def getRsaPublic():
+ path = getHomeDir()
+ path += '/.ssh/id_rsa.pub'
+
+ content = mw.readFile(path)
+
+ data = {}
+ data['mw'] = content
+ return mw.getJson(data)
+
+
+def getTotalStatistics():
+ st = status()
+ data = {}
+ if st.strip() == 'start':
+ list_count = pQuery('select count(id) as num from repository')
+ count = list_count[0]["num"]
+ data['status'] = True
+ data['count'] = count
+ data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
+ return mw.returnJson(True, 'ok', data)
+
+ data['status'] = False
+ data['count'] = 0
+ return mw.returnJson(False, 'fail', data)
+
+
+def uninstallPreInspection():
+ repo_dir = getServerDir() + "/data/gitea-repositories"
+ if not os.path.exists(repo_dir):
+ return 'ok'
+ dir_list = os.listdir(repo_dir)
+ if len(dir_list) > 0:
+ return "有项目数据!请手动删除Gitea
rm -rf {}".format(getServerDir())
+ return 'ok'
+
+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 == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'post_receive_log':
+ print(postReceiveLog())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'init_conf':
+ print(getInitdConf())
+ elif func == 'get_gogs_conf':
+ print(getGogsConf())
+ elif func == 'submit_gogs_conf':
+ print(submitGogsConf())
+ elif func == 'gogs_edit_tpl':
+ print(gogsEditTpl())
+ elif func == 'user_list':
+ print(userList())
+ elif func == 'repo_list':
+ print(repoList())
+ elif func == 'user_project_list':
+ print(userProjectList())
+ elif func == 'project_script_edit':
+ print(projectScriptEdit())
+ elif func == 'project_script_load':
+ print(projectScriptLoad())
+ elif func == 'project_script_unload':
+ print(projectScriptUnload())
+ elif func == 'project_script_debug':
+ print(projectScriptDebug())
+ elif func == 'project_script_run':
+ print(projectScriptRun())
+ elif func == 'project_script_self':
+ print(projectScriptSelf())
+ elif func == 'project_script_self_create':
+ print(projectScriptSelf_Create())
+ elif func == 'project_script_self_del':
+ print(projectScriptSelf_Del())
+ elif func == 'project_script_self_logs':
+ print(projectScriptSelf_Logs())
+ elif func == 'project_script_self_run':
+ print(projectScriptSelf_Run())
+ elif func == 'project_script_self_rename':
+ print(projectScriptSelf_Rename())
+ elif func == 'project_script_self_enable':
+ print(projectScriptSelf_Enable())
+ elif func == 'project_script_self_status':
+ print(projectScriptSelf_Status())
+ elif func == 'get_rsa_public':
+ print(getRsaPublic())
+ elif func == 'get_total_statistics':
+ print(getTotalStatistics())
+ else:
+ print('fail')
diff --git a/plugins/doh/info.json b/plugins/doh/info.json
new file mode 100755
index 000000000..21edc77d9
--- /dev/null
+++ b/plugins/doh/info.json
@@ -0,0 +1,18 @@
+{
+ "ps": "DNS over HTTPS(DoH)是一种通过HTTPS协议加密域名解析请求的技术!",
+ "name": "doh",
+ "title": "DoH",
+ "versions": ["0.9.15"],
+ "tip": "soft",
+ "install_pre_inspection":false,
+ "uninstall_pre_inspection":true,
+ "checks": "server/doh",
+ "path":"server/doh",
+ "author": "doh",
+ "date": "2025-11-23",
+ "home": "https://github.com/DNSCrypt/doh-server",
+ "type": "doh",
+ "shell": "install.sh",
+ "pid": "4",
+ "sort": 7
+}
\ No newline at end of file
diff --git a/plugins/doh/init.d/doh.service.tpl b/plugins/doh/init.d/doh.service.tpl
new file mode 100644
index 000000000..8b71687a8
--- /dev/null
+++ b/plugins/doh/init.d/doh.service.tpl
@@ -0,0 +1,20 @@
+[Unit]
+Description=DOH (Git with a cup of tea)
+After=syslog.target
+After=network.target
+
+[Service]
+RestartSec=2s
+Type=simple
+User=www
+Group=www
+WorkingDirectory={$SERVER_PATH}/doh
+ExecStart={$SERVER_PATH}/doh/doh web
+Restart=always
+Environment=USER=www HOME=/home/www GITEA_WORK_DIR={$SERVER_PATH}/doh
+RemainAfterExit=yes
+#AmbientCapabilities=CAP_NET_BIND_SERVICE
+#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/doh/install.sh b/plugins/doh/install.sh
new file mode 100755
index 000000000..62ce1a4ea
--- /dev/null
+++ b/plugins/doh/install.sh
@@ -0,0 +1,96 @@
+#!/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")
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+
+# cd /www/server/mdserver-web/plugins/doh && bash install.sh install 0.9.15
+
+# 详细状态信息
+# sudo systemctl status doh -l
+# 查看完整日志
+# sudo journalctl -u doh -n 100
+# 实时日志跟踪
+# sudo journalctl -u doh -f
+
+
+URL_DOWNLOAD=https://github.com/DNSCrypt/doh-server/releases/download
+
+
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+Install_App()
+{
+
+ mkdir -p $serverPath/source/doh
+
+ echo '正在安装脚本文件...'
+ version=$1
+
+ if [ "macos" == "$OSNAME" ];then
+ echo "not support!"
+ exit
+ else
+ file=doh-proxy_${version}_linux-x86_64
+ fi
+
+ # https://github.com/DNSCrypt/doh-server/releases/download/0.9.15/doh-proxy_0.9.15_linux-aarch64.tar.bz2
+ file_xz="${file}.tar.bz2"
+ echo "wget -O $serverPath/source/doh/$file_xz ${URL_DOWNLOAD}/${version}/${file_xz}"
+ if [ ! -f $serverPath/source/doh/$file_xz ];then
+ wget --no-check-certificate -O $serverPath/source/doh/$file_xz ${URL_DOWNLOAD}/doh/${version}/${file_xz}
+ fi
+
+ cd $serverPath/source/doh && xz -k -d $file_xz
+ if [ -f $file ];then
+ mkdir -p $serverPath/doh
+ mv $serverPath/source/doh/$file $serverPath/doh/doh
+ fi
+
+
+ if [ -d $serverPath/doh ];then
+ echo $version > $serverPath/doh/version.pl
+
+ cd ${rootPath} && python3 plugins/doh/index.py start
+ cd ${rootPath} && python3 plugins/doh/index.py initd_install
+ fi
+
+ echo 'install doh success'
+}
+
+Uninstall_App()
+{
+
+ if [ -f /usr/lib/systemd/system/doh.service ];then
+ systemctl stop doh
+ systemctl disable doh
+ rm -rf /usr/lib/systemd/system/doh.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/doh/initd/doh ];then
+ $serverPath/doh/initd/doh stop
+ fi
+
+ rm -rf $serverPath/doh
+ echo 'uninstall doh success'
+}
+
+
+action=$1
+version=$2
+if [ "${1}" == 'install' ];then
+ Install_App $version
+else
+ Uninstall_App $version
+fi
diff --git a/plugins/doh/js/doh.js b/plugins/doh/js/doh.js
new file mode 100755
index 000000000..98d85fadf
--- /dev/null
+++ b/plugins/doh/js/doh.js
@@ -0,0 +1,774 @@
+
+function gogsPost(method,args,callback, title){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var _title = '正在获取...';
+ if (typeof(title) != 'undefined'){
+ _title = title;
+ }
+
+ var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'gitea', func:method, args:_args}, 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 gogsSetConfig(){
+ gogsPost('get_gogs_conf', '', function(data){
+ var rrdata = $.parseJSON(data.data);
+ if (!rrdata.status){
+ layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var rdata = rrdata.data;
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '140';
+ if (rdata[i].name == 'error_reporting') w = '250';
+ var ibody = '';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '';
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
+ ibody = ''
+ break;
+ case 2:
+ var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
+ ibody = ''
+ break;
+ }
+ mlist += '' + rdata[i].name + '' + ibody + ', ' + rdata[i].ps + '
'
+ }
+ var html = '\
+ ' + mlist + '\
+
\
+ \
+
\
+
';
+ $(".soft-man-con").html(html);
+ });
+}
+
+
+//提交PHP配置
+function submitGogsConf() {
+ var data = {
+ DOMAIN: $("input[name='DOMAIN']").val(),
+ ROOT_URL: $("input[name='ROOT_URL']").val(),
+ HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
+ HTTP_PORT: $("input[name='HTTP_PORT']").val(),
+ START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
+ SSH_PORT: $("input[name='SSH_PORT']").val(),
+ REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
+ ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
+ DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
+ ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
+ FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
+ SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
+ SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
+ SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
+ };
+
+ gogsPost('submit_gogs_conf', data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ gogsSetConfig();
+ });
+}
+
+function gogsEdit(){
+
+ gogsPost('gogs_edit',{} , function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ var edit = '通用的手动编辑:
';
+ edit +='\
+ \
+ \
+
';
+ $(".soft-man-con").html(edit);
+ });
+
+}
+
+
+function giteaUserList(page, search) {
+
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ if(typeof(search) != 'undefined'){
+ _data['search'] = search;
+ }
+
+ gogsPost('user_list', _data, function(data){
+
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ content = '';
+ content += '
';
+
+ content += '';
+ content += '';
+ content += '| 序号 | ';
+ content += '用户或组织 | ';
+ content += '邮件地址 | ';
+ content += '操作(WEB管理) | ';
+ content += '
';
+
+ content += '';
+
+ ulist = rdata['data']['data'];
+ for (i in ulist){
+
+ var email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
+ var user_url = rdata['data']['root_url'] + ulist[i]["name"];
+ content += '| '+ulist[i]["id"]+' | '+
+ ''+ulist[i]["name"]+' | '+
+ ''+email+' | '+
+ '项目管理 | '+
+ '
';
+ }
+
+ content += '';
+ content += '
';
+
+ var page_html = '';
+
+ content += page_html;
+
+ $(".soft-man-con").html(content);
+
+ $('.find_user').click(function(){
+ var name = $('#find_user').val();
+ giteaUserList(page, name);
+ });
+ });
+}
+
+function userProjectList(user, search){
+ var loadOpen = layer.open({
+ type: 1,
+ title: '用户('+user+')项目列表',
+ area: '500px',
+ content:"",
+ success:function(){
+ userProjectListPost(user,search);
+ }
+ });
+}
+
+function userProjectListPost(user, search){
+ var req = {};
+ if (!isNaN(user)){
+ req['page'] = user;
+ req['name'] = user = getCookie('gogsUserSelected');
+ } else {
+ req['page'] = 1;
+ req['name'] = user;
+ setCookie('gogsUserSelected', user);
+ }
+
+ req['page_size'] = 5;
+ req['search'] = '';
+ if(typeof(search) != 'undefined'){
+ req['search'] = search;
+ }
+
+ gogsPost('user_project_list', req, function(data){
+ var rdata = [];
+ try {
+ rdata = $.parseJSON(data.data);
+ } catch(e){}
+
+ if (!rdata['status']){
+ layer.msg(rdata['msg'], { icon: 2 });
+ return;
+ }
+
+ var list = '';
+ // console.log(rdata);
+ var project_list = rdata['data']['data'];
+ for (i in project_list) {
+ var name = project_list[i]['name'];
+ list += '\
+ | '+name+' | \
+ \
+ 源码 | \
+ 脚本\
+ | \
+
';
+ }
+
+ $('#gitea_table tbody').html(list);
+
+ var page = rdata['data']['list'];
+ $('#gitea_table .gitea_page').html(page);
+ });
+}
+
+
+function projectScript(user, name,has_hook){
+ // console.log(user,name,has_hook);
+ var html = '';
+ if (has_hook){
+ html += '';
+ html += '';
+ html += '';
+ html += '';
+ } else {
+ html += '';
+ }
+
+ var loadOpen = layer.open({
+ type: 1,
+ title: '['+user+']['+name+']脚本设置',
+ area: '240px',
+ content:''+html+'
',
+ success:function(layero,index) {
+
+ $('.hook_edit').click(function(){
+ projectScriptEdit(user,name,index);
+ });
+
+ $('.hook_log').click(function(){
+ projectScriptDebug(user,name,index);
+ });
+
+ $('.hook_load').click(function(){
+ projectScriptLoad(user,name,index);
+ });
+
+ $('.hook_unload').click(function(){
+ projectScriptUnload(user,name,index);
+ });
+ }
+ });
+}
+
+function projectScriptEdit(user,name,index){
+ gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata['status']){
+ onlineEditFile(0, rdata['data']['path']);
+ } else {
+ layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+function projectScriptLoad(user,name,index){
+ gogsPost('project_script_load', {'user':user,'name':name}, function(data){
+ if (data.data != 'ok'){
+ layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ showMsg('加载成功!',function(){
+ layer.close(index);
+ userProjectListPost(1);
+ },{icon:1,time:2000,shade: [0.3, '#000']},2000);
+ });
+}
+
+function projectScriptUnload(user,name,index){
+ gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
+ if (data.data != 'ok'){
+ layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ showMsg('卸载成功!',function(){
+ layer.close(index);
+ userProjectListPost(1);
+ },{icon:1,time:2000,shade: [0.3, '#000']},2000);
+ });
+}
+
+function projectScriptDebug(user,name,index){
+ gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata['status']){
+ onlineEditFile(0, rdata['path']);
+ } else {
+ showMsg(rdata.msg,function(){
+ },{icon:1,time:2000,shade: [0.3, '#000']},2000);
+ }
+ });
+}
+
+function gogsRepoListPage(page, search){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ if(typeof(search) != 'undefined'){
+ _data['search'] = search;
+ }
+
+ gogsPost('repo_list', _data, function(data){
+
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var ulist = rdata['data']['data'];
+ var body = ''
+ for (i in ulist){
+ // console.log(ulist[i]);
+
+ var option = '';
+ if(ulist[i]['has_hook']){
+ option += '卸载脚本' + ' | ';
+ option += '重载' + ' | ';
+ option += '编辑' + ' | ';
+ option += '日志' + ' | ';
+ option += '手动' + ' | ';
+ option += '自定义';
+ } else{
+ option += '加载脚本';
+ }
+
+
+ body += '| '+ulist[i]["id"]+' | '+
+ '' + ulist[i]["name"]+' | '+
+ '' + ulist[i]["repo"]+' | '+
+ '' +
+ '源码' + ' | ' +
+ option +
+ ' | ' +
+ '
';
+ }
+
+ $('#repo_list tbody').html(body);
+ $('#repo_list_page').html(rdata['data']['list']);
+
+ $('.find_repo').click(function(){
+ var find_repo = $('#find_repo').val();
+ gogsRepoListPage(page, find_repo);
+ });
+
+
+ $('#repo_list .load').click(function(){
+ var i = $(this).data('index');
+ var user = ulist[i]["name"];
+ var name = ulist[i]["repo"];
+
+ gogsPost('project_script_load', {'user':user,'name':name}, function(data){
+ if (data.data != 'ok'){
+ layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ layer.msg('加载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ setTimeout(function(){
+ gogsRepoListPage(page, search);
+ }, 2000);
+ });
+ });
+
+ $('#repo_list .unload').click(function(){
+ var i = $(this).data('index');
+ var user = ulist[i]["name"];
+ var name = ulist[i]["repo"];
+
+ gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
+ if (data.data != 'ok'){
+ layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ layer.msg('卸载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ setTimeout(function(){
+ gogsRepoListPage(page, search);
+ }, 2000);
+ });
+ });
+
+ $('#repo_list .edit').click(function(){
+ var i = $(this).data('index');
+ var user = ulist[i]["name"];
+ var name = ulist[i]["repo"];
+
+ gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata['status']){
+ onlineEditFile(0, rdata['data']['path']);
+ } else {
+ layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
+ }
+ });
+ });
+
+
+ $('#repo_list .debug').click(function(){
+ var i = $(this).data('index');
+ var user = ulist[i]["name"];
+ var name = ulist[i]["repo"];
+
+ gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata['status']){
+ onlineEditFile(0, rdata['path']);
+ } else {
+ layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
+ }
+ });
+ });
+
+
+ $('#repo_list .run').click(function(){
+ var i = $(this).data('index');
+ var user = ulist[i]["name"];
+ var name = ulist[i]["repo"];
+
+ gogsPost('project_script_run', {'user':user,'name':name}, function(data){
+ var data = $.parseJSON(data.data);
+ layer.msg(data.msg,{icon:data.status?1:2,time:2000,shade: [0.3, '#000']});
+ });
+ });
+
+ //---------
+ });
+}
+
+
+function giteaRepoList() {
+ content = '';
+ content += '
';
+
+ content += '';
+ content += '';
+ content += '| 序号 | ';
+ content += '用户/组织 | ';
+ content += '项目名 | ';
+ content += '操作 | ';
+ content += '
';
+
+ content += '';
+ content += '
';
+
+ var page = '';
+
+ content += page;
+
+ $(".soft-man-con").html(content);
+
+ gogsRepoListPage(1);
+}
+
+function projectScriptSelfRender(user, name){
+ gogsPost('project_script_self', {'user':user,'name':name}, function(data){
+ var rdata = $.parseJSON(data.data);
+
+ var data = rdata['data']['data'];
+
+ if (rdata['data']['self_hook']){
+ $('#open_script').prop('checked',true);
+ }
+
+ var body = '';
+ if(data.length == 0 ){
+ body += '| 无脚本数据 |
';
+ } else{
+ for (var i = 0; i < data.length; i++) {
+ var b_status = '已使用';
+ if (data[i]["is_hidden"]){
+ b_status = '已隐藏';
+ }
+
+ body += ''+
+ '| ' + data[i]["name"]+' | '+
+ '' + b_status + ' | '+
+ '' +
+ '删除' + ' | ' +
+ '编辑' + ' | ' +
+ '日志' + ' | ' +
+ '手动' + ' | ' +
+ '重命名' +
+ ' |
';
+ }
+
+ }
+
+ $('#gogs_self_table tbody').html(body);
+ $('#gogs_self_table .page').html(rdata['data']['list']);
+
+ $('#gogs_self_table .status').click(function(){
+ var i = $(this).data('index');
+ var file = data[i]["name"];
+ var status = '1';
+ if (data[i]["is_hidden"]){
+ status = '0';
+ }
+ gogsPost('project_script_self_status', {'user':user,'name':name,'file':file, status:status}, function(data){
+ var data = $.parseJSON(data.data);
+ showMsg(data.msg ,function(){
+ projectScriptSelfRender(user, name);
+ },{icon:data.code?2:1,time:2000,shade: [0.3, '#000']},2000);
+ });
+ });
+
+ $('#gogs_self_table .del').click(function(){
+ var i = $(this).data('index');
+ var file = data[i]["name"];
+ gogsPost('project_script_self_del', {'user':user,'name':name,'file':file}, function(data){
+ var data = $.parseJSON(data.data);
+ showMsg(data.msg ,function(){
+ projectScriptSelfRender(user, name);
+ },{icon:data.code?2:1,time:2000,shade: [0.3, '#000']},2000);
+ });
+ });
+
+ $('#gogs_self_table .edit').click(function(){
+ var i = $(this).data('index');
+ var path = data[i]["path"];
+ onlineEditFile(0,path);
+ });
+
+ $('#gogs_self_table .logs').click(function(){
+ var i = $(this).data('index');
+ var file = data[i]["name"];
+ gogsPost('project_script_self_logs', {'user':user,'name':name,'file':file}, function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ if (rdata['status']){
+ onlineEditFile(0, rdata['data']['path']);
+ } else {
+ layer.msg(rdata.msg,{icon:data.status?2:1,time:2000,shade: [0.3, '#000']});
+ }
+ });
+ });
+
+ $('#gogs_self_table .run').click(function(){
+ var i = $(this).data('index');
+ var file = data[i]["name"];
+ if (data[i]["is_hidden"]){
+ layer.msg("已经禁用,不能执行!",{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ gogsPost('project_script_self_run', {'user':user,'name':name,'file':file}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:data.status?1:2,time:2000,shade: [0.3, '#000']});
+ });
+ });
+
+
+ $('#gogs_self_table .rename').click(function(){
+ var i = $(this).data('index');
+ var file = data[i]["name"];
+
+ if (data[i]["is_hidden"]){
+ layer.msg("已经禁用,不能执行!",{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ file = file.split('.sh')[0];
+
+ layer.open({
+ type: 1,
+ shift: 5,
+ closeBtn: 1,
+ area: '320px',
+ title: '重命名',
+ btn:['设置','关闭'],
+ content: '',
+ success:function(){
+ $("#newFileName").focus().keyup(function(e){
+ if(e.keyCode == 13) $(".layui-layer-btn0").click();
+ });
+ },
+ yes:function(){
+ var n_file = $("#newFileName").val();
+ var o_file = file;
+
+ gogsPost('project_script_self_rename', {'user':user,'name':name,'o_file':o_file,'n_file':n_file}, function(data){
+ var data = $.parseJSON(data.data);
+ showMsg(data.msg ,function(){
+ $(".layui-layer-btn1").click();
+ projectScriptSelfRender(user, name);
+ },{icon:data.code?2:1,time:2000,shade: [0.3, '#000']},2000);
+ });
+ }
+ });
+ //-----
+ });
+ //------
+ });
+}
+
+//新建文件
+function createScriptFile(type, user, name, file) {
+ if (type == 1) {
+ gogsPost('project_script_self_create', {'user':user,'name':name,'file': file }, function(data){
+ var rdata = $.parseJSON(data.data);
+ if(!rdata['status']){
+ layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ showMsg(rdata.msg, function(){
+ $(".layui-layer-btn1").click();
+ onlineEditFile(0,rdata['data']['abs_file']);
+ projectScriptSelfRender(user, name);
+ }, {icon:1,shade: [0.3, '#000']},2000);
+ });
+ return;
+ }
+ layer.open({
+ type: 1,
+ shift: 5,
+ closeBtn: 1,
+ area: '320px',
+ title: '新建自定义脚本',
+ btn:['新建','关闭'],
+ content: '',
+ success:function(){
+ $("#newFileName").focus().keyup(function(e){
+ if(e.keyCode == 13) $(".layui-layer-btn0").click();
+ });
+ },
+ yes:function(){
+ var file = $("#newFileName").val();;
+ createScriptFile(1, user, name, file);
+ }
+ });
+}
+
+function projectScriptSelf(user, name){
+ layer.open({
+ type: 1,
+ title: '项目('+user+'/'+name+')自定义脚本',
+ area: '500px',
+ content:"",
+ success:function(){
+ projectScriptSelfRender(user, name);
+
+ $('#create_script').click(function(){
+ createScriptFile(0, user, name);
+ });
+
+ $('#script_hook_enable').click(function(){
+ var enable = $('#open_script').prop('checked');
+ var enable_option = '0';
+ if (!enable){
+ enable_option = '1';
+ }
+ gogsPost('project_script_self_enable', {'user':user,'name':name,'enable':enable_option}, function(data){
+ var data = $.parseJSON(data.data);
+ showMsg(data.msg ,function(){
+ projectScriptSelfRender(user, name);
+ },{icon:data.status?1:2,shade: [0.3, '#000']},2000);
+ });
+
+ });
+ }
+ });
+}
+
+function getRsaPublic(){
+ gogsPost('get_rsa_public', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ var con = ''
+ layer.open({
+ type: 1,
+ area: "600px",
+ title: '本机公钥',
+ closeBtn: 2,
+ shift: 5,
+ shadeClose: false,
+ content:con
+ });
+ });
+}
+
+function dohRead(){
+
+ var readme = '';
+ readme += '- 默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置
';
+ readme += '- 邮件端口使用456,gitea仅支持使用STARTTLS的SMTP协议
';
+ readme += '- 项目【加载脚本】后,会自动同步到wwwroot目录下
';
+ readme += '- 点击查看本机公钥>
';
+ readme += '
';
+
+ $('.soft-man-con').html(readme);
+}
\ No newline at end of file