├── .idea ├── .gitignore ├── AMEN-GoEdge-User.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── API ├── HTTPWebService.py ├── MetricStatService.py ├── OriginService.py ├── ReverseProxyService.py ├── SSLCertService.py ├── SSLPolicyService.py ├── ServerService.py ├── UserService.py └── __init__.py ├── Config └── Config_initialization.json ├── Controller ├── ConfigService.py ├── HTTPAccessLogService.py ├── HTTPWebService.py ├── MetricStatService.py ├── OriginService.py ├── SSLCertService.py ├── SSLPolicyService.py ├── ServerService.py ├── UserService.py └── Verification.py ├── ErrCode └── __init__.py ├── LICENSE ├── README.md ├── Sql └── __init__.py ├── Utils ├── __init__.py ├── cert.py └── tencent.py ├── edge_code.sql ├── main.py ├── requirements.txt └── test_main.http /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/AMEN-GoEdge-User.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /API/HTTPWebService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | import traceback 4 | 5 | from API import Api 6 | from API import ServerService 7 | 8 | 9 | class HTTPWebService(Api): 10 | def updateHTTPWebRedirectToHTTPS(self, ServerService: ServerService, serverId:int, httpWebId: int, isOn: bool, status: bool): 11 | """ 12 | 13 | :param ServerService: 14 | :param httpWebId: webid 15 | :param isOn: 启用禁用 16 | :param status: bool位=为301否则302 17 | :return: 18 | """ 19 | ServerConfig = ServerService.findEnabledServerConfig(serverId) 20 | if not ServerConfig[0]: 21 | if ServerConfig[1] is None: 22 | return [False, None] 23 | else: 24 | return [False, ServerConfig[1]] 25 | try: 26 | Config = ServerConfig[1] 27 | Config = json.loads(base64.b64decode(Config).decode()) 28 | redirectToHTTPSJson: dict = Config['web']['redirectToHTTPS'] 29 | if redirectToHTTPSJson is None: 30 | status_i = 301 31 | isOn = False 32 | redirectToHTTPSJson = {"host": "", "port": 0, "isPrior": False, "onlyDomains": None, "exceptDomains": None} 33 | if status: 34 | status_i = 301 35 | else: 36 | status_i = 302 37 | redirectToHTTPSJson.update({ 38 | "isOn": isOn, 39 | "status": status_i 40 | }) 41 | redirectToHTTPSJson_bytes = base64.b64encode(json.dumps(redirectToHTTPSJson).encode('utf-8')).decode( 42 | "utf-8") 43 | except: 44 | return [False, None] 45 | submit = { 46 | "httpWebId": httpWebId, 47 | "redirectToHTTPSJSON": redirectToHTTPSJson_bytes 48 | } 49 | res = self.post(self.Host + "/HTTPWebService/updateHTTPWebRedirectToHTTPS", json=submit) 50 | try: 51 | if res.json()['code'] == 200: 52 | return [True, "ok"] 53 | else: 54 | return [False, res.json()['message']] 55 | except: 56 | return [False, None] 57 | -------------------------------------------------------------------------------- /API/MetricStatService.py: -------------------------------------------------------------------------------- 1 | from API import Api 2 | 3 | 4 | class MetricStatService(Api): 5 | def listMetricStats(self): 6 | res = self.post(self.Host + "/MetricStatService/listMetricStats", json={ 7 | "metricItemId": 1, 8 | "offset":0, 9 | "size":80 10 | }) 11 | print(res.content.decode()) 12 | -------------------------------------------------------------------------------- /API/OriginService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | import traceback 4 | 5 | from API import Api, ReverseProxyService 6 | 7 | 8 | class OriginService(Api): 9 | def findEnabledOrigin(self, originId: int): 10 | submit = { 11 | "originId": originId 12 | } 13 | # findEnabledOrigin是简要信息 14 | # {"code":200,"data":{"Origin":{"id":2,"isOn":true,"addr":{"protocol":"http","host":"webserver1.ymqq.top","portRange":"8800"}}},"message":"ok"} 15 | # findEnabledOriginConfig是复杂信息,data.originJSON是base64编码 16 | res = self.post(self.Host + "/OriginService/findEnabledOriginConfig", json=submit) 17 | try: 18 | if res.json()['code'] == 200: 19 | if 'originJSON' in res.json()['data']: 20 | return [True, res.json()['data']['originJSON']] 21 | else: 22 | return [True, None] 23 | else: 24 | return [False, res.json()['message']] 25 | except: 26 | return [False, None] 27 | 28 | def updateOrigin(self, originJson: dict): 29 | submit = originJson 30 | submit_v1 = {} 31 | try: 32 | submit['connTimeout']['count'] = int(submit['connTimeout']['count']) 33 | submit['readTimeout']['count'] = int(submit['readTimeout']['count']) 34 | submit['idleTimeout']['count'] = int(submit['idleTimeout']['count']) 35 | connTimeoutJSON = base64.b64encode(json.dumps(submit['connTimeout']).encode('utf-8')).decode("utf-8") 36 | readTimeoutJSON = base64.b64encode(json.dumps(submit['readTimeout']).encode('utf-8')).decode("utf-8") 37 | idleTimeoutJSON = base64.b64encode(json.dumps({"unit": "second", "count": 0}).encode('utf-8')).decode("utf-8") # submit['idleTimeout'] 38 | # certRefJSON = base64.b64encode(json.dumps(submit['certRef']).encode('utf-8')).decode("utf-8") 39 | if submit['certRef']['certId'] == 0: 40 | certRefJSON = "" 41 | else: 42 | certRefJSON = base64.b64encode(('{"isOn": true, "certId": '+str(submit['certRef']['certId'])+'}').encode('utf-8')).decode("utf-8") 43 | 44 | submit_v1['originId'] = submit['id'] 45 | submit_v1['connTimeoutJSON'] = connTimeoutJSON # 连接时间 46 | submit_v1['readTimeoutJSON'] = readTimeoutJSON # 读取时间 47 | submit_v1['idleTimeoutJSON'] = idleTimeoutJSON # 闲置超时 idleTimeoutJSON 48 | submit_v1['name'] = submit['name'] # 名 49 | 50 | submit['addr']['maxPort'] = 0 51 | submit['addr']['minPort'] = 0 52 | submit_v1['addr'] = submit['addr'] # 源站信息 host portRange protocol 【maxPort minPort】这两个保持默认0 53 | 54 | submit_v1[ 55 | 'description'] = "AMEN-GoEdge-User创建的源站,不可删除,请通过AMEN-GoEdge-User删除,否则AMEN-GoEdge-User异常" # 备注submit['description'] 56 | submit_v1['weight'] = int(submit['weight']) # 权重 57 | submit_v1['isOn'] = submit['isOn'] # 是否启用 58 | submit_v1['maxConns'] = 0 # 最大连接数submit['maxConns'] 59 | submit_v1['maxIdleConns'] = 0 # 最大空闲超时时间submit['idleConns'] 60 | submit_v1['domains'] = [] # 专属域名 61 | submit_v1['certRefJSON'] = certRefJSON # 源站https 62 | submit_v1['followPort'] = False # 端口跟随submit['followPort'] 63 | submit_v1['host'] = "" # 回源主机名submit['requestHost'] 64 | except: 65 | traceback.print_exc() 66 | return [False, "提交数据错误"] 67 | res = self.post(self.Host + "/OriginService/updateOrigin", json=submit_v1) 68 | try: 69 | if res.json()['code'] == 200: 70 | if 'originJSON' in res.json()['data']: 71 | return [True, res.json()['data']['originJSON']] 72 | else: 73 | return [True, None] 74 | else: 75 | return [False, res.json()['message']] 76 | except: 77 | return [False, None] 78 | 79 | def deleteOrigin(self, reverseProxyId: int, GoEdgeApi: ReverseProxyService, originId: int, originJson: dict, 80 | Primary: bool = False): 81 | """ 82 | 通过源数据+删除主备字段+将要删除id叠合后触发更新源站信息 83 | :param reverseProxyId: 84 | :param GoEdgeApi: 85 | :param originId: 86 | :param originJson: 87 | :param Primary: 88 | :return: 89 | """ 90 | isOk = False 91 | for i, v in enumerate(originJson): 92 | if v['originId'] == originId: 93 | isOk = True 94 | break 95 | if not isOk: 96 | return [False, '未找到此源站信息'] 97 | originJson.pop(i) 98 | return GoEdgeApi.updateReverseProxOrigins(reverseProxyId=reverseProxyId, originsJSON=originJson, 99 | Primary=Primary) 100 | 101 | def createOrigin(self, originJson: dict): 102 | submit = originJson 103 | submit_v1 = {} 104 | try: 105 | submit['connTimeout']['count'] = int(submit['connTimeout']['count']) 106 | submit['readTimeout']['count'] = int(submit['readTimeout']['count']) 107 | # submit['idleTimeout']['count'] = int(submit['idleTimeout']['count']) 108 | submit['connTimeout']['unit'] = "second" 109 | submit['readTimeout']['unit'] = "second" 110 | # submit['idleTimeout'] = {} 111 | # submit['idleTimeout']['unit'] = "second" 112 | connTimeoutJSON = base64.b64encode(json.dumps(submit['connTimeout']).encode('utf-8')).decode("utf-8") 113 | readTimeoutJSON = base64.b64encode(json.dumps(submit['readTimeout']).encode('utf-8')).decode("utf-8") 114 | # idleTimeoutJSON = base64.b64encode(json.dumps(submit['idleTimeout']).encode('utf-8')).decode("utf-8") 115 | idleTimeoutJSON = base64.b64encode(json.dumps({"unit": "second", "count": 0}).encode('utf-8')).decode( 116 | "utf-8") 117 | 118 | certRefJSON = base64.b64encode(json.dumps(None).encode('utf-8')).decode("utf-8") 119 | 120 | submit_v1['name'] = "" 121 | 122 | submit_v1['addr'] = submit['addr'] 123 | submit_v1['addr']['minPort'] = 0 124 | submit_v1['addr']['maxPort'] = 0 125 | 126 | submit_v1['description'] = "" 127 | 128 | submit_v1['weight'] = int(submit['weight']) 129 | 130 | submit_v1['isOn'] = True 131 | 132 | submit_v1['connTimeoutJSON'] = connTimeoutJSON 133 | submit_v1['readTimeoutJSON'] = readTimeoutJSON 134 | submit_v1['idleTimeoutJSON'] = idleTimeoutJSON 135 | 136 | submit_v1['maxConns'] = 0 137 | 138 | submit_v1['maxIdleConns'] = 0 139 | 140 | submit_v1['domains'] = [] 141 | 142 | submit_v1['certRefJSON'] = certRefJSON 143 | 144 | submit_v1['host'] = submit['host'] 145 | 146 | submit_v1['followPort'] = False 147 | except: 148 | traceback.print_exc() 149 | return [False, "提交数据错误"] 150 | res = self.post(self.Host + "/OriginService/createOrigin", json=submit_v1) 151 | try: 152 | if res.json()['code'] == 200: 153 | if 'originId' in res.json()['data']: 154 | return [True, res.json()['data']['originId']] 155 | else: 156 | return [True, None] 157 | else: 158 | return [False, res.json()['message']] 159 | except: 160 | return [False, None] 161 | -------------------------------------------------------------------------------- /API/ReverseProxyService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | 4 | from API import Api 5 | 6 | 7 | class ReverseProxyService(Api): 8 | def updateReverseProxOrigins(self, reverseProxyId: int, originsJSON: dict, Primary: bool = False): 9 | """ 10 | 修改源站信息,不是源站配置,是网站服务器->源站信息->源站配置。做新增,新增数据为:源数据+新数据,源数据由上层传递,本函数不做查询源数据 11 | :param reverseProxyId: 12 | :param originsJSON: 13 | :param Primary: 14 | :return: 15 | """ 16 | originsJSON_bytes = base64.b64encode(json.dumps(originsJSON).encode('utf-8')).decode("utf-8") 17 | submit = { 18 | "reverseProxyId": reverseProxyId, 19 | "originsJSON": originsJSON_bytes 20 | } 21 | if Primary: 22 | res = self.post(self.Host + "/ReverseProxyService/updateReverseProxyPrimaryOrigins", json=submit) 23 | else: 24 | res = self.post(self.Host + "/ReverseProxyService/updateReverseProxyBackupOrigins", json=submit) 25 | try: 26 | if res.json()['code'] == 200: 27 | return [True,"ok"] 28 | else: 29 | return [True, res.json()['message']] 30 | except: 31 | return [False,None] 32 | -------------------------------------------------------------------------------- /API/SSLCertService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from API import Api 4 | 5 | 6 | class SSLCertService(Api): 7 | def listSSLCerts(self, userId: int, offset: int, size: int, keyword: str = None): 8 | if keyword is None: 9 | submit = { 10 | "userId": userId, 11 | "offset": offset, 12 | "size": size 13 | } 14 | else: 15 | submit = { 16 | "userId": userId, 17 | "offset": offset, 18 | "size": size, 19 | "keyword": keyword 20 | } 21 | res = self.post(self.Host + "/SSLCertService/listSSLCerts", json=submit) 22 | try: 23 | if res.json()['code'] == 200: 24 | if "sslCertsJSON" in res.json()['data']: 25 | return [True, res.json()['data']['sslCertsJSON']] 26 | else: 27 | return [True, 'W10='] 28 | else: 29 | return [False, res.json()['message']] 30 | except: 31 | return [False, None] 32 | 33 | def createSSLCert(self, name: str, isCA: bool, certData: str, keyData: str,timeBeginAt:int,timeEndAt:int,dnsNames:list,issuer_type:str,issuer_author:str): 34 | """ 35 | certData keyData base64编码 36 | :param issuer_author: 37 | :param issuer_type: 38 | :param dnsNames: 39 | :param timeEndAt: 40 | :param timeBeginAt: 41 | :param name: 42 | :param isCA: 43 | :param certData: 44 | :param keyData: 45 | :return: 46 | """ 47 | # certData_bytes = base64.b64encode(certData.encode('utf-8')).decode("utf-8") 48 | # keyData_bytes = base64.b64encode(keyData.encode('utf-8')).decode("utf-8") 49 | # print(certData_bytes) 50 | submit = { 51 | "isOn": True, 52 | "name": name, 53 | "serverName": "", 54 | "isCA": isCA, 55 | "certData": certData, 56 | "keyData": keyData, 57 | "timeBeginAt": timeBeginAt, 58 | "timeEndAt": timeEndAt, 59 | "dnsNames": dnsNames, 60 | "commonNames": [issuer_type, issuer_author], 61 | "description": "AMEN-GoEdge-User创建的HTTPS证书,用户为:AMEN,不可删除,请通过AMEN-GoEdge-User删除,否则AMEN-GoEdge-User异常" 62 | } 63 | res = self.post(self.Host + "/SSLCertService/createSSLCert", json=submit) 64 | try: 65 | if res.json()['code'] == 200: 66 | if "sslCertId" in res.json()['data']: 67 | return [True,res.json()['data']['sslCertId']] 68 | else: 69 | return [False,res.json()['message']] 70 | else: 71 | return [False,res.json()['message']] 72 | except: 73 | return [False,None] 74 | 75 | def countSSLCerts(self, userId: int, keyword: str = None): 76 | if keyword is None: 77 | submit = { 78 | "userId": userId 79 | } 80 | else: 81 | submit = { 82 | "userId": userId, 83 | "keyword": keyword 84 | } 85 | res = self.post(self.Host + "/SSLCertService/countSSLCerts", json=submit) 86 | try: 87 | if res.json()['code'] == 200: 88 | if "count" in res.json()['data']: 89 | return [True, res.json()['data']['count']] 90 | else: 91 | return [True, 0] 92 | else: 93 | return [False, res.json()['message']] 94 | except: 95 | return [False, None] 96 | 97 | def deleteSSLCert(self,sslCertId:int): 98 | submit = { 99 | "sslCertId":sslCertId 100 | } 101 | res = self.post(self.Host + "/SSLCertService/deleteSSLCert", json=submit) 102 | try: 103 | if res.json()['code'] == 200: 104 | return [True, "ok"] 105 | else: 106 | return [False, res.json()['message']] 107 | except: 108 | return [False, None] 109 | 110 | def countAllEnabledServersWithSSLCertId(self,sslCertId:int): 111 | """计算使用某个SSL证书的服务数量""" 112 | submit = { 113 | "sslCertId":sslCertId 114 | } 115 | res = self.post(self.Host+"/ServerService/countAllEnabledServersWithSSLCertId",json=submit) 116 | try: 117 | if res.json()['code'] == 200: 118 | if "count" in res.json()['data']: 119 | return [True, res.json()['data']['count']] 120 | else: 121 | return [True, 0] 122 | else: 123 | return [False, res.json()['message']] 124 | except: 125 | return [False, None] 126 | 127 | -------------------------------------------------------------------------------- /API/SSLPolicyService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | 4 | from API import Api 5 | 6 | 7 | class SSLPolicyService(Api): 8 | def updateSSLPolicy(self, sslPolicyId: int, ocsp: bool, sslID: int, hsts: dict, http2Enabled: bool, 9 | minVersion: str): 10 | if "isOn" not in hsts or "maxAge" not in hsts or "preload" not in hsts or "includeSubDomains" not in hsts: 11 | return [False, None] 12 | try: 13 | hstsV2 = {} 14 | hstsV2['isOn'] = hsts['isOn'] 15 | hstsV2['maxAge'] = int(hsts['maxAge']) 16 | hstsV2['preload'] = hsts['preload'] 17 | hstsV2['includeSubDomains'] = hsts['includeSubDomains'] 18 | hstsV2['domains'] = [] 19 | hsts_bytes = base64.b64encode(json.dumps(hstsV2).encode('utf-8')).decode('utf-8') 20 | if sslID <= 0: 21 | ssl = base64.b64encode('[]'.encode('utf-8')).decode('utf-8') 22 | else: 23 | ssl = base64.b64encode(('[{"isOn": true, "certId": '+str(sslID)+'}]').encode('utf-8')).decode('utf-8') 24 | except: 25 | return [False,None] 26 | submit = { 27 | "sslPolicyId": sslPolicyId, 28 | "http2Enabled": http2Enabled, 29 | "sslCertsJSON": ssl, 30 | "hstsJSON": hsts_bytes, 31 | "ocspIsOn": ocsp, 32 | "minVersion": minVersion 33 | } 34 | res = self.post(self.Host + "/SSLPolicyService/updateSSLPolicy", json=submit) 35 | try: 36 | if res.json()['code'] == 200: 37 | return [True, "ok"] 38 | else: 39 | return [False, res.json()['message']] 40 | except: 41 | return [False, None] 42 | -------------------------------------------------------------------------------- /API/ServerService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | import traceback 4 | 5 | import Sql 6 | from API import Api, ReverseProxyService 7 | 8 | 9 | class ServerService(Api): 10 | def createServer(self, ClusterId: int, userId: int, name: str, domain: str): 11 | id_info = self.createReverseProxy() # 创建反向代理 12 | if not id_info[0]: 13 | # 创建失败 14 | return [False, id_info[1]] 15 | # 创建成功 16 | reverse_id = id_info[1] 17 | reverse_json = json.dumps( 18 | {"isOn": True, "isPrior": False, "reverseProxyId": int(reverse_id)} 19 | ) 20 | reverse_json = base64.b64encode(reverse_json.encode('utf-8')).decode("utf-8") 21 | # 创建ssl配置 22 | ssl_info = self.createSSLPolicy() 23 | if not ssl_info[0]: 24 | # 创建失败 25 | return [False, ssl_info[1]] 26 | ssl_id = ssl_info[1] 27 | 28 | # 查询是否存在domain 29 | # if not self.finddomain(ESql, domain): 30 | # return [False, "域名 {} 已经被其他服务所占用,不能重复使用".format(domain)] 31 | domainList = self.finddomain(ClusterId, [domain]) 32 | 33 | if domainList is None: 34 | return [False, None] 35 | if domain in domainList: 36 | return [False, "域名 {} 已经被其他服务所占用,不能重复使用,如果确保域名是自己的,请带上域名所有权和所有权本人有效证件提交给本站管理员/客服处理".format(domain)] 37 | server_json = json.dumps( 38 | [{"name": domain, "type": "full"}] 39 | ) 40 | server_json = base64.b64encode(server_json.encode('utf-8')).decode("utf-8") 41 | http_json = json.dumps( 42 | {"isOn": True, "listen": [{"host": "", "maxPort": 0, "minPort": 0, "protocol": "http", "portRange": "80"}]} 43 | ) 44 | http_json = base64.b64encode(http_json.encode('utf-8')).decode("utf-8") 45 | https_json = json.dumps( 46 | {"isOn": False, 47 | "listen": [{"host": "", "maxPort": 443, "minPort": 443, "protocol": "https", "portRange": "443"}], 48 | "sslPolicy": None, "sslPolicyRef": {"isOn":True,"sslPolicyId":int(ssl_id)}} 49 | ) 50 | https_json = base64.b64encode(https_json.encode('utf-8')).decode("utf-8") 51 | submit = { 52 | "userId": userId, 53 | "name": name, 54 | "type": "httpProxy", 55 | # httpProxy:CDN加速 分发全部 56 | # httpWeb:HTTP Web服务 分发静态 57 | # tcpProxy:TCP反向代理 tcp反代 58 | # udpProxy:UDP反向代理 udp反代 59 | # 先只做分发全部 60 | "description": "AMEN-GoEdge-User创建的网站服务,不可删除,请通过AMEN-GoEdge-User删除,否则AMEN-GoEdge-User异常", 61 | "serverNamesJON": server_json, 62 | "httpJSON": http_json, 63 | "httpsJSON": https_json, 64 | "reverseProxyJSON": reverse_json 65 | } 66 | res = self.post(self.Host + "/ServerService/createServer", json=submit) 67 | try: 68 | if res is None: 69 | return [False, None] 70 | if res.json()['code'] != 200: 71 | return [False, res.json()['message']] 72 | self.findAndInitServerWebConfig(res.json()['data']['serverId']) # 初始化web 73 | return [True, res.json()['data']['serverId']] 74 | except: 75 | traceback.print_exc() 76 | return [False, None] 77 | 78 | def finddomain(self, ClusterId: int, domain: list): 79 | # 查询是否已存在此域名,返回id x 80 | # 同样没有此接口,遍历数据库 81 | # row = ESql.fetch("""select * from `edgeServers` WHERE `plainServerNames` like '"qqq.com"' limit 1""") 82 | # if row is None: 83 | # return None 84 | # return row['id'] 85 | submit = { 86 | "nodeClusterId": ClusterId, 87 | "serverNames": domain 88 | } 89 | res = self.post(self.Host + "/ServerService/checkServerNameDuplicationInNodeCluster", json=submit) 90 | try: 91 | if "duplicatedServerNames" in res.json()['data']: 92 | return res.json()['data']['duplicatedServerNames'] 93 | else: 94 | return [] 95 | except: 96 | return None 97 | 98 | def findAllUserServers(self, userId: int): 99 | # 防止越权,用usertoken 100 | submit = { 101 | "userId": userId 102 | } 103 | res = self.post(self.Host + "/ServerService/findAllUserServers", json=submit) 104 | try: 105 | if res.json()['code'] == 200: 106 | if "servers" in res.json()['data']: 107 | return [True, res.json()['data']['servers']] 108 | else: 109 | return [True, []] 110 | else: 111 | return [False, res.json()['message']] 112 | except: 113 | return [False, None] 114 | 115 | def updateServerIsOn(self, serverId: int, isOn: bool): 116 | submit = { 117 | "serverId": serverId, 118 | "isOn": isOn 119 | } 120 | res = self.post(self.Host + "/ServerService/updateServerIsOn", json=submit) 121 | try: 122 | if res.json()['code'] == 200: 123 | return [True, 'ok'] 124 | else: 125 | return [False, res.json()['message']] 126 | except: 127 | return [False, None] 128 | 129 | def findEnabledServerConfig(self, serverId: int): 130 | submit = { 131 | "serverId": serverId 132 | } 133 | res = self.post(self.Host + "/ServerService/findEnabledServerConfig", json=submit) 134 | try: 135 | if res.json()['code'] == 200: 136 | if 'serverJSON' in res.json()['data']: 137 | # base64.b64decode(res.json()['data']['serverJSON']) 138 | return [True, res.json()['data']['serverJSON']] 139 | else: 140 | return [True, "e30="] 141 | else: 142 | return [False, res.json()['message']] 143 | except: 144 | return [False, None] 145 | 146 | def findEnabledServer(self, serverId: int): 147 | submit = { 148 | "serverId": serverId 149 | } 150 | res = self.post(self.Host + "/ServerService/findEnabledServer", json=submit) 151 | try: 152 | if res.json()['code'] == 200: 153 | if 'server' in res.json()['data']: 154 | return [True, res.json()['data']['server']] 155 | else: 156 | return [True, {}] 157 | else: 158 | return [False, res.json()['message']] 159 | except: 160 | return [False, None] 161 | 162 | def deleteServer(self, serverId: int): 163 | submit = { 164 | "serverId": serverId 165 | } 166 | res = self.post(self.Host + "/ServerService/deleteServer", json=submit) 167 | try: 168 | if res.json()['code'] == 200: 169 | return [True, "ok"] 170 | else: 171 | return [False, res.json()['message']] 172 | except: 173 | return [False, None] 174 | 175 | def updateServerReverseProxy(self, serverId: int, reverseProxyJSON: dict): 176 | # 这种方式不安全,换 177 | # reverseProxyJSON_bytes = base64.b64encode(json.dumps(reverseProxyJSON).encode('utf-8')).decode("utf-8") 178 | # reverseProxyJSON_bytes = base64.b64encode(json.dumps(reverseProxyJSON).encode('utf-8')).decode("utf-8") 179 | ServerConfig = self.findEnabledServerConfig(serverId) 180 | if not ServerConfig[0]: 181 | if ServerConfig[1] is None: 182 | return [False, None] 183 | else: 184 | return [False, ServerConfig[1]] 185 | try: 186 | Config = ServerConfig[1] 187 | Config = json.loads(base64.b64decode(Config).decode()) 188 | reverseProxy = Config['reverseProxyRef'] 189 | reverseProxy['isOn'] = reverseProxyJSON['isOn'] 190 | reverseProxyJSON_bytes = base64.b64encode(json.dumps(reverseProxy).encode('utf-8')).decode("utf-8") 191 | except: 192 | return [False, None] 193 | submit = { 194 | "serverId": serverId, 195 | "reverseProxyJSON": reverseProxyJSON_bytes 196 | } 197 | res = self.post(self.Host + "/ServerService/updateServerReverseProxy", json=submit) 198 | try: 199 | if res.json()['code'] == 200: 200 | return [True, "ok"] 201 | else: 202 | return [False, res.json()['message']] 203 | except: 204 | return [False, None] 205 | 206 | def updateServerHTTPS(self, serverId: int, isOn: bool): 207 | ServerConfig = self.findEnabledServerConfig(serverId) 208 | if not ServerConfig[0]: 209 | if ServerConfig[1] is None: 210 | return [False, None] 211 | else: 212 | return [False, ServerConfig[1]] 213 | try: 214 | Config = ServerConfig[1] 215 | Config = json.loads(base64.b64decode(Config).decode()) 216 | https = Config['https'] 217 | https['isOn'] = isOn 218 | httpsJSON_bytes = base64.b64encode(json.dumps(https).encode('utf-8')).decode("utf-8") 219 | except: 220 | return [False, None] 221 | submit = { 222 | "serverId": serverId, 223 | "httpsJSON": httpsJSON_bytes 224 | } 225 | res = self.post(self.Host + "/ServerService/updateServerHTTPS", json=submit) 226 | try: 227 | if res.json()['code'] == 200: 228 | return [True, "ok"] 229 | else: 230 | return [False, res.json()['message']] 231 | except: 232 | return [False, None] 233 | 234 | def updateServerHTTP(self, serverId: int, isOn: bool): 235 | ServerConfig = self.findEnabledServerConfig(serverId) 236 | if not ServerConfig[0]: 237 | if ServerConfig[1] is None: 238 | return [False, None] 239 | else: 240 | return [False, ServerConfig[1]] 241 | try: 242 | Config = ServerConfig[1] 243 | Config = json.loads(base64.b64decode(Config).decode()) 244 | https = Config['http'] 245 | https['isOn'] = isOn 246 | httpsJSON_bytes = base64.b64encode(json.dumps(https).encode('utf-8')).decode("utf-8") 247 | except: 248 | return [False, None] 249 | submit = { 250 | "serverId": serverId, 251 | "httpJSON": httpsJSON_bytes 252 | } 253 | res = self.post(self.Host + "/ServerService/updateServerHTTP", json=submit) 254 | try: 255 | if res.json()['code'] == 200: 256 | return [True, "ok"] 257 | else: 258 | return [False, res.json()['message']] 259 | except: 260 | return [False, None] 261 | 262 | def findAndInitServerWebConfig(self, serverId: int): 263 | # 初始化web设置 264 | submit = { 265 | "serverId": serverId 266 | } 267 | res = self.post(self.Host + "/ServerService/findAndInitServerWebConfig", json=submit) 268 | try: 269 | if res.json()['code'] == 200: 270 | return [True, "ok"] 271 | else: 272 | return [False, res.json()['message']] 273 | except: 274 | return [False, None] 275 | 276 | def createReverseProxy(self): 277 | # 创建反向代理,返回id 278 | # submit = { 279 | # "reverseProxyId": reverseProxyId, 280 | # "originsJSON": originsJSON_bytes 281 | # } 282 | res = self.post(self.Host + "/ReverseProxyService/createReverseProxy") 283 | print(res.json()) 284 | try: 285 | if res.json()['code'] == 200: 286 | return [True, res.json()['data']['reverseProxyId']] 287 | else: 288 | return [True, res.json()['message']] 289 | except: 290 | return [False, None] 291 | 292 | def createSSLPolicy(self): 293 | # 创建ssl配置 294 | hsts_json = json.dumps( 295 | {"isOn": False, "maxAge": 2592000, "domains": [], "preload": False, "includeSubDomains": False} 296 | ) 297 | hsts_json = base64.b64encode(hsts_json.encode('utf-8')).decode("utf-8") 298 | submit = { 299 | "minVersion": "TLS 1.0", 300 | "http2Enabled": False, 301 | "hstsJSON": hsts_json, 302 | "clientAuthType": 0, 303 | "ocspIsOn": False, 304 | "cipherSuitesIsOn": False, 305 | "clientCACertsJSON": None, 306 | "sslCertsJSON": None 307 | } 308 | res = self.post(self.Host + "/SSLPolicyService/createSSLPolicy", json=submit) 309 | try: 310 | if res.json()['code'] == 200: 311 | return [True, res.json()['data']['sslPolicyId']] 312 | else: 313 | return [True, res.json()['message']] 314 | except: 315 | return [False, None] 316 | -------------------------------------------------------------------------------- /API/UserService.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | 4 | import requests 5 | 6 | import Sql 7 | import Utils 8 | from API import Api 9 | 10 | 11 | class UserService(Api): 12 | def registerUser(self, username, password, mobile, email, fullname, ip, source): 13 | """注册用户""" 14 | submit = { 15 | "username": username, 16 | "password": password, 17 | "mobile": mobile, 18 | "email": email, 19 | "fullname": fullname, 20 | "ip": ip 21 | } 22 | res = self.post(self.Host + "/UserService/registerUser", json=submit) 23 | return res.json() 24 | 25 | def createUser(self, username, password, mobile, email, fullname, ip, source, tel, nodeClusterId): 26 | submit = { 27 | "username": username, 28 | "password": password, 29 | "mobile": mobile, 30 | "email": email, 31 | "fullname": fullname, 32 | "remark": "AMEN-GoEdge-User创建的用户,不可删除,请通过AMEN-GoEdge-User删除,否则AMEN-GoEdge-User异常", 33 | "nodeClusterId": nodeClusterId, 34 | "tel": tel 35 | } 36 | res = self.post(self.Host + "/UserService/createUser", json=submit) 37 | if res is None: 38 | return None 39 | return res.json() 40 | 41 | def loginUser(self, Emysql: Sql.Sql, username: str, password: str): 42 | # 登陆服务官方没有开,自己写,数据库的密码就是md5(原始密码) 43 | # submit = { 44 | # "username": username, 45 | # "password": password, 46 | # } 47 | # # res = requests.post(self.Host + "/UserService/loginUser",json=submit) 48 | # res = self.post(self.Host + "/UserService/loginUser", json=submit) 49 | # # print(res.content.decode()) 50 | # return res.json() 51 | # md = hashlib.md5(password.encode()) 52 | 53 | userinfo = Emysql.fetch_where("edgeUsers", { 54 | "username": username, 55 | # "password": md.hexdigest(), 56 | # "serversEnabled":1, 57 | "state": 1 58 | }) 59 | # isOn字段无用 60 | # state=0被删除 61 | # serversEnabled=0被禁用 62 | if userinfo is not None: 63 | # 查找AccessKey 64 | userinfov2: dict = self.findAllEnabledUserAccessKeys(userinfo['id']) 65 | if userinfov2 is None: 66 | # 生成key 67 | keyinfo = self.createUserAccessKey(Emysql, userinfo['id']) 68 | if not keyinfo[0]: 69 | # 生成失败 70 | return None 71 | # 生成成功 72 | userinfov2 = {'uniqueId': keyinfo[1], 'secret': keyinfo[2]} 73 | elif userinfov2 == "0": 74 | # 异常 75 | return None 76 | # userinfov2 = json.loads(userinfov2) 77 | userinfov3 = { 78 | "uniqueId": userinfov2['uniqueId'], 79 | "secret": userinfov2['secret'] 80 | } 81 | userinfo.update(userinfov3) 82 | return userinfo 83 | else: 84 | return None 85 | 86 | def createUserAccessKey(self, ESql: Sql.Sql, userid: int): 87 | # 理由为None,内部出错 88 | submit = { 89 | "userId": userid, 90 | "description": "AMEN-GoEdge-User创建的Key,不可删除,请通过AMEN-GoEdge-User删除,否则AMEN-GoEdge-User异常" 91 | } 92 | res = self.post(self.Host + "/UserAccessKeyService/createUserAccessKey", json=submit) 93 | if res is None: 94 | return [False, None] 95 | keyinfo = None 96 | try: 97 | if res.json()['code'] != 200: 98 | msg = "code为:{}".format(res.json()['code']) 99 | if "message" in res.json(): 100 | msg = res.json()['message'] 101 | return [False, msg] 102 | keyId = res.json()['data']['userAccessKeyId'] 103 | # 根据keyId查找,无此接口通过数据库查找 104 | keyinfo = ESql.fetch_where("edgeUserAccessKeys", { 105 | "id": keyId 106 | }) 107 | except: 108 | return [False, None] 109 | if keyinfo is None: 110 | return [False, None] 111 | else: 112 | return [True, keyinfo['uniqueId'], keyinfo['secret']] 113 | 114 | def findAllEnabledUserAccessKeys(self, userid: int): 115 | submit = { 116 | "userId": userid, 117 | } 118 | res = self.post(self.Host + "/UserAccessKeyService/findAllEnabledUserAccessKeys", json=submit) 119 | try: 120 | if res is not None: 121 | if len(res.json()['data']['userAccessKeys']) == 0: 122 | return None 123 | else: 124 | return res.json()['data']['userAccessKeys'][0] 125 | else: 126 | return "0" 127 | except: 128 | return None 129 | 130 | def token_find_userid(self, ESql: Sql.Sql, UserToken: str): 131 | userinfo_token = ESql.fetch_where("edgeAPIAccessTokens", { 132 | "token": UserToken 133 | }) 134 | if userinfo_token is None: 135 | return None 136 | return userinfo_token['userId'] 137 | -------------------------------------------------------------------------------- /API/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | import traceback 3 | 4 | import requests 5 | 6 | 7 | class Api: 8 | def __init__(self, Host, Id, Key, Token=None): 9 | self.Token = Token 10 | self.Host = Host 11 | self.Id = Id 12 | self.Key = Key 13 | self.header = { 14 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36", 15 | "Content-Type": "application/json", 16 | "AMEN": "YMWLGZS" 17 | } 18 | session = requests.session() 19 | session.headers['X-Edge-Access-Token'] = Token 20 | session.headers['AMEN'] = "YMWLGZS" 21 | # session.headers['Content-Type'] = "application/json" 22 | session.headers[ 23 | 'User-Agent'] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36" 24 | session.keep_alive = False 25 | self.Session = session 26 | 27 | def get(self, url, headers): 28 | try: 29 | return self.Session.get(url, headers=headers) 30 | except: 31 | return None 32 | 33 | def post(self, url, json=None, headers=None): 34 | try: 35 | return self.Session.post(url, json=json, headers=headers) 36 | except: 37 | traceback.print_exc() 38 | return None 39 | 40 | def getToken(self, type: str = "admin", accessKeyId: str = None, accessKey: str = None): 41 | if accessKey is None or accessKeyId is None: 42 | submit = { 43 | "type": "admin", 44 | "accessKeyId": self.Id, 45 | "accessKey": self.Key 46 | } 47 | else: 48 | submit = { 49 | "type": type, 50 | "accessKeyId": accessKeyId, 51 | "accessKey": accessKey 52 | } 53 | try: 54 | res = requests.post(self.Host + "/APIAccessTokenService/getAPIAccessToken", data=json.dumps(submit), 55 | headers=self.header) 56 | if res.json()['message'] != "ok": 57 | return None 58 | return [res.json()['data']['token'], res.json()['data']['expiresAt']] 59 | except: 60 | # traceback.print_exc() 61 | return None 62 | 63 | def listHTTPAccessLogs(self): 64 | """列出单页访问日志""" 65 | submit = { 66 | "serverId": 2, 67 | "day": "20221121", 68 | "size": 100, 69 | "reverse": True 70 | } 71 | # submit = { 72 | # "node": 1 73 | # } 74 | # res = self.post(self.Host + "/HTTPAccessLogService/listHTTPAccessLogs", json=submit) 75 | res = self.post(self.Host + "/DBService/findAllDBTables") 76 | print(res.text) 77 | # text = res.content.decode() 78 | # return json.loads(text) 79 | return "" 80 | -------------------------------------------------------------------------------- /Config/Config_initialization.json: -------------------------------------------------------------------------------- 1 | { 2 | "Config": { 3 | "whetherVerificationCodeIsEnabl": false 4 | }, 5 | "mysql": { 6 | "host": "127.0.0.1", 7 | "port": 3306, 8 | "user": "goedge", 9 | "password": "goedge", 10 | "database": "goedge" 11 | }, 12 | "EdgeAdmin_mysql": { 13 | "host": "", 14 | "port": 8001, 15 | "user": "", 16 | "password": "", 17 | "database": "" 18 | }, 19 | "Verification": { 20 | "type": 0, 21 | "host": "", 22 | "port": 465, 23 | "ssl": true, 24 | "user": "", 25 | "password": "", 26 | "title": "AMEN-GoEdge-User", 27 | "frequentTime": 60 28 | }, 29 | "API": { 30 | "HOST": "", 31 | "AccessKey ID": "", 32 | "AccessKey Key": "", 33 | "nodeClusterId": 1 34 | }, 35 | "Token": "", 36 | "expiresAt": 0, 37 | "adminId": 2 38 | } -------------------------------------------------------------------------------- /Controller/ConfigService.py: -------------------------------------------------------------------------------- 1 | import API.UserService as UserService 2 | import Sql 3 | import Utils 4 | from fastapi import APIRouter, Path, Body, Depends, Request, Cookie 5 | 6 | from API import ServerService, ReverseProxyService 7 | 8 | app = APIRouter(prefix="/ConfigService", tags=['获取本站配置']) 9 | 10 | 11 | @app.get("/get") 12 | async def get(): 13 | allConfig = Utils.read_config() 14 | Config = allConfig['Config'] 15 | ConfigV2 = allConfig['Verification']['title'] 16 | C = {} 17 | C.update(Config) 18 | C.update({ 19 | "title": ConfigV2 20 | }) 21 | C.update({ 22 | "nodeClusterId": allConfig['API']['nodeClusterId'] 23 | }) 24 | return C 25 | -------------------------------------------------------------------------------- /Controller/HTTPAccessLogService.py: -------------------------------------------------------------------------------- 1 | 2 | import API 3 | import Utils 4 | from fastapi import APIRouter, Path 5 | 6 | app = APIRouter(prefix="/HTTPAccessLogService", tags=['访问日志相关服务']) 7 | 8 | 9 | @app.get("/listHTTPAccessLogs") 10 | async def listHTTPAccessLogs(): 11 | config = Utils.read_config() 12 | api = config['API'] 13 | if config['Token'] == "" or config['Token'] is None: 14 | return {"code": 401, "msg": "server is abnormal"} 15 | GoEdge_Api = API.Api(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 16 | return GoEdge_Api.listHTTPAccessLogs() 17 | -------------------------------------------------------------------------------- /Controller/HTTPWebService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | 4 | from fastapi import APIRouter, Depends, Cookie, Body 5 | import API.ServerService as ServerService 6 | import API.UserService as UserService 7 | import Utils 8 | from API import HTTPWebService 9 | 10 | app = APIRouter(prefix="/HTTPWebService", tags=['WEB服务']) 11 | 12 | @app.post("/updateHTTPWebRedirectToHTTPS") 13 | async def updateHTTPWebRedirectToHTTPS(UserToken: str = Cookie(None), 14 | httpWebId: int = Body(None), 15 | serverId: int = Body(None), 16 | isOn: bool = Body(None), 17 | status: bool = Body(None), 18 | commons: dict = Depends(Utils.init)): 19 | if isOn is None or httpWebId is None or status is None or serverId is None: 20 | return Utils.message(201) 21 | # 此时全部是用户token 22 | config = commons[0] 23 | api = config['API'] 24 | GoEdge_Api = HTTPWebService.HTTPWebService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 25 | GoEdge_Api2 = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 26 | info = GoEdge_Api.updateHTTPWebRedirectToHTTPS(ServerService=GoEdge_Api2,serverId=serverId,httpWebId=httpWebId,isOn=isOn,status=status) 27 | if not info[0]: 28 | if info[1] is None: 29 | return Utils.message(402) 30 | else: 31 | return Utils.message(389, message=info[1]) 32 | return Utils.message(200) 33 | 34 | -------------------------------------------------------------------------------- /Controller/MetricStatService.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import re 4 | import time 5 | import API.MetricStatService as MetricStatService 6 | import API.UserService as UserService 7 | import API.SSLCertService as SSLCertService 8 | import Sql 9 | import Utils 10 | import API.ServerService as ServerService 11 | import API.OriginService as OriginService 12 | import API.ReverseProxyService as ReverseProxyService 13 | from Utils.tencent import Tencent 14 | from fastapi import APIRouter, Path, Body, Depends, Request, Response, Cookie 15 | 16 | # from main import Data_Config 17 | app = APIRouter(prefix="/MetricStatService", tags=['指标']) 18 | 19 | 20 | @app.get("/listMetricStats", tags=['读取']) 21 | async def listMetricStats(UserToken: str = Cookie(None), 22 | commons: dict = Depends(Utils.init)): 23 | config = commons[0] 24 | ESql = commons[2] 25 | UserToken = "YMJwls2Xj7JU7zBjGzlk6qjoZKj6ERkiicHx0TfgQgUcD5ntwQeJcuEziS6wNUAp1F2MkKbkdrAvx7AQYpcTgZOmrNLu2X2zt3ARPMA4OgL4nnsOORHlGJvAd3ak6MxN" 26 | if ESql is None: 27 | return Utils.message(393) 28 | api = config['API'] 29 | # GoEdge_Api = MetricStatService.MetricStatService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 30 | # GoEdge_Api.listMetricStats() 31 | # GoEdge_Api = OriginService.OriginService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 32 | # GoEdge_Api = ReverseProxyService.ReverseProxyService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 33 | # GoEdge_Api = OriginService.OriginService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 34 | GoEdge_Api2 = SSLCertService.SSLCertService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 35 | # GoEdge_Api = SSLCertService.SSLCertService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], 36 | # UserToken) 37 | # serverInfo = GoEdge_Api.findEnabledOrigin(2) 38 | a = """ 39 | [{"isOn": false, "originId": 2}] 40 | """ 41 | # GoEdge_Api.updateReverseProxOrigins(2,json.loads(a),True) 42 | # GoEdge_Api.listSSLCerts(1,0,5) 43 | # GoEdge_Api2.listSSLCerts() 44 | GoEdge_Api2.countAllEnabledServersWithSSLCertId(2) 45 | return 1 -------------------------------------------------------------------------------- /Controller/OriginService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import json 4 | import re 5 | import time 6 | 7 | import API.UserService as UserService 8 | import API.ReverseProxyService as ReverseProxyService 9 | import Sql 10 | import Utils 11 | from API import OriginService as OriginService, ServerService, SSLPolicyService 12 | from Utils.tencent import Tencent 13 | from fastapi import APIRouter, Path, Body, Depends, Request, Response, Cookie 14 | 15 | # from main import Data_Config 16 | app = APIRouter(prefix="/OriginService", tags=['源站管理服务']) 17 | 18 | 19 | @app.post("/updateOrigin", tags=['修改源站']) 20 | async def updateOrigin( 21 | UserToken: str = Cookie(None), 22 | OriginJson: str = Body(None), 23 | isOn: bool = Body(None), 24 | commons: dict = Depends(Utils.init)): 25 | # 单参数时错误,需要加一个 26 | config = commons[0] 27 | api = config['API'] 28 | if OriginJson is None: 29 | return Utils.message(201) 30 | GoEdge_Api = OriginService.OriginService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 31 | OriginJson_bytes = json.loads(base64.b64decode(OriginJson).decode("utf-8")) 32 | originInfo = GoEdge_Api.updateOrigin(OriginJson_bytes) 33 | if not originInfo[0]: 34 | if originInfo[1] is None: 35 | return Utils.message(402) 36 | else: 37 | return Utils.message(389, message=originInfo[1]) 38 | return Utils.message(200) 39 | 40 | 41 | @app.post("/findEnabledOrigin", tags=['获取源站配置']) 42 | async def findEnabledOrigin( 43 | UserToken: str = Cookie(None), 44 | originId: int = Body(None), 45 | isOn: bool = Body(None), 46 | commons: dict = Depends(Utils.init)): 47 | # 源站配置base64传入解码后再传入类工具,类工具再编码 48 | # 单int参数需要带其他类型参数,否则异常,fastapibug 49 | config = commons[0] 50 | api = config['API'] 51 | GoEdge_Api = OriginService.OriginService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 52 | if originId is None: 53 | return Utils.message(201) 54 | originInfo = GoEdge_Api.findEnabledOrigin(originId) 55 | if not originInfo[0]: 56 | if originInfo[1] is None: 57 | return Utils.message(402) 58 | else: 59 | return Utils.message(389, message=originInfo[1]) 60 | return Utils.message(200, data=originInfo[1]) 61 | 62 | 63 | @app.post("/deleteOrigin") 64 | async def deleteOrigin( 65 | UserToken: str = Cookie(None), 66 | originId: int = Body(None), 67 | reverseProxyId: int = Body(None), 68 | isOn: bool = Body(None), 69 | originJson: str = Body(None), 70 | commons: dict = Depends(Utils.init)): 71 | # isOn是否主源站,originJson primaryOrigins等字段更新,通过这个字段进行引导源站 72 | config = commons[0] 73 | api = config['API'] 74 | GoEdge_Api = OriginService.OriginService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 75 | GoEdge_Api2 = ReverseProxyService.ReverseProxyService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], 76 | UserToken) 77 | if originId is None: 78 | return Utils.message(201) 79 | if reverseProxyId is None: 80 | return Utils.message(201) 81 | if isOn is None: 82 | return Utils.message(201) 83 | if originJson is None: 84 | return Utils.message(201) 85 | OriginJson_bytes = json.loads(base64.b64decode(originJson).decode("utf-8")) 86 | ReverseProxOriginsInfo = GoEdge_Api.deleteOrigin(reverseProxyId=reverseProxyId, GoEdgeApi=GoEdge_Api2, 87 | originId=originId, originJson=OriginJson_bytes, Primary=isOn) 88 | if not ReverseProxOriginsInfo[0]: 89 | if ReverseProxOriginsInfo[1] is None: 90 | return Utils.message(402) 91 | else: 92 | return Utils.message(389, message=ReverseProxOriginsInfo[1]) 93 | return Utils.message(200) 94 | 95 | 96 | @app.post("/createOrigin") 97 | async def createOrigin( 98 | UserToken: str = Cookie(None), 99 | reverseProxyId: int = Body(None), 100 | originJson: str = Body(None), 101 | isOn: bool = Body(None), 102 | commons: dict = Depends(Utils.init)): 103 | # 单int参数需要带其他类型参数,否则异常,fastapibug 104 | # isOn为是否主源站,true主,false备 105 | config = commons[0] 106 | api = config['API'] 107 | GoEdge_Api = OriginService.OriginService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 108 | if originJson is None: 109 | return Utils.message(201) 110 | if isOn is None: 111 | return Utils.message(201) 112 | if reverseProxyId is None: 113 | return Utils.message(201) 114 | OriginJson_bytes = json.loads(base64.b64decode(originJson).decode("utf-8")) 115 | originInfo = GoEdge_Api.createOrigin(OriginJson_bytes) 116 | 117 | if not originInfo[0]: 118 | if originInfo[1] is None: 119 | return Utils.message(402) 120 | else: 121 | return Utils.message(389, message=originInfo[1]) 122 | # 创建成功,放入反代理 123 | sourceData = OriginJson_bytes['sourceData'] 124 | GoEdge_Api2 = ReverseProxyService.ReverseProxyService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], 125 | UserToken) 126 | if originInfo[1] is None: 127 | return Utils.message(402) 128 | sourceData.append({"isOn": True, "originId": originInfo[1]}) 129 | ReverseProxOriginsInfo = GoEdge_Api2.updateReverseProxOrigins(reverseProxyId=reverseProxyId, originsJSON=sourceData, 130 | Primary=isOn) 131 | if not ReverseProxOriginsInfo[0]: 132 | if ReverseProxOriginsInfo[1] is None: 133 | return Utils.message(402) 134 | else: 135 | return Utils.message(389, message=ReverseProxOriginsInfo[1]) 136 | return Utils.message(200) 137 | 138 | 139 | @app.post("/updateOriginSSL") 140 | async def updateOriginSSL( 141 | UserToken: str = Cookie(None), 142 | serverId: int = Body(None), 143 | certId: int = Body(None), 144 | commons: dict = Depends(Utils.init)): 145 | config = commons[0] 146 | api = config['API'] 147 | GoEdge_Api = SSLPolicyService.SSLPolicyService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 148 | GoEdge_Api2 = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 149 | serverInfo = GoEdge_Api2.findEnabledServerConfig(serverId) 150 | if not serverInfo[0]: 151 | if serverInfo[1] is None: 152 | return Utils.message(402) 153 | else: 154 | return Utils.message(389, message=serverInfo[1]) 155 | serverInfoConfig = json.loads(base64.b64decode(serverInfo[1]).decode('utf-8')) 156 | sslPolicyId = serverInfoConfig['https']['sslPolicyRef']['sslPolicyId'] 157 | hsts = serverInfoConfig['https']['sslPolicy']['hsts'] 158 | http2Enabled = serverInfoConfig['https']['sslPolicy']['http2Enabled'] 159 | ocspIsOn = serverInfoConfig['https']['sslPolicy']['ocspIsOn'] 160 | minVersion = serverInfoConfig['https']['sslPolicy']['minVersion'] 161 | if hsts is None: 162 | hsts = { 163 | "isOn":False, 164 | "maxAge":1, 165 | "preload":False, 166 | "includeSubDomains":False 167 | } 168 | sslInfo = GoEdge_Api.updateSSLPolicy(sslPolicyId=sslPolicyId,ocsp=ocspIsOn,sslID=certId,hsts=hsts,http2Enabled=http2Enabled,minVersion=minVersion) 169 | if not sslInfo[0]: 170 | if sslInfo[1] is None: 171 | return Utils.message(402) 172 | else: 173 | return Utils.message(389, message=sslInfo[1]) 174 | return Utils.message(200) 175 | 176 | -------------------------------------------------------------------------------- /Controller/SSLCertService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import datetime 3 | import json 4 | import time 5 | 6 | from OpenSSL import crypto 7 | from cryptography import x509 8 | import cryptography 9 | from cryptography.hazmat.backends import default_backend 10 | 11 | from fastapi import APIRouter, Depends, Cookie, Body 12 | import API.ServerService as ServerService 13 | import API.UserService as UserService 14 | import API.SSLCertService as SSLCertService 15 | import Utils 16 | 17 | app = APIRouter(prefix="/SSLCertService", tags=['SSL服务']) 18 | 19 | 20 | # 将userid放进cookie httponly中 21 | 22 | @app.post("/createSSLCert") 23 | async def createSSLCert(UserToken: str = Cookie(None), 24 | name: str = Body(None), 25 | isCA: bool = Body(None), 26 | certData: str = Body(None), 27 | keyData: str = Body(None), 28 | commons: dict = Depends(Utils.init)): 29 | # 此时全部是用户token 30 | config = commons[0] 31 | ESql = commons[2] 32 | if ESql is None: 33 | return Utils.message(393) 34 | if name is None: 35 | return Utils.message(201) 36 | if isCA is None: 37 | return Utils.message(201) 38 | if certData is None: 39 | return Utils.message(201) 40 | # if keyData is None: 41 | # return Utils.message(201) 42 | api = config['API'] 43 | GoEdge_Api = SSLCertService.SSLCertService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 44 | 45 | c = base64.b64decode(certData) 46 | try: 47 | cert1 = crypto.load_certificate(crypto.FILETYPE_PEM, c) 48 | # cert.get_issuer().CN 颁发机构 49 | # cert.get_subject().CN 获得通用名称 50 | # cert.get_serial_number() 证书SN 51 | # cert.subject_name_hash()证书hash 52 | # cert.get_notBefore() 证书生效 b'20221006000000Z' 53 | # cert.get_notBefore() 证书失效 b'20230104235959Z' 54 | # print(cert.get_subject()) 55 | cert2 = x509.load_pem_x509_certificate(c, default_backend()) 56 | ski = cert2.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME) 57 | DNSNameS = ski.value.get_values_for_type(cryptography.x509.DNSName) 58 | issuer_type = cert1.get_issuer().CN 59 | issuer_author = cert1.get_issuer().O 60 | Atime = int(datetime.datetime.strptime(cert1.get_notBefore().decode()[0:-1], '%Y%m%d%H%M%S').timestamp()) 61 | Etime = int(datetime.datetime.strptime(cert1.get_notAfter().decode()[0:-1], '%Y%m%d%H%M%S').timestamp()) 62 | except: 63 | return Utils.message(389, message="证书不正确") 64 | SSLinfo = GoEdge_Api.createSSLCert(name=name, isCA=isCA, certData=certData, keyData=keyData, timeBeginAt=Atime, 65 | timeEndAt=Etime, dnsNames=DNSNameS, issuer_type=issuer_type, 66 | issuer_author=issuer_author) 67 | if not SSLinfo[0]: 68 | if SSLinfo[1] is None: 69 | return Utils.message(402) 70 | else: 71 | return Utils.message(389, message=SSLinfo[1]) 72 | return Utils.message(200) 73 | 74 | 75 | @app.post("/listSSLCerts") 76 | async def listSSLCerts(UserToken: str = Cookie(None), 77 | UserId: int = Cookie(None), 78 | offset: int = Body(), 79 | size: int = Body(), 80 | keyword: str = Body(None), 81 | commons: dict = Depends(Utils.init)): 82 | config = commons[0] 83 | api = config['API'] 84 | GoEdge_Api = SSLCertService.SSLCertService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 85 | listSSL = GoEdge_Api.listSSLCerts(UserId, offset, size, keyword) 86 | countSSL = GoEdge_Api.countSSLCerts(UserId, keyword) 87 | if not countSSL[0]: 88 | if countSSL[1] is None: 89 | return Utils.message(402) 90 | else: 91 | return Utils.message(389, message=countSSL[1]) 92 | if not listSSL[0]: 93 | if listSSL[1] is None: 94 | return Utils.message(402) 95 | else: 96 | return Utils.message(389, message=listSSL[1]) 97 | try: 98 | listSSL_V2 = json.loads(base64.b64decode(listSSL[1]).decode()) 99 | for i,v in enumerate(listSSL_V2): 100 | countInfo = GoEdge_Api.countAllEnabledServersWithSSLCertId(int(v['id'])) 101 | if not countInfo[0]: 102 | if countInfo[1] is None: 103 | return Utils.message(402) 104 | else: 105 | return Utils.message(389, message=countInfo[1]) 106 | listSSL_V2[i]['count'] = countInfo[1] 107 | listSSL_V2 = base64.b64encode(json.dumps(listSSL_V2).encode('utf-8')).decode("utf-8") 108 | except: 109 | return Utils.message(402) 110 | ret = { 111 | "count": countSSL[1], 112 | "data": listSSL_V2 113 | } 114 | return Utils.message(200, data=ret) 115 | 116 | 117 | @app.post("/deleteSSLCert") 118 | async def deleteSSLCert(UserToken: str = Cookie(None), 119 | sslCertId: int = Body(None), 120 | isOn: bool = Body(None), 121 | commons: dict = Depends(Utils.init)): 122 | config = commons[0] 123 | api = config['API'] 124 | if sslCertId is None: 125 | return Utils.message(201) 126 | GoEdge_Api = SSLCertService.SSLCertService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 127 | sslInfo = GoEdge_Api.deleteSSLCert(sslCertId) 128 | if not sslInfo[0]: 129 | if sslInfo[1] is None: 130 | return Utils.message(402) 131 | else: 132 | return Utils.message(389, message=sslInfo[1]) 133 | return Utils.message(200) 134 | -------------------------------------------------------------------------------- /Controller/SSLPolicyService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | 4 | from fastapi import APIRouter, Depends, Cookie, Body 5 | import API.ServerService as ServerService 6 | import API.UserService as UserService 7 | import Utils 8 | from API import SSLPolicyService 9 | 10 | app = APIRouter(prefix="/SSLPolicyService", tags=['SSL服务']) 11 | 12 | @app.post("/updateSSLPolicy") 13 | async def updateSSLPolicy(UserToken: str = Cookie(None), 14 | sslPolicyId: int = Body(None), 15 | ocsp: bool = Body(None), 16 | sslID: int = Body(None), 17 | hsts: str = Body(None), 18 | minVersion: str = Body(None), 19 | http2Enabled: bool = Body(None), 20 | commons: dict = Depends(Utils.init)): 21 | try: 22 | hsts_bytes = json.loads(base64.b64decode(hsts).decode()) 23 | except: 24 | return Utils.message(402) 25 | if sslPolicyId is None or ocsp is None or sslID is None or http2Enabled is None or minVersion is None or hsts is None: 26 | return Utils.message(201) 27 | # 此时全部是用户token 28 | config = commons[0] 29 | api = config['API'] 30 | GoEdge_Api = SSLPolicyService.SSLPolicyService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 31 | info = GoEdge_Api.updateSSLPolicy(sslPolicyId=sslPolicyId,ocsp=ocsp,sslID=sslID,hsts=hsts_bytes,http2Enabled=http2Enabled,minVersion=minVersion) 32 | if not info[0]: 33 | if info[1] is None: 34 | return Utils.message(402) 35 | else: 36 | return Utils.message(389, message=info[1]) 37 | return Utils.message(200) 38 | 39 | -------------------------------------------------------------------------------- /Controller/ServerService.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | 4 | from fastapi import APIRouter, Depends, Cookie, Body 5 | import API.ServerService as ServerService 6 | import API.UserService as UserService 7 | import Utils 8 | 9 | app = APIRouter(prefix="/ServerService", tags=['网站服务']) 10 | 11 | 12 | # 将userid放进cookie httponly中 13 | 14 | @app.post("/createServer") 15 | async def createServer(UserToken: str = Cookie(None), 16 | UserId: int = Cookie(None), 17 | clusterId: int = Body(), 18 | domian: str = Body(), 19 | name: str = Body(), 20 | commons: dict = Depends(Utils.init)): 21 | # 此时全部是用户token 22 | config = commons[0] 23 | ESql = commons[2] 24 | if ESql is None: 25 | return Utils.message(393) 26 | api = config['API'] 27 | # 用户管理还是需要管理token的 28 | GoEdge_Api = UserService.UserService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 29 | # userId = GoEdge_Api.token_find_userid(ESql, UserToken) 30 | # if userId is None: 31 | # return Utils.message(388) 32 | if clusterId != api['nodeClusterId']: 33 | return Utils.message(387) 34 | GoEdge_Api = ServerService.ServerService(api['HOST'], None, None, UserToken) 35 | info = GoEdge_Api.createServer(clusterId, userId=UserId, name=name, domain=domian) 36 | if not info[0]: 37 | if info[1] is None: 38 | return Utils.message(402) 39 | else: 40 | return Utils.message(389, message=info[1]) 41 | return Utils.message(200, info[1]) 42 | 43 | 44 | @app.get("/findAllUserServers") 45 | async def findAllUserServers(UserToken: str = Cookie(None), 46 | UserId: int = Cookie(None), 47 | commons: dict = Depends(Utils.init) 48 | ): 49 | # 此时全部是用户token 50 | config = commons[0] 51 | ESql = commons[2] 52 | api = config['API'] 53 | GoEdge_Api = ServerService.ServerService(api['HOST'], None, None, UserToken) 54 | # GoEdge_UserService = UserService.UserService(api['HOST'], None, None, UserToken) 55 | # userId = GoEdge_UserService.token_find_userid(ESql, UserToken) 56 | domainList = GoEdge_Api.findAllUserServers(UserId) 57 | if not domainList[0]: 58 | if domainList[1] is None: 59 | return Utils.message(402) 60 | else: 61 | return Utils.message(389, message=domainList[1]) 62 | return Utils.message(200, domainList[1]) 63 | 64 | 65 | @app.post("/updateServerIsOn") 66 | async def updateServerIsOn(UserToken: str = Cookie(None), 67 | UserId: int = Cookie(None), 68 | serverId: int = Body(None), 69 | isOn: bool = Body(None), 70 | commons: dict = Depends(Utils.init)): 71 | # 此时全部是用户token 72 | config = commons[0] 73 | ESql = commons[2] 74 | api = config['API'] 75 | GoEdge_Api = ServerService.ServerService(api['HOST'], None, None, UserToken) 76 | updateServer = GoEdge_Api.updateServerIsOn(serverId, isOn) 77 | if not updateServer[0]: 78 | if updateServer[1] is None: 79 | return Utils.message(402) 80 | else: 81 | return Utils.message(389, message=updateServer[1]) 82 | return Utils.message(200) 83 | 84 | 85 | @app.post("/findEnabledServerConfig") 86 | async def findEnabledServerConfig(UserToken: str = Cookie(None), 87 | serverId: int = Body(None), 88 | isOn: bool = Body(None), 89 | commons: dict = Depends(Utils.init)): 90 | # 单独int参数时需要带上其他类型参数,不然类型错误,fastapibug 91 | config = commons[0] 92 | api = config['API'] 93 | GoEdge_Api = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 94 | if serverId is None: 95 | return Utils.message(201) 96 | serverInfo = GoEdge_Api.findEnabledServerConfig(serverId) 97 | if not serverInfo[0]: 98 | if serverInfo[1] is None: 99 | return Utils.message(402) 100 | else: 101 | return Utils.message(389, message=serverInfo[1]) 102 | return Utils.message(200, serverInfo[1]) 103 | 104 | 105 | @app.post("/deleteServer") 106 | async def deleteServer(UserToken: str = Cookie(None), 107 | serverId: int = Body(None), 108 | isOn: bool = Body(None), 109 | commons: dict = Depends(Utils.init)): 110 | # 单独int参数时需要带上其他类型参数,不然类型错误,fastapibug 111 | config = commons[0] 112 | api = config['API'] 113 | GoEdge_Api = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 114 | if serverId is None: 115 | return Utils.message(201) 116 | # 检查是否被禁用,无禁用不可删除 117 | ServerInfo1 = GoEdge_Api.findEnabledServer(serverId) 118 | if not ServerInfo1[0]: 119 | if ServerInfo1[1] is None: 120 | return Utils.message(402) 121 | else: 122 | return Utils.message(389, message=ServerInfo1[1]) 123 | if ServerInfo1[1] == {}: 124 | return Utils.message(402) 125 | if "isOn" in ServerInfo1[1] and ServerInfo1[1]['isOn']: 126 | return Utils.message(386) 127 | serverInfo = GoEdge_Api.deleteServer(serverId) 128 | if not serverInfo[0]: 129 | if serverInfo[1] is None: 130 | return Utils.message(402) 131 | else: 132 | return Utils.message(389, message=serverInfo[1]) 133 | return Utils.message(200) 134 | 135 | 136 | @app.post("/updateServerReverseProxy") 137 | async def updateServerReverseProxy(UserToken: str = Cookie(None), 138 | serverId: int = Body(None), 139 | reverseProxyJSON: str = Body(None), 140 | commons: dict = Depends(Utils.init)): 141 | # 源站配置base64传入解码后再传入类工具,类工具再编码 142 | config = commons[0] 143 | api = config['API'] 144 | GoEdge_Api = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 145 | if serverId is None: 146 | return Utils.message(201) 147 | reverseProxyJSON_bytes = json.loads(base64.b64decode(reverseProxyJSON).decode("utf-8")) 148 | serverInfo = GoEdge_Api.updateServerReverseProxy(serverId, reverseProxyJSON_bytes) 149 | if not serverInfo[0]: 150 | if serverInfo[1] is None: 151 | return Utils.message(402) 152 | else: 153 | return Utils.message(389, message=serverInfo[1]) 154 | return Utils.message(200) 155 | 156 | 157 | @app.post("/updateServerHTTPS") 158 | async def updateServerHTTPS(UserToken: str = Cookie(None), 159 | serverId: int = Body(None), 160 | isOn: bool = Body(None), 161 | commons: dict = Depends(Utils.init)): 162 | if isOn is None or serverId is None: 163 | return Utils.message(201) 164 | config = commons[0] 165 | api = config['API'] 166 | GoEdge_Api = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 167 | HTTPSinfo = GoEdge_Api.updateServerHTTPS(serverId, isOn) 168 | if not HTTPSinfo[0]: 169 | if HTTPSinfo[1] is None: 170 | return Utils.message(402) 171 | else: 172 | return Utils.message(389, message=HTTPSinfo[1]) 173 | return Utils.message(200) 174 | 175 | @app.post("/updateServerHTTP") 176 | async def updateServerHTTP(UserToken: str = Cookie(None), 177 | serverId: int = Body(None), 178 | isOn: bool = Body(None), 179 | commons: dict = Depends(Utils.init)): 180 | if isOn is None or serverId is None: 181 | return Utils.message(201) 182 | config = commons[0] 183 | api = config['API'] 184 | GoEdge_Api = ServerService.ServerService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], UserToken) 185 | HTTPSinfo = GoEdge_Api.updateServerHTTP(serverId, isOn) 186 | if not HTTPSinfo[0]: 187 | if HTTPSinfo[1] is None: 188 | return Utils.message(402) 189 | else: 190 | return Utils.message(389, message=HTTPSinfo[1]) 191 | return Utils.message(200) 192 | -------------------------------------------------------------------------------- /Controller/UserService.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import re 4 | import time 5 | 6 | import API.UserService as UserService 7 | import Sql 8 | import Utils 9 | from Utils.tencent import Tencent 10 | from fastapi import APIRouter, Path, Body, Depends, Request, Response, Cookie 11 | 12 | # from main import Data_Config 13 | app = APIRouter(prefix="/UserService", tags=['用户相关服务']) 14 | 15 | 16 | @app.post("/registerUser", tags=['注册用户']) 17 | async def registerUser(request1: Request, 18 | username: str = Body( 19 | description='必须是正确的手机号或邮箱,且必填'), 20 | # username: str = Body(regex=r"^[a-zA-Z][a-zA-Z0-9_]{4,15}$", min_length=5, 21 | # max_length=13, description='用户必填,最小5个字符,最大13个字符,且只能大写/小写字母,数字和特殊符号'), 22 | password: str = Body(regex=r"^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).*$", min_length=5, 23 | max_length=13, description='密码必填,最小5个字符,最大13个字符,且只能大写/小写字母,数字和特殊符号'), 24 | # mobile: str = Body( 25 | # regex='^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$', 26 | # description='必须是正确的手机号,且必填'), 27 | # 先暂时参数邮箱保留,后期手机验证码写上后,根据用户名决定不填,手机注册邮箱空,手机号存在。邮箱注册手机空,邮箱号存在 28 | # email: str = Body(regex=r'([\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+)', description='必须正确的邮箱,且必填'), 29 | code: str = Body(), 30 | ticket: str = Body(None), 31 | randstr: str = Body(None), 32 | commons: dict = Depends(Utils.init)): 33 | # 初始化 34 | email = None 35 | mobile = None 36 | # 用户名用手机或邮箱 37 | ret = re.match('^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$', username) 38 | type = 0 # 0是邮箱 1是手机 39 | if not ret: 40 | ret = re.match(r'([\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+)', username) 41 | if not ret: 42 | return Utils.message(395) 43 | else: 44 | email = username 45 | type = 0 46 | else: 47 | mobile = username 48 | type = 1 49 | if type == 1: 50 | return Utils.message(202) 51 | 52 | config = commons[0] 53 | ip = request1.client.host 54 | # 验证腾讯滑块验证码 55 | if config['Config']['whetherVerificationCodeIsEnabl']: 56 | if ticket is None or randstr is None: 57 | return Utils.message(396) 58 | T = Tencent() 59 | flag = T.check_ticket(ticket, randstr) 60 | if flag == -1: 61 | return Utils.message(394) 62 | if flag == 0: 63 | return Utils.message(396) 64 | # 滑块结束 65 | api = config['API'] 66 | Verification = config['Verification'] 67 | # 连接数据库查询验证码 68 | VSql: Sql.Sql = commons[1] 69 | if VSql is None: 70 | return Utils.message(401) 71 | code_db = VSql.fetch_where("edge_code", { 72 | "number": email 73 | }, additionalContent="order by `timeStamp` desc") 74 | # 查询验证码是否过期 75 | millis = int(round(time.time() * 1000)) 76 | if code_db is not None: 77 | if millis - int(code_db['timeStamp']) > Verification['frequentTime'] * 1000: 78 | return Utils.message(397) 79 | else: 80 | # 查询验证码是否正确 81 | if code.lower() != str(code_db['code']).lower(): 82 | return Utils.message(397) 83 | else: 84 | return Utils.message(397) 85 | 86 | # 验证成功,删除此验证码 87 | VSql.delete("edge_code", { 88 | "id": code_db['id'] 89 | }) 90 | GoEdge_Api = UserService.UserService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 91 | # res = GoEdge_Api.createUser(username, password, mobile, email, username, ip, None, mobile, 1) 92 | res = GoEdge_Api.createUser(username, password, mobile, email, username, ip, None, mobile, api['nodeClusterId']) 93 | if res is None: 94 | return Utils.message(402) 95 | return res 96 | 97 | 98 | @app.post("/loginUser") 99 | async def loginUser( 100 | response: Response, 101 | username: str = Body(), 102 | password: str = Body(), 103 | ticket: str = Body(None), 104 | randstr: str = Body(None), 105 | commons: dict = Depends(Utils.init)): 106 | config = commons[0] 107 | # 判断参数 108 | if username is None or username == "" or password is None or password == "": 109 | return Utils.message(390) 110 | # 验证腾讯滑块验证码 111 | if config['Config']['whetherVerificationCodeIsEnabl']: 112 | if ticket is None or randstr is None: 113 | return Utils.message(396) 114 | T = Tencent() 115 | flag = T.check_ticket(ticket, randstr) 116 | if flag == -1: 117 | return Utils.message(394) 118 | if flag == 0: 119 | return Utils.message(396) 120 | # 滑块结束 121 | api = config['API'] 122 | ESql = commons[2] 123 | if ESql is None: 124 | return Utils.message(393) 125 | GoEdge_Api = UserService.UserService(api['HOST'], api['AccessKey ID'], api['AccessKey Key'], config['Token']) 126 | res = GoEdge_Api.loginUser(ESql, username, password) 127 | if res is None: 128 | return Utils.message(392) 129 | md = hashlib.md5(password.encode()) 130 | if res['password'] != md.hexdigest(): 131 | return Utils.message(390) 132 | if res['serversEnabled'] == 0: 133 | return Utils.message(391) 134 | # 获取成功,获取token 135 | userToken = GoEdge_Api.getToken("user", res['uniqueId'], res['secret']) 136 | if userToken is None: 137 | return Utils.message(402) 138 | # 置入cookie 139 | # 置入cookie 140 | # response.set_cookie(key="UserToken",value=userToken[0]) 141 | response.set_cookie(key="UserToken", value=userToken[0], max_age=userToken[1], httponly=True) 142 | response.set_cookie(key="UserId", value=res['id'], max_age=userToken[1], httponly=True) 143 | return Utils.message(200, { 144 | "Token": userToken[0], 145 | "Time": userToken[1] 146 | }) 147 | 148 | @app.post("/logout") 149 | async def logout(response: Response, 150 | UserToken: str = Cookie(None), 151 | UserId: int = Cookie(None), ): 152 | response.delete_cookie(key="UserToken", httponly=True) 153 | response.delete_cookie(key="UserId", httponly=True) 154 | 155 | return Utils.message(200) 156 | -------------------------------------------------------------------------------- /Controller/Verification.py: -------------------------------------------------------------------------------- 1 | import re 2 | import time 3 | 4 | from email.header import Header 5 | from email.mime.text import MIMEText 6 | 7 | 8 | import Utils 9 | from fastapi import APIRouter, Path, Depends, Body 10 | import smtplib 11 | 12 | from Utils.tencent import Tencent 13 | 14 | app = APIRouter(prefix="/Verification", tags=['验证服务']) 15 | 16 | 17 | @app.post("/getcode") 18 | async def getcode(number: str = Body(description='必须正确的邮箱,且必填'), 19 | # 注册必须滑块,不可关闭 20 | ticket: str = Body(), 21 | randstr: str = Body(), 22 | commons: dict = Depends(Utils.init)): 23 | # if type != 0 and type != 1: 24 | # return Utils.message(201) 25 | # if type == 1: 26 | # return Utils.message(202) 27 | 28 | # 用户名用手机或邮箱 29 | ret = re.match('^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$', number) 30 | type = 0 # 0是邮箱 1是手机 31 | if not ret: 32 | ret = re.match(r'([\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+)', number) 33 | if not ret: 34 | return Utils.message(395) 35 | else: 36 | type = 0 37 | else: 38 | type = 1 39 | if type == 1: 40 | return Utils.message(202) 41 | # 参数正确,开始滑块 42 | 43 | # 验证腾讯滑块验证码 44 | T = Tencent() 45 | flag = T.check_ticket(ticket, randstr) 46 | if flag == -1: 47 | return Utils.message(394) 48 | if flag == 0: 49 | return Utils.message(396) 50 | # 滑块结束 51 | 52 | # 查询数据库寻找title,暂时查询不到,用配置文件替代 53 | config = commons[0] 54 | Verification = config['Verification'] 55 | mysql = config['mysql'] 56 | host = Verification['host'] 57 | port = Verification['port'] 58 | uer_name = Verification['user'] 59 | password = Verification['password'] 60 | # 写入验证码 61 | yzm = Utils.generate_random_str(6) 62 | # VSql = Sql.Sql(mysql['host'], mysql['port'], mysql['user'], mysql['password'], mysql['database']) 63 | VSql = commons[1] 64 | if VSql is None: 65 | return Utils.message(401) 66 | millis = int(round(time.time() * 1000)) 67 | toBeWritten = { 68 | "type": 0, 69 | "number": number, 70 | "code": yzm, 71 | "timeStamp": str(millis) 72 | } 73 | # 先查询是否频繁 74 | code_db = VSql.fetch_where("edge_code", { 75 | "number": number 76 | }, additionalContent="order by `timeStamp` desc") 77 | if code_db is not None: 78 | if millis - int(code_db['timeStamp']) < Verification['frequentTime'] * 1000: 79 | return Utils.message(398) 80 | 81 | flag = VSql.insert("edge_code", toBeWritten) 82 | if not flag: 83 | return Utils.message(4012) 84 | msg = "标题不是验证码。\n您正在注册 {} 的验证码,验证码为:{}".format(Verification['title'], yzm) 85 | msg = MIMEText(msg, 'plain', 'utf-8') 86 | # 放入邮件主题 随机标题,防止拦截,特别是qq邮箱 87 | msg['Subject'] = Header("{}注册验证码 {}".format(Verification['title'], Utils.generate_random_str(6)), 'utf-8') 88 | 89 | # 放入发件人 90 | msg['From'] = uer_name 91 | 92 | try: 93 | '''2、创建 SMTP 对象 94 | SMTP 协议是由源服务器到目的地服务器传送邮件的一组规则。(可简单理解为:我们需要通过SMTP指定一个服务器,这样才能把邮件送到另一个服务器)''' 95 | 96 | if Verification['ssl']: 97 | smtpObj = smtplib.SMTP_SSL(host, port) 98 | else: 99 | smtpObj = smtplib.SMTP(host, port) 100 | '''3、连接(connect)指定服务器''' 101 | smtpObj.connect(host, port) 102 | '''4、登录,需要:登录邮箱和授权码''' 103 | smtpObj.login(uer_name, password) 104 | '''5、发邮件。 105 | 参数:发件人,收件人和邮件内容 106 | msg.as_string()是一个字符串类型:as_string()是将发送的信息msg变为字符串类型。 107 | ''' 108 | smtpObj.sendmail(uer_name, number, msg.as_string()) 109 | '''6、退出''' 110 | smtpObj.quit() 111 | except: 112 | # traceback.print_exc() 113 | # 邮箱发送失败。删除记录 114 | VSql.delete("edge_code", toBeWritten) 115 | return Utils.message(399) 116 | return Utils.message(200) 117 | 118 | -------------------------------------------------------------------------------- /ErrCode/__init__.py: -------------------------------------------------------------------------------- 1 | ErrCode = { 2 | # 无400,402要么是管理员token错误,要么是api接口错误,要么是程序错误 3 | "200": "ok", 4 | "201": "参数错误,漏写参数或参数值不正确", 5 | "202": "该功能可能未开启", 6 | "399": "邮箱连接失败,发送失败", 7 | "398": "请勿频繁发送验证码", 8 | "397": "验证码错误或失效", 9 | "396": "滑块错误或未滑块", 10 | "394": "滑块接口失效", 11 | "395": "用户名必须是手机号或邮箱", 12 | "393": "EdgeAdmin_mysql数据库连接失败,请通知网站管理员查看日志", 13 | "392": "用户不存在", 14 | "391": "用户已被封禁", 15 | "390": "账号密码错误", 16 | "389": "自定义出错", 17 | "388": "你还没有登陆或登录态失效,请重新登陆", 18 | "387": "此套餐不属于你或暂不支持此套餐", 19 | "386": "服务器未禁用,不可删除", 20 | "401": "数据库连接失败,请通知网站管理员查看日志", 21 | "402": "一般是你操作的对象不属于你,你没有权限。或内部处理错误,请联系客服咨询", 22 | "4012": "数据库连接失败或数据库语句执行失败,请通知网站管理员查看日志" 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 AMEN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AMEN-GoEdge-User后端 2 | 3 | ## 这是什么项目? 4 | 这是 GoEdge CDN的用户端 5 | 6 | ## 为什么要创作,GoEdge没有用户端吗? 7 | 有,但是是商业版才可用,也就是需要💴 8 | 9 | ## 有哪些功能 10 | 1.注册 11 | 2.登录 12 | 3.验证码 13 | 4.网站管理 14 | 5.添加/删除网站 15 | 6.https设置(http2/强制https/https证书/hsts/开关http|s/ocps/tls) 16 | 7.源站管理(支持端口/支持域名/支持ip/支持https/支持https证书/添加/删除) 17 | 8.证书管理(一键绑定/添加/删除) 18 | 19 | ## 后续会增加功能吗? 20 | 会的,会逐渐加上:waf规则自定义/webp/缓存/防盗链/Websocket/地区限制/URL跳转/网站统计(流量/请求数)/拦截日志 21 | 22 | ## 只有后端,没有前端吗? 23 | 有的,项目在 https://github.com/2331892928/AMEN-GoEdge-User_frontEnd 24 | 25 | ## 如何升级? 26 | 后期会给升级方法,基本覆盖即可 27 | 28 | ## 如何安装后端? 29 | ### 导入sql 30 | 将此项目根目录下的 edge_code.sql导入即可 31 | ### GoEdgeAdmin设置 32 | 1.系统设置->高级设置->API节点->主节点详情->修改节点->更多选项->是否开启HTTP API端口(开启)->HTTP API监听端口(自行设置)->启用当前节点(打开) 33 | 2.系统用户->随便一个->详情->accesskeys—>按需创建添加,记住即可后边用 34 | ### 填写配置文件 35 | 没有配置文件? 36 | 将项目运行一遍,即可在项目根目录下的Config文件夹里 37 | 配置文件名:Config.json 38 | 配置解释: 39 | ```json 40 | { 41 | "Config": { 42 | "whetherVerificationCodeIsEnabl": false # 是否开启腾讯验证码,验证码界面有: 43 | # 注册/登录/发送验证码。关闭后只有以下生效:发送验证码 44 | }, 45 | "mysql": {# 本项目sql配置 46 | "host": "127.0.0.1", # mysql主机名 47 | "port": 3306,# mysql端口 48 | "user": "goedge",# mysql用户 49 | "password": "goedge",# mysql密码 50 | "database": "goedge"# mysql数据库 51 | }, 52 | "EdgeAdmin_mysql": { # GoEdgeAdmin的数据库 53 | "host": "", 54 | "port": 8001, 55 | "user": "", 56 | "password": "", 57 | "database": "" 58 | }, 59 | "Verification": { # 发送验证码配置,邮箱配置 60 | "type": 0, 61 | "host": "", # 邮箱主机名 62 | "port": 465, #邮箱端口 63 | "ssl": true, #邮箱是否启用ssl 64 | "user": "", #邮箱用户名 65 | "password": "", #邮箱密码 66 | "title": "AMEN-GoEdge-User", #邮箱主题/网站主题 67 | "frequentTime": 60 # 验证码多久失效/内频繁 秒单位,如60秒: 68 | # 60秒内生效,60秒内不可再次发送否则频繁 69 | }, 70 | "API": { # 安装第二步GoEdgeAdmin设置的内容 nodeClusterId为注册用户默认绑定的集群id 71 | "HOST": "", 72 | "AccessKey ID": "", 73 | "AccessKey Key": "", 74 | "nodeClusterId": 1 75 | }, 76 | # 以下为系统运行临时配置,不可动 77 | "Token": "", 78 | "expiresAt": 0, 79 | "adminId": 2 80 | } 81 | ``` 82 | ### 宝塔安装示例 83 | > 什么是宝塔?bt.cn 84 | > 软件商店搜索 python项目管理器 进行安装 85 | > 软件商店搜索 进程守护 进行安装 86 | > 打开 python项目管理器 87 | > 添加项目 88 | > 版本管理,安装python3.7.9等待 89 | > 添加项目 框架python 90 | > 启动方式 gunicorn 91 | > 项目路径 不解释 92 | > 日志目录 不解释 93 | > 启动文件 选择项目路径下的main.py 94 | > 端口随意 95 | > 权限看你的项目文件夹权限不解释 96 | > 安装模块依赖勾上,守护进程开机启动看自己 97 | > 确定 等待 98 | > 启动完毕,关闭项目 99 | > 配置 100 | > 修改worker_class节点为 'uvicorn.workers.UvicornWorker' 101 | > 保存 运行 102 | > 产生配置文件 关闭项目 103 | > 修改配置文件 104 | > 启动项目,启动成功,若失败,请提交issues进行咨询或查看日志自行研究 -------------------------------------------------------------------------------- /Sql/__init__.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | import pymysql 4 | from pymysql import cursors 5 | 6 | 7 | class Sql: 8 | def __init__(self, host, port, user, password, database): 9 | # 链接服务端 10 | try: 11 | self.connect = pymysql.connect( 12 | host=host, # MySQL服务端的IP地址 13 | port=port, # MySQL默认PORT地址(端口号) 14 | user=user, # 用户名 15 | password=password, # 密码,也可以简写为passwd 16 | database=database, # 库名称,也可以简写为db 17 | charset='utf8', # 字符编码 18 | cursorclass=cursors.DictCursor 19 | ) 20 | except: 21 | self.connect = None 22 | if self.connect is None: 23 | self.cursor = None 24 | else: 25 | self.cursor = self.connect.cursor() 26 | 27 | def fj(self, args: dict): 28 | # 分解 29 | keys = "" 30 | values = "" 31 | for i in args: 32 | # 分解Key 33 | if keys == "": 34 | keys = "(`" + i + "`" 35 | else: 36 | keys = keys + ",`" + i + "`" 37 | # 分解value 38 | if values == "": 39 | values = "(" + str(args[i]) 40 | else: 41 | if isinstance(args[i], int): 42 | values = values + "," + str(args[i]) 43 | elif isinstance(args[i], float): 44 | values = values + "," + str(args[i]) 45 | elif isinstance(args[i], str): 46 | values = values + ",'" + args[i] + "'" 47 | else: 48 | return None 49 | if keys != "": 50 | keys = keys + ")" 51 | if values != "": 52 | values = values + ")" 53 | return [keys, values] 54 | 55 | def fj_where(self, args: dict, separator: str = "and"): 56 | prepare = "" 57 | value = "" 58 | for i in args: 59 | if isinstance(args[i], int): 60 | value = str(args[i]) 61 | elif isinstance(args[i], float): 62 | values = str(args[i]) 63 | elif isinstance(args[i], str): 64 | value = "'" + args[i] + "'" 65 | else: 66 | return None 67 | if value == "": 68 | return None 69 | if prepare == "": 70 | prepare = "`" + i + "`" + "=" + value 71 | else: 72 | prepare = prepare + " " + separator + " `" + i + "`" + "=" + value 73 | return prepare 74 | 75 | def query(self, sql: str): 76 | try: 77 | affect_rows = self.cursor.execute(sql) 78 | res = self.cursor.fetchall() # 获取所有数据 79 | return res 80 | except: 81 | return None 82 | 83 | def execute(self, sql: str): 84 | # self.connect.query(sql) 85 | try: 86 | return self.cursor.execute(sql) 87 | except: 88 | # traceback.print_exc() 89 | return None 90 | 91 | def fetchall(self, tableName: str): 92 | sql = "select * from `{}`".format(tableName) 93 | return self.query(sql) 94 | 95 | def fetchall_where(self, tableName: str, args, separator: str = "and", additionalContent: str = None): 96 | prepare = self.fj_where(args, separator) 97 | sql = """select * from `{}` where {}""".format(tableName, prepare) 98 | if additionalContent is not None: 99 | sql = sql + " " + additionalContent 100 | return self.query(sql) 101 | 102 | def fetch_where(self, tableName: str, args, separator: str = "and", additionalContent: str = None): 103 | prepare = self.fj_where(args, separator) 104 | sql = """select * from `{}` where {}""".format(tableName, prepare) 105 | if additionalContent is not None: 106 | sql = sql + " " + additionalContent 107 | res = self.query(sql) 108 | if res is None or len(res) == 0: 109 | return None 110 | return res[0] 111 | 112 | def fetch(self, tableName: str): 113 | sql = "select * from `{}`".format(tableName) 114 | res = self.query(sql) 115 | if res is None or len(res) == 0: 116 | return None 117 | return res[0] 118 | 119 | def insert(self, tableName: str, args: dict): 120 | res = self.fj(args) 121 | keys = res[0] 122 | values = res[1] 123 | sql = """insert into `{}` {} values {}""".format(tableName, keys, values) 124 | affect_rows = self.execute(sql) 125 | if affect_rows is None: 126 | return False 127 | else: 128 | return True 129 | 130 | def delete(self, tableName: str, args: dict, separator: str = "and"): 131 | prepare = self.fj_where(args, separator) 132 | sql = """delete from `{}` where {}""".format(tableName, prepare) 133 | affect_rows = self.execute(sql) 134 | if affect_rows is None: 135 | return False 136 | else: 137 | return True 138 | -------------------------------------------------------------------------------- /Utils/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import random 4 | import shutil 5 | import time 6 | 7 | import API 8 | import ErrCode 9 | import Sql 10 | 11 | 12 | def read_config(): 13 | """"读取配置""" 14 | # 检查配置文件是否存在,不存在创建一个 15 | if not os.path.exists('./Config/Config.json'): 16 | shutil.copy('./Config/Config_initialization.json', './Config/Config.json') 17 | with open("Config/Config.json") as json_file: 18 | config = json.load(json_file) 19 | return config 20 | 21 | 22 | def update_config(config): 23 | """"更新配置""" 24 | with open("Config/Config.json", 'w') as json_file: 25 | json.dump(config, json_file, indent=4) 26 | return None 27 | 28 | 29 | def verify_token(): 30 | # 查询token是否过期 31 | config = read_config() 32 | api = config['API'] 33 | GoEdge_Api = API.Api(api['HOST'], api['AccessKey ID'], api['AccessKey Key']) 34 | if config['Token'] == "" or config['Token'] is None: 35 | # 生成Token 36 | token = GoEdge_Api.getToken() 37 | if token is not None: 38 | token = { 39 | "Token": token[0], 40 | "expiresAt": token[1] 41 | } 42 | 43 | else: 44 | token = { 45 | "Token": "", 46 | "expiresAt": 0 47 | } 48 | config = read_config() 49 | config.update(token) 50 | update_config(config) 51 | # 是否过期 52 | # 给两分钟宽限期 53 | if config['expiresAt'] - 120 < time.time(): 54 | token = GoEdge_Api.getToken() 55 | if token is not None: 56 | token = { 57 | "Token": token[0], 58 | "expiresAt": token[1] 59 | } 60 | 61 | else: 62 | token = { 63 | "Token": "", 64 | "expiresAt": 0 65 | } 66 | config = read_config() 67 | config.update(token) 68 | update_config(config) 69 | 70 | 71 | async def init(): 72 | """读取配置文件,连接数据库等初始化操作""" 73 | # 连接数据库 74 | config = read_config() 75 | mysql = config['mysql'] 76 | Emysql = config['EdgeAdmin_mysql'] 77 | VSql = Sql.Sql(mysql['host'], mysql['port'], mysql['user'], mysql['password'], mysql['database']) 78 | ESql = Sql.Sql(Emysql['host'], Emysql['port'], Emysql['user'], Emysql['password'], Emysql['database']) 79 | return [config, VSql, ESql] 80 | 81 | 82 | def generate_random_str(randomlength=16): 83 | """ 84 | 生成一个指定长度的随机字符串 85 | """ 86 | random_str = '' 87 | base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789' 88 | length = len(base_str) - 1 89 | for i in range(randomlength): 90 | random_str += base_str[random.randint(0, length)] 91 | return random_str 92 | 93 | 94 | def message(code: int, data=None, message: str = "unknown"): 95 | """返回json,message只有400code或ErrCode没有相应的时候才有用""" 96 | if data is None: 97 | data = {} 98 | if code == 400 or code == 389: 99 | msg = message 100 | else: 101 | msg = ErrCode.ErrCode[str(code)] 102 | res = { 103 | "code": code, 104 | "data": data, 105 | "message": msg 106 | } 107 | return res 108 | -------------------------------------------------------------------------------- /Utils/cert.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2331892928/AMEN-GoEdge-User_backEnd/2afa05f98d2263e716feb8d1e71aa7aaecfbaf0b/Utils/cert.py -------------------------------------------------------------------------------- /Utils/tencent.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import urllib 4 | from urllib import parse 5 | 6 | import requests 7 | 8 | 9 | class Tencent: 10 | def __init__(self): 11 | pass 12 | 13 | def check_ticket(self, ticket: str, randstr: str): 14 | url = 'https://cgi.urlsec.qq.com/index.php?m=check&a=gw_check&callback=url_query&url=https%3A%2F%2Fwww.qq.com' \ 15 | '%2F' + str( 16 | random.randint( 17 | 111111, 999999)) + '&ticket=' + urllib.parse.quote(ticket) + '&randstr=' + urllib.parse.quote(randstr) 18 | try: 19 | res = requests.get(url, headers={ 20 | "Accept": "application/json", 21 | "Accept-Language": "zh-CN,zh;q=0.8", 22 | "Connection": "close", 23 | "referer": "https://urlsec.qq.com/check.html", 24 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " 25 | "Chrome/86.0.4240.198 Safari/537.36", 26 | }) 27 | # print(res.content.decode()) 28 | arr = self.jsonp_decode(res.content.decode()) 29 | arr = json.loads(arr) 30 | if arr['reCode'] == 0: # 验证通过 31 | return 1 32 | elif arr['reCode'] == -109: # 验证码错误 33 | return 0 34 | else: # 接口已失效 35 | return -1 36 | except: # 接口已失效 37 | return -1 38 | 39 | def jsonp_decode(self, jsonp: str): 40 | jsonp = jsonp.lstrip() 41 | jsonp = jsonp.rstrip() 42 | if jsonp[0:1] != "[" and jsonp[0:1] != "{": 43 | begin = jsonp.find("(") 44 | 45 | if begin != -1: 46 | end = jsonp.find(")") 47 | if end != -1: 48 | jsonp = jsonp[begin + 1:end] 49 | return jsonp 50 | -------------------------------------------------------------------------------- /edge_code.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : localhost 5 | Source Server Type : MySQL 6 | Source Server Version : 50726 7 | Source Host : localhost:3306 8 | Source Schema : goedge 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50726 12 | File Encoding : 65001 13 | 14 | Date: 30/11/2022 19:37:04 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for edge_code 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `edge_code`; 24 | CREATE TABLE `edge_code` ( 25 | `id` int(11) NOT NULL AUTO_INCREMENT, 26 | `type` int(11) NOT NULL, 27 | `number` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 28 | `code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 29 | `timeStamp` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 30 | PRIMARY KEY (`id`) USING BTREE 31 | ) ENGINE = MyISAM AUTO_INCREMENT = 52 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 32 | 33 | SET FOREIGN_KEY_CHECKS = 1; 34 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import sys 4 | import time 5 | from fastapi import FastAPI, Request, Depends, Cookie, HTTPException 6 | import requests 7 | 8 | import Sql 9 | import Utils 10 | import API 11 | from Controller.HTTPAccessLogService import app as HTTPAccessLogService 12 | from Controller.UserService import app as UserService 13 | from Controller.Verification import app as Verification 14 | from Controller.ConfigService import app as ConfigService 15 | from Controller.ServerService import app as ServerService 16 | from Controller.MetricStatService import app as MetricStatService 17 | from Controller.OriginService import app as OriginService 18 | from Controller.SSLCertService import app as SSLCertService 19 | from Controller.SSLPolicyService import app as SSLPolicyService 20 | from Controller.HTTPWebService import app as HTTPWebService 21 | 22 | 23 | def userInit(UserToken: str = Cookie(None), 24 | UserId: int = Cookie(None), 25 | commons: dict = Depends(Utils.init)): 26 | # 查询cookie是否过期 或 非法Token 27 | # print(UserToken) 28 | # 没有API验证是否正确,读取数据库,虽然每个接口都有输出token错误,提前做好 29 | if UserToken is None: 30 | raise HTTPException(status_code=388, detail=Utils.message(388)) 31 | ESql: Sql.Sql = commons[2] 32 | if ESql is None: 33 | raise HTTPException(status_code=393, detail=Utils.message(393)) 34 | userinfo_token = ESql.fetch_where("edgeAPIAccessTokens", { 35 | "token": UserToken 36 | }) 37 | if userinfo_token is None: 38 | raise HTTPException(status_code=388, detail=Utils.message(388)) 39 | if int(userinfo_token['expiredAt']) < time.time(): 40 | # 过期了 41 | raise HTTPException(status_code=388, detail=Utils.message(388)) 42 | # 查询userid是否有效 43 | if userinfo_token['userId'] != UserId: 44 | raise HTTPException(status_code=388, detail=Utils.message(388)) 45 | 46 | 47 | # 添加全局依赖 48 | app = FastAPI(dependencies=[Depends(Utils.verify_token)], docs_url=None, redoc_url=None) 49 | # app.mount() 50 | # 挂载 51 | 52 | app.include_router(HTTPAccessLogService) 53 | app.include_router(UserService) 54 | app.include_router(Verification) 55 | app.include_router(ConfigService) 56 | app.include_router(ServerService, dependencies=[Depends(userInit)]) 57 | app.include_router(OriginService, dependencies=[Depends(userInit)]) 58 | app.include_router(SSLCertService, dependencies=[Depends(userInit)]) 59 | app.include_router(SSLPolicyService, dependencies=[Depends(userInit)]) 60 | app.include_router(HTTPWebService, dependencies=[Depends(userInit)]) 61 | app.include_router(MetricStatService) 62 | 63 | 64 | @app.on_event('startup') 65 | def init_data(): 66 | # 检查配置文件是否存在,不存在创建一个 67 | if not os.path.exists('./Config/Config.json'): 68 | shutil.copy('./Config/Config_initialization.json', './Config/Config.json') 69 | # 查询adminId,根据acceskeyid 和key查找 70 | config = Utils.read_config() 71 | mysql = config['mysql'] 72 | API_Config = config['API'] 73 | Emysql = config['EdgeAdmin_mysql'] 74 | ESql = Sql.Sql(Emysql['host'], Emysql['port'], Emysql['user'], Emysql['password'], Emysql['database']) 75 | user_info = ESql.fetch_where("edgeUserAccessKeys", { 76 | "uniqueId": API_Config['AccessKey ID'], 77 | "secret": API_Config['AccessKey Key'], 78 | "state": 1 79 | }) 80 | if user_info is None: 81 | raise "id 和 key错误" 82 | else: 83 | # 写入adminId 84 | update = { 85 | "adminId": user_info["adminId"] 86 | } 87 | config.update(update) 88 | Utils.update_config(config) 89 | print("AMEN-GoEdge-User后端启动完毕!") 90 | 91 | 92 | @app.on_event('shutdown') 93 | def shutdown(): 94 | print("shutdown") 95 | 96 | 97 | if __name__ == "__main__": 98 | pass 99 | #import uvicorn 100 | 101 | #uvicorn.run(app='main:app', host="0.0.0.0", port=8002, reload=True, debug=True) 102 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | traceback 2 | json 3 | requests 4 | base64 5 | hashlib 6 | pyOpenSSL 7 | smtplib 8 | pymysql 9 | urllib 10 | random 11 | time 12 | fastapi 13 | uvicorn -------------------------------------------------------------------------------- /test_main.http: -------------------------------------------------------------------------------- 1 | # Test your FastAPI endpoints 2 | 3 | GET http://127.0.0.1:8000/ 4 | Accept: application/json 5 | 6 | ### 7 | 8 | GET http://127.0.0.1:8000/hello/User 9 | Accept: application/json 10 | 11 | ### 12 | --------------------------------------------------------------------------------