diff --git a/.gitignore b/.gitignore index 5111785c9..1d72f627d 100644 --- a/.gitignore +++ b/.gitignore @@ -163,7 +163,6 @@ plugins/l2tp plugins/openlitespeed plugins/tamper_proof plugins/cryptocurrency_trade -plugins/gdrive plugins/mtproxy plugins/zimg plugins/bk_demo diff --git a/plugins/gdrive/class/gdriveclient.py b/plugins/gdrive/class/gdriveclient.py new file mode 100644 index 000000000..9f03da8de --- /dev/null +++ b/plugins/gdrive/class/gdriveclient.py @@ -0,0 +1,413 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import re +import json +import io + + +sys.path.append(os.getcwd() + "/class/core") +import mw + +# ----------------------------- +import google.oauth2.credentials +import google_auth_oauthlib.flow +from googleapiclient.discovery import build +from google_auth_oauthlib.flow import InstalledAppFlow +from google.auth.transport.requests import Request +from googleapiclient.http import MediaFileUpload +from googleapiclient.http import MediaIoBaseDownload + + +class gdriveclient(): + __plugin_dir = '' + __server_dir = '' + + __credentials = "/root/credentials.json" + __backup_dir_name = "backup" + __creds = None + __exclude = "" + __scpos = ['https://www.googleapis.com/auth/drive.file'] + _title = 'Google Drive' + _name = 'Google Drive' + __debug = False + + _DEFAULT_AUTH_PROMPT_MESSAGE = ( + 'Please visit this URL to authorize this application: {url}') + """str: The message to display when prompting the user for + authorization.""" + _DEFAULT_AUTH_CODE_MESSAGE = ( + 'Enter the authorization code: ') + """str: The message to display when prompting the user for the + authorization code. Used only by the console strategy.""" + + _DEFAULT_WEB_SUCCESS_MESSAGE = ( + 'The authentication flow has completed, you may close this window.') + + def __init__(self, plugin_dir, server_dir): + self.__plugin_dir = plugin_dir + self.__server_dir = server_dir + self.set_creds() + # self.set_libList() + + # self.get_exclode() + + def setDebug(self, d=False): + self.__debug = d + + def D(self, msg=''): + if self.__debug: + print(msg) + + # 检查gdrive连接 + def _check_connect(self): + try: + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().list( + pageSize=10, fields="nextPageToken, files(id, name)").execute() + results.get('files', []) + except: + return False + return True + + # 设置creds + def set_creds(self): + token_file = self.__server_dir + '/token.json' + if os.path.exists(token_file): + with open(token_file, 'rb') as token: + tmp_data = json.load(token)['credentials'] + self.__creds = google.oauth2.credentials.Credentials( + tmp_data['token'], + tmp_data['refresh_token'], + tmp_data['id_token'], + tmp_data['token_uri'], + tmp_data['client_id'], + tmp_data['client_secret'], + tmp_data['scopes']) + # if not self._check_connect(): + # return False + # else: + # return True + + def get_sign_in_url(self): + flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( + self.__plugin_dir + '/credentials.json', + scopes=self.__scpos) + flow.redirect_uri = 'https://localhost' + auth_url, state = flow.authorization_url( + access_type='offline', + prompt='consent', + include_granted_scopes='false') + return auth_url, state + + def set_auth_url(self, url): + token_file = self.__server_dir + '/token.json' + if os.path.exists(token_file): + return mw.returnJson(True, "验证成功") + + flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( + self.__plugin_dir + '/credentials.json', + scopes=self.__scpos, + state=url.split('state=')[1].split('&code=')[0]) + flow.redirect_uri = 'https://localhost' + flow.fetch_token(authorization_response=url) + credentials = flow.credentials + + credentials_data = {} + credentials_data['credentials'] = { + 'token': credentials.token, + 'id_token': credentials.id_token, + 'refresh_token': credentials.refresh_token, + 'token_uri': credentials.token_uri, + 'client_id': credentials.client_id, + 'client_secret': credentials.client_secret, + 'scopes': credentials.scopes} + with open(token_file, 'w') as token: + json.dump(credentials_data, token) + if not self.set_creds(): + return mw.returnJson(False, "验证失败,请根据页面1 2 3 步骤完成验证") + return mw.returnJson(True, "验证成功") + + def set_libList(self): + libList = public.readFile("/www/server/panel/data/libList.conf") + if libList: + libList = json.loads(libList) + for i in libList: + if "gdrive" in i.values(): + return + d = { + "name": "Google Drive", + "type": "Cron job", + "ps": "Back up your website or database to Google Cloud Storage.", + "status": False, + "opt": "gdrive", + "module": "os", + "script": "gdrive", + "help": "http://forum.aapanel.com", + "key": "", + "secret": "", + "bucket": "", + "domain": "", + "check": ["/www/server/panel/plugin/gdrive/gdrive_main.py", + "/www/server/panel/script/backup_gdrive.py"] + } + d1 = { + "name": "谷歌硬盘", + "type": "计划任务", + "ps": "将网站或数据库打包备份到谷歌硬盘.", + "status": False, + "opt": "gdrive", + "module": "os", + "script": "gdrive", + "help": "http://www.bt.cn/bbs", + "key": "", + "secret": "", + "bucket": "", + "domain": "", + "check": ["/www/server/panel/plugin/gdrive/gdrive_main.py", + "/www/server/panel/script/backup_gdrive.py"] + } + language = public.readFile("/www/server/panel/config/config.json") + if "English" in language: + data = d + else: + data = d1 + libList.append(data) + public.writeFile("/www/server/panel/data/libList.conf", + json.dumps(libList)) + return libList + + # 获取token + def get_token(self, get): + token_file = self.__server_dir + '/token.json' + import requests + try: + respone = requests.get("https://www.google.com", timeout=2) + except: + return mw.returnJson(False, "连接谷歌失败") + if respone.status_code != 200: + return mw.returnJson(False, "连接谷歌失败") + if not self.set_creds(): + return mw.returnJson(False, "验证失败,请根据页面1 2 3 步骤完成验证") + if not os.path.exists(token_file): + return mw.returnJson(False, "验证失败,请根据页面1 2 3 步骤完成验证") + return mw.returnJson(True, "验证成功") + + # 获取auth_url + def get_auth_url(self, get): + self.get_sign_in_url() + if os.path.exists("/tmp/auth_url"): + return mw.readFile("/tmp/auth_url") + + # 检查连接 + def check_connect(self, get): + token_file = self.__server_dir + '/token.json' + if os.path.exists(token_file): + with open(token_file, 'rb') as token: + self.set_creds() + else: + self.D("Failed to get Google token, please verify before use") + return mw.returnJson(True, "Failed to get Google token, please verify before use") + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().list( + pageSize=10, fields="nextPageToken, files(id, name)").execute() + try: + results.get('files', []) + return mw.returnJson(False, "验证失败,请根据页面1 2 3 步骤完成验证") + return mw.returnJson(True, "验证成功") + except: + return mw.returnJson(False, "验证失败,请根据页面1 2 3 步骤完成验证") + + def _get_filename(self, filename): + l = filename.split("/") + return l[-1] + + def _create_folder_cycle(self, filepath): + l = filepath.split("/") + fid_list = [] + for i in l: + if not i: + continue + fid = self.__get_folder_id(i) + if fid: + fid_list.append(fid) + continue + if not fid_list: + fid_list.append("") + fid_list.append(self.create_folder(i, fid_list[-1])) + return fid_list[-1] + + def build_object_name(self, data_type, file_name): + """根据数据类型构建对象存储名称 + + :param data_type: + :param file_name: + :return: + """ + + import re + prefix_dict = { + "site": "web", + "database": "db", + "path": "path", + } + + if not prefix_dict.get(data_type): + print("data_type 类型错误!!!") + exit(1) + + file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\." + sub_search = re.search(file_regx.lower(), file_name) + sub_path_name = "" + if sub_search: + sub_path_name = sub_search.groups()[0] + sub_path_name += '/' + # 构建OS存储路径 + object_name = self.__backup_dir_name + \ + '/{}/{}'.format(data_type, sub_path_name) + + if object_name[:1] == "/": + object_name = object_name[1:] + return object_name + + # 上传文件 + def upload_file(self, filename, data_type=None): + """ + get.filename 上传后的文件名 + get.filepath 上传文件路径 + 被面板新版计划任务调用时 + get表示file_name + :param get: + :return: + """ + # filename = filename + filepath = self.build_object_name(data_type, filename) + _filename = self._get_filename(filename) + self.D(filepath) + self.D(filename) + + parents = self._create_folder_cycle(filepath) + self.D(parents) + drive_service = build('drive', 'v3', credentials=self.__creds) + file_metadata = {'name': _filename, 'parents': [parents]} + media = MediaFileUpload(filename, resumable=True) + file = drive_service.files().create( + body=file_metadata, media_body=media, fields='id').execute() + self.D('Upload Success ,File ID: %s' % file.get('id')) + return True + + def _get_file_id(self, filename): + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().list(pageSize=10, q="name='{}'".format( + filename), fields="nextPageToken, files(id, name)").execute() + items = results.get('files', []) + if not items: + return [] + else: + for item in items: + return item["id"] + + def delete_file(self, filename=None, data_type=None): + file_id = self._get_file_id(filename) + self.delete_file_by_id(file_id) + return True + + def delete_file_by_id(self, file_id): + self.D("delete id:{}".format(file_id)) + try: + drive_service = build('drive', 'v3', credentials=self.__creds) + drive_service.files().delete(fileId=file_id).execute() + return True + except Exception as e: + return False + + # 创建目录 + def create_folder(self, folder_name, parents=""): + self.D(self.__creds) + self.D("folder_name: {}".format(folder_name)) + self.D("parents: {}".format(parents)) + service = build('drive', 'v3', credentials=self.__creds) + file_metadata = { + 'name': folder_name, + 'mimeType': 'application/vnd.google-apps.folder' + } + if parents: + file_metadata['parents'] = [parents] + folder = service.files().create(body=file_metadata, fields='id').execute() + self.D('Create Folder ID: %s' % folder.get('id')) + return folder.get('id') + + def get_rootdir_id(self, folder_name='backup'): + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().list(pageSize=10, q="name='{}' and mimeType='application/vnd.google-apps.folder'".format(folder_name), + fields="nextPageToken, files(id, name,size,parents,webViewLink)").execute() + items = results.get('files', []) + if len(items) == 0: + self.create_folder(folder_name) + return self.get_rootdir_id(folder_name) + + return items[0]['parents'][0] + + # 获取目录id + def __get_folder_id(self, floder_name): + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().list(pageSize=10, q="name='{}' and mimeType='application/vnd.google-apps.folder'".format(floder_name), + fields="nextPageToken, files(id, name)").execute() + items = results.get('files', []) + if not items: + return [] + else: + for item in items: + return item["id"] + + def get_res_info(self, rid): + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().get(fileId='{}'.format(rid)).execute() + return results + + def get_id_list(self, driveId=''): + service = build('drive', 'v3', credentials=self.__creds) + results = service.files().list(pageSize=10, driveId="{}".format(driveId), + fields="nextPageToken, files(id, name,size,parents)").execute() + items = results.get('files', []) + return items + + def get_list(self, dir_id='', next_page_token=''): + if dir_id == '': + dir_id = self.get_rootdir_id(self.__backup_dir_name) + + service = build('drive', 'v3', credentials=self.__creds) + cmd_query = "trashed=false and '{}' in parents".format(dir_id) + results = service.files().list(pageSize=10, q=cmd_query, orderBy='folder asc', + fields="nextPageToken, files(id, name,size,createdTime,parents,webViewLink)").execute() + items = results.get('files', []) + nextPageToken = results.get('nextPageToken', []) + # print(items) + # print(nextPageToken) + return items + + def get_exclode(self, exclude=[]): + if not exclude: + tmp_exclude = os.getenv('BT_EXCLUDE') + if tmp_exclude: + exclude = tmp_exclude.split(',') + if not exclude: + return "" + for ex in exclude: + self.__exclude += " --exclude=\"" + ex + "\"" + self.__exclude += " " + return self.__exclude + + def download_file(self, filename): + file_id = self._get_file_id(filename) + service = build('drive', 'v3', credentials=self.__creds) + request = service.files().get_media(fileId=file_id) + with open('/tmp/{}'.format(filename), 'wb') as fh: + downloader = MediaIoBaseDownload(fh, request) + done = False + while done is False: + status, done = downloader.next_chunk() + print("Download %d%%." % int(status.progress() * 100)) diff --git a/plugins/gdrive/credentials.json b/plugins/gdrive/credentials.json new file mode 100644 index 000000000..cb209bdcd --- /dev/null +++ b/plugins/gdrive/credentials.json @@ -0,0 +1,11 @@ +{ + "web": { + "client_id": "540181629246-1horo9i4htamdbhiqar9rcbq33bqe2ob.apps.googleusercontent.com", + "project_id": "plated-inn-369901", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_secret": "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", + "redirect_uris": ["https://drive.aapanel.com"] + } +} \ No newline at end of file diff --git a/plugins/gdrive/ico.png b/plugins/gdrive/ico.png new file mode 100644 index 000000000..3cd2b985f Binary files /dev/null and b/plugins/gdrive/ico.png differ diff --git a/plugins/gdrive/index.html b/plugins/gdrive/index.html new file mode 100644 index 000000000..f0f95cbb0 --- /dev/null +++ b/plugins/gdrive/index.html @@ -0,0 +1,310 @@ + +
+
+ + + +
+
    +
    + + + +
    + +
    +
    + + + +
    名称大小更新时间操作
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/plugins/gdrive/index.py b/plugins/gdrive/index.py new file mode 100644 index 000000000..bc864901b --- /dev/null +++ b/plugins/gdrive/index.py @@ -0,0 +1,342 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import re +import json + + +# print(sys.platform) +if sys.platform != "darwin": + os.chdir("/www/server/mdserver-web") + + +sys.path.append(os.getcwd() + "/class/core") +import mw +import db + +_ver = sys.version_info +is_py2 = (_ver[0] == 2) +is_py3 = (_ver[0] == 3) + +DEBUG = False + +if is_py2: + reload(sys) + sys.setdefaultencoding('utf-8') + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'gdrive' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def in_array(name, arr=[]): + for x in arr: + if name == x: + return True + return False + + +sys.path.append(getPluginDir() + "/class") +from gdriveclient import gdriveclient + + +gd = gdriveclient(getPluginDir(), getServerDir()) +gd.setDebug(False) + + +def getArgs(): + args = sys.argv[2:] + tmp = {} + args_len = len(args) + if args_len == 1: + t = args[0].strip('{').strip('}') + if t.strip() == '': + tmp = [] + else: + 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 status(): + return 'start' + + +def isAuthApi(): + cfg = getServerDir() + "/token.json" + if os.path.exists(cfg): + return True + return False + + +def getConf(): + if not isAuthApi(): + sign_in_url, state = gd.get_sign_in_url() + return mw.returnJson(False, "未授权!", {'auth_url': sign_in_url}) + return mw.returnJson(True, "OK") + + +def setAuthUrl(): + args = getArgs() + data = checkArgs(args, ['url']) + if not data[0]: + return data[1] + + url = args['url'] + + try: + if url.startswith("http://"): + url = url.replace("http://", "https://") + token = gd.set_auth_url(url) + # gd.store_token(token) + # gd.store_user() + return mw.returnJson(True, "授权成功!") + except Exception as e: + return mw.returnJson(False, "授权失败2!:" + str(e)) + return mw.returnJson(False, "授权失败!:" + str(e)) + + +def clearAuth(): + cfg = getServerDir() + "/user.conf" + if os.path.exists(cfg): + os.remove(cfg) + + token = getServerDir() + "/token.pickle" + if os.path.exists(token): + os.remove(token) + + return mw.returnJson(True, "清空授权成功!") + + +def getList(): + if not isAuthApi(): + return mw.returnJson(False, "未配置,请点击`授权`", []) + + args = getArgs() + data = checkArgs(args, ['file_id']) + if not data[0]: + return data[1] + + try: + flist = gd.get_list(args['file_id']) + return mw.returnJson(True, "ok", flist) + except Exception as e: + return mw.returnJson(False, str(e), []) + + +def createDir(): + if not isAuthApi(): + return mw.returnJson(False, "未配置,请点击`授权`", []) + + args = getArgs() + data = checkArgs(args, ['parents', 'name']) + if not data[0]: + return data[1] + isok = gd.create_folder(args['name'], args['parents']) + if isok: + return mw.returnJson(True, "创建成功") + return mw.returnJson(False, "创建失败") + + +def deleteDir(): + args = getArgs() + data = checkArgs(args, ['dir_name', 'path']) + if not data[0]: + return data[1] + + isok = gd.delete_file_by_id(args['dir_name']) + if isok: + return mw.returnJson(True, "删除成功") + return mw.returnJson(False, "文件不为空,删除失败!") + + +def deleteFile(): + args = getArgs() + data = checkArgs(args, ['path', 'filename']) + if not data[0]: + return data[1] + + isok = gd.delete_file(args['filename']) + if isok: + return mw.returnJson(True, "删除成功") + return mw.returnJson(False, "删除失败") + + +def findPathName(path, filename): + f = os.scandir(path) + l = [] + for ff in f: + t = {} + if ff.name.find(filename) > -1: + t['filename'] = path + '/' + ff.name + l.append(t) + return l + + +def backupAllFunc(stype): + if not isAuthApi(): + mw.echoInfo("未授权API,无法使用!!!") + return '' + + os.chdir(mw.getRunDir()) + backup_dir = mw.getBackupDir() + run_dir = mw.getRunDir() + + stype = sys.argv[1] + name = sys.argv[2] + num = sys.argv[3] + + prefix_dict = { + "site": "web", + "database": "db", + "path": "path", + } + + backups = [] + sql = db.Sql() + + # print("stype:", stype) + # 提前获取-清理多余备份 + if stype == 'site': + pid = sql.table('sites').where('name=?', (name,)).getField('id') + backups = sql.table('backup').where( + 'type=? and pid=?', ('0', pid)).field('id,filename').select() + if stype == 'database': + db_path = mw.getServerDir() + '/mysql' + pid = mw.M('databases').dbPos(db_path, 'mysql').where( + 'name=?', (name,)).getField('id') + backups = sql.table('backup').where( + 'type=? and pid=?', ('1', pid)).field('id,filename').select() + if stype == 'path': + backup_path = backup_dir + '/path' + _name = 'path_{}'.format(os.path.basename(name)) + backups = findPathName(backup_path, _name) + + # 其他类型关系性数据库(mysql类的) + if stype.find('database_') > -1: + plugin_name = stype.replace('database_', '') + db_path = mw.getServerDir() + '/' + plugin_name + pid = mw.M('databases').dbPos(db_path, 'mysql').where( + 'name=?', (name,)).getField('id') + backups = sql.table('backup').where( + 'type=? and pid=?', ('1', pid)).field('id,filename').select() + + args = stype + " " + name + " " + num + cmd = 'python3 ' + run_dir + '/scripts/backup.py ' + args + if stype.find('database_') > -1: + plugin_name = stype.replace('database_', '') + args = "database " + name + " " + num + cmd = 'python3 ' + run_dir + '/plugins/' + \ + plugin_name + '/scripts/backup.py ' + args + + if stype == 'path': + name = os.path.basename(name) + + # print("cmd:", cmd) + os.system(cmd) + + # 开始执行上传信息. + if stype.find('database_') > -1: + bk_name = 'database' + plugin_name = stype.replace('database_', '') + bk_prefix = plugin_name + '/db' + stype = 'database' + else: + bk_prefix = prefix_dict[stype] + bk_name = stype + + find_path = backup_dir + '/' + bk_name + '/' + bk_prefix + '_' + name + find_new_file = "ls " + find_path + \ + "_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'" + + # print(find_new_file) + + filename = mw.execShell(find_new_file)[0].strip() + if filename == "": + mw.echoInfo("not find upload file!") + return False + + mw.echoInfo("准备上传文件 {}".format(filename)) + mw.echoStart('开始上传') + gd.upload_file(filename, stype) + mw.echoEnd('上传成功') + + # print(backups) + backups = sorted(backups, key=lambda x: x['filename'], reverse=False) + mw.echoStart('开始删除远程备份') + num = int(num) + sep = len(backups) - num + if sep > -1: + for backup in backups: + fn = os.path.basename(backup['filename']) + gd.delete_file(fn, stype) + mw.echoInfo("---已清理远程过期备份文件:" + fn) + sep -= 1 + if sep < 0: + break + mw.echoEnd('结束删除远程备份') + + return '' + + +def installPreInspection(): + 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 == 'install_pre_inspection': + print(installPreInspection()) + elif func == 'conf': + print(getConf()) + elif func == 'set_auth_url': + print(setAuthUrl()) + elif func == 'clear_auth': + print(clearAuth()) + elif func == "get_list": + print(getList()) + elif func == "create_dir": + print(createDir()) + elif func == "delete_dir": + print(deleteDir()) + elif func == 'delete_file': + print(deleteFile()) + elif in_array(func, ['site', 'database', 'path']) or func.find('database_') > -1: + print(backupAllFunc(func)) + else: + print('error') diff --git a/plugins/gdrive/info.json b/plugins/gdrive/info.json new file mode 100644 index 000000000..9e866a073 --- /dev/null +++ b/plugins/gdrive/info.json @@ -0,0 +1,19 @@ +{ + "hook":["backup"], + "id":998, + "title":"谷歌云网盘", + "tip":"lib", + "name":"gdrive", + "type":"扩展", + "ps":"备份你的数据到谷歌云网盘", + "versions":"2.0", + "shell":"install.sh", + "checks": "server/gdrive", + "path":"server/gdrive", + "author":"google", + "home":"https://drive.google.com/", + "api_doc":"https://developers.google.com/drive/api/guides/about-sdk?hl=zh_CN", + "api_doc2":"https://developers.google.cn/drive/api/reference/rest/v3/comments/list?hl=zh-cn", + "date":"2022-06-26", + "pid":"4" +} \ No newline at end of file diff --git a/plugins/gdrive/install.sh b/plugins/gdrive/install.sh new file mode 100644 index 000000000..1ad2d004d --- /dev/null +++ b/plugins/gdrive/install.sh @@ -0,0 +1,79 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl + +PATH=$PATH:${rootPath}/bin +export PATH + +VERSION=$2 + +Install_App() +{ + tmp_ping=`ping -c 1 google.com 2>&1` + echo $tmp_ping + if [ $? -eq 0 ];then + tmp=`python -V 2>&1|awk '{print $2}'` + pVersion=${tmp:0:3} + + which pip + if [ $? -eq 0 ];then + tmp=$(pip list|grep google-api-python-client|awk '{print $2}') + if [ "$tmp" != '2.39.0' ];then + pip install --upgrade google-api-python-client + # pip uninstall google-api-python-client -y + pip install -I google-api-python-client==2.39.0 -i https://pypi.Python.org/simple + fi + tmp=$(pip list|grep google-auth-httplib2|awk '{print $2}') + if [ "$tmp" != '0.1.0' ];then + pip uninstall google-auth-httplib2 -y + pip install -I google-auth-httplib2==0.1.0 -i https://pypi.Python.org/simple + fi + tmp=$(pip list|grep google-auth-oauthlib|awk '{print $2}') + if [ "$tmp" != '0.5.0' ];then + # pip uninstall google-auth-oauthlib -y + pip install -I google-auth-oauthlib==0.5.0 -i https://pypi.Python.org/simple + fi + tmp=$(pip list|grep -E '^httplib2'|awk '{print $2}') + if [ "$tmp" != '0.18.1' ];then + # pip uninstall httplib2 -y + pip install -I httplib2==0.18.1 -i https://pypi.Python.org/simple + fi + else + pip install -I pyOpenSSL + pip install -I google-api-python-client==2.39.0 google-auth-httplib2==0.1.0 google-auth-oauthlib==0.5.0 -i https://pypi.Python.org/simple + pip install -I httplib2==0.18.1 -i https://pypi.Python.org/simple + fi + echo '正在安装脚本文件...' > $install_tmp + + mkdir -p $serverPath/gdrive + echo "${VERSION}" > $serverPath/gdrive/version.pl + echo '安装完成' > $install_tmp + else + echo '服务器连接不上谷歌云!安装失败!' > $install_tmp + exit 1 + fi +} + +Uninstall_App() +{ + rm -rf $serverPath/gdrive + echo '卸载完成' > $install_tmp +} + + +action=$1 +type=$2 + +echo $action $type +if [ "${action}" == 'install' ];then + Install_App +else + Uninstall_App +fi diff --git a/plugins/gdrive/js/gdrive.js b/plugins/gdrive/js/gdrive.js new file mode 100644 index 000000000..08aa336a1 --- /dev/null +++ b/plugins/gdrive/js/gdrive.js @@ -0,0 +1,260 @@ + +function gdPost(method,args,callback){ + var _args = null; + if (typeof(args) == 'string'){ + _args = JSON.stringify(toArrayObject(args)); + } else { + _args = JSON.stringify(args); + } + + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + $.post('/plugins/run', {name:'gdrive', 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 createDir(){ + layer.open({ + type: 1, + area: "400px", + title: "创建目录", + closeBtn: 1, + shift: 5, + shadeClose: false, + btn: ['确定','取消'], + content:'
    \ +

    \ + 目录名称:\ + \ +

    \ +
    ', + success:function(){ + $("input[name='newPath']").focus().keyup(function(e){ + if(e.keyCode == 13) $(".layui-layer-btn0").click(); + }); + }, + yes:function(index,layero){ + var name = $("input[name='newPath']").val(); + if(name == ''){ + layer.msg('目录名称不能为空!',{icon:2}); + return; + } + var parents = $("#myPath").val(); + var cur_file_id = $('#curPath').val(); + if (cur_file_id!=''){ + parents = cur_file_id; + } + + var dirname = name; + var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']}); + gdPost('create_dir', {parents:parents,name:dirname}, function(data){ + layer.close(loadT); + var rdata = $.parseJSON(data.data); + if(rdata.status) { + showMsg(rdata.msg, function(){ + layer.close(index); + var file_id = $('#myPath').val(); + if (cur_file_id!=''){ + file_id = cur_file_id; + } + gdList(file_id); + } ,{icon:1}, 2000); + } else{ + layer.msg(rdata.msg,{icon:2}); + } + }); + } + }); +} + + +//设置API +function authApi(){ + + gdPost('conf', {}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + + // console.log(rdata); + // console.log(rdata.data.auth_url); + var apicon = ''; + if (rdata.status){ + + var html = ''; + html += ''; + + var loadOpen = layer.open({ + type: 1, + title: '已授权', + area: '240px', + content:'
    '+html+'
    ', + success: function(){ + $('#clear_auth').click(function(){ + gdPost('clear_auth', {}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg,function(){ + layer.close(loadOpen); + gdList(''); + },{icon:rdata.status?1:2},2000); + }); + }); + } + }); + return true; + + } else{ + apicon = '
    '+$("#check_api").html()+'
    '; + } + + var layer_auth = layer.open({ + type: 1, + area: "620px", + title: "Google Drive 授权", + closeBtn: 1, + shift: 5, + shadeClose: false, + content:apicon, + success:function(layero,index){ + // console.log(layero,index); + if (!rdata.status){ + $('.check_api .step_two_url').val(rdata.data['auth_url']); + $('.check_api .open_btlink').attr('href',rdata.data['auth_url']); + + $('.check_api .ico-copy').click(function(){ + copyPass(rdata.data['auth_url']); + }); + + $('.check_api .set_auth_btn').click(function(){ + + var url = $('.check_api .google_drive').val(); + if ( url == ''){ + layer.msg("验证URL不能为空",{icon:2}); + return; + } + // console.log(url); + gdPost('set_auth_url', {url:url}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + var show_time = 2000; + if (!rdata.status){ + show_time = 10000; + } + + showMsg(rdata.msg,function(){ + if (rdata.status){ + layer.close(layer_auth); + gdList(''); + } + },{icon:rdata.status?1:2},show_time); + }); + }); + + + } + + } + }); + }); +} + +//计算当前目录偏移 +function upPathLeft(){ + var UlWidth = $(".place-input ul").width(); + var SpanPathWidth = $(".place-input").width() - 20; + var Ml = UlWidth - SpanPathWidth; + if(UlWidth > SpanPathWidth ){ + $(".place-input ul").css("left",-Ml) + } + else{ + $(".place-input ul").css("left",0) + } +} + +function getGDTime(a) { + return new Date(a).format("yyyy/MM/dd hh:mm:ss") +} + +function gdList(file_id){ + $('#curPath').val(file_id); + gdPost('get_list', {file_id:file_id}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + console.log(rdata); + if(rdata.status === false){ + showMsg(rdata.msg,function(){ + authApi(); + },{icon:2},2000); + return; + } + + var mlist = rdata.data; + var listBody = ''; + var listFiles = ''; + for(var i=0;i\'+mlist[i].name+'\ + -\ + -\ + 删除' + }else{ + listFiles += ''+mlist[i].name+'\ + '+toSize(mlist[i].size)+'\ + '+getGDTime(mlist[i].createdTime)+'\ + 下载 | 删除' + } + } + listBody += listFiles; + var pathLi = '
  • 根目录
  • '; + + if (mlist.length>0){ + $('#myPath').val(mlist[0]['parents'][0]); + } + + $(".upyunCon .place-input ul").html(pathLi); + $(".upyunlist .list-list").html(listBody); + + $('#backBtn').unbind().click(function() { + gdList(''); + }); + + $('.upyunCon .refreshBtn').unbind().click(function(){ + var file_id = $('#myPath').val(); + gdList(file_id); + }); + }); +} + + +//删除文件 +function deleteFile(name, is_dir){ + if (is_dir === false){ + safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){ + var path = $("#myPath").val(); + var filename = name; + gdPost('delete_file', {filename:filename,path:path}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg,function(){ + var file_id = $('#myPath').val(); + gdList(file_id); + },{icon:rdata.status?1:2},2000); + }); + }); + } else { + safeMessage('删除文件夹','删除后将无法恢复,真的要删除文件资源['+name+']吗?',function(){ + var path = $("#myPath").val(); + gdPost('delete_dir', {dir_name:name,path:path}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg,function(){ + var file_id = $('#myPath').val(); + gdList(file_id); + },{icon:rdata.status?1:2},2000); + }); + }); + } +} \ No newline at end of file diff --git a/plugins/gdrive/t/test.py b/plugins/gdrive/t/test.py new file mode 100644 index 000000000..360191934 --- /dev/null +++ b/plugins/gdrive/t/test.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# coding: utf-8 + +# python3 plugins/gdrive/t/test.py +# https://console.cloud.google.com/apis/credentials/consent?project=plated-inn-369901 + +# https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=226011946234-d3e1vashgag64utjedu1ljt9u39ncrpq.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fdrive.aapanel.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file&state=I2da4aZUwqgmikrgrqAwwSjyoEHVs1&access_type=offline&prompt=consent&include_granted_scopes=false + + +# https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/appId/08125e6b-6502-4ac9-9548-ad682f00848d/objectId/62b1d655-9828-47ed-be99-65eb18c3a929/isMSAApp~/false/defaultBlade/Overview/appSignInAudience/AzureADandPersonalMicrosoftAccount/servicePrincipalCreated~/true + +# 0WA8Q~sZkZFZKv50ryP4ux~.fpVtbHw7BuTZmbQB +# client_id:d9878fac-8526-4ff6-8036-e1c92dd9dd80 + +# 08125e6b-6502-4ac9-9548-ad682f00848d + + +# /drive/v2internal/files?openDrive=false&reason=102&syncType=0&errorRecovery=false&q=trashed = false and '1-z6gUXseXmlxmntvkLzOe_tYFT5tdhM9' in parents&fields=kind,nextPageToken,items(kind,modifiedDate,hasVisitorPermissions,containsUnsubscribedChildren,modifiedByMeDate,lastViewedByMeDate,alternateLink,fileSize,owners(kind,permissionId,emailAddressFromAccount,id),lastModifyingUser(kind,permissionId,emailAddressFromAccount,id),customerId,ancestorHasAugmentedPermissions,hasThumbnail,thumbnailVersion,title,id,resourceKey,abuseIsAppealable,abuseNoticeReason,shared,accessRequestsCount,sharedWithMeDate,userPermission(role),explicitlyTrashed,mimeType,quotaBytesUsed,copyable,subscribed,folderColor,hasChildFolders,fileExtension,primarySyncParentId,sharingUser(kind,permissionId,emailAddressFromAccount,id),flaggedForAbuse,folderFeatures,spaces,sourceAppId,recency,recencyReason,version,actionItems,teamDriveId,hasAugmentedPermissions,createdDate,primaryDomainName,organizationDisplayName,passivelySubscribed,trashingUser(kind,permissionId,emailAddressFromAccount,id),trashedDate,parents(id),capabilities(canMoveItemIntoTeamDrive,canUntrash,canModifyContentRestriction,canMoveItemWithinTeamDrive,canMoveItemOutOfTeamDrive,canDeleteChildren,canTrashChildren,canRequestApproval,canReadCategoryMetadata,canEditCategoryMetadata,canAddMyDriveParent,canRemoveMyDriveParent,canShareChildFiles,canShareChildFolders,canRead,canMoveItemWithinDrive,canMoveChildrenWithinDrive,canAddFolderFromAnotherDrive,canChangeSecurityUpdateEnabled,canBlockOwner,canReportSpamOrAbuse,canCopy,canDownload,canEdit,canAddChildren,canDelete,canRemoveChildren,canShare,canTrash,canRename,canReadTeamDrive,canMoveTeamDriveItem),contentRestrictions(readOnly),approvalMetadata(approvalVersion,approvalSummaries,hasIncomingApproval),shortcutDetails(targetId,targetMimeType,targetLookupStatus,targetFile,canRequestAccessToTarget),spamMetadata(markedAsSpamDate,inSpamView),labels(starred,trashed,restricted,viewed)),incompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&maxResults=200&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder,title_natural asc&retryCount=0&key=AIzaSyD_InbmSFufIEps5UAt2NmB_3LvBH3Sz_8 HTTP/1.1 + +# http://localhost/?code=M.C106_BAY.2.3e12c859-6107-0c5b-9ef4-14b3fb8269ba&state=JzHdzHXmA7x6zl7Be6cJ6uOlf9Bg69 + + +# python3 /www/mdserver-web/plugins/gdrive/index.py site zzzvps.com 3 +# python3 /www/mdserver-web/plugins/gdrive/index.py database t1 3 +# python3 /www/server/mdserver-web/plugins/msonedrive/index.py path +# /Users/midoks/Desktop/dev/python 3 + + +import sys +import io +import os +import time +import re +import json + + +sys.path.append(os.getcwd() + "/class/core") +import mw + + +def getPluginName(): + return 'gdrive' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +# print(getPluginDir()) +sys.path.append(getPluginDir() + "/class") +from gdriveclient import gdriveclient + + +gd = gdriveclient(getPluginDir(), getServerDir()) +gd.setDebug(True) +# sign_in_url, state = gd.get_sign_in_url() +# print(sign_in_url) + +# url = 'https://localhost/?state=GH2YZ1VeytzVB1BqJJpQZIBk2GGAub&code=4/0Adeu5BXnD2dQvXx8Sg0WPn1XiMpihcRBNaG1yaFKIo86gUiG7q65KU1MaNCxrj_f2bjkwQ&scope=https://www.googleapis.com/auth/drive.file' +# t = gd.set_auth_url(url) +# print(t) + +# def set_auth_url(url): +# try: +# if url.startswith("http://"): +# url = url.replace("http://", "https://") +# token = msodc.get_token_from_authorized_url( +# authorized_url=url) +# msodc.store_token(token) +# msodc.store_user() +# return mw.returnJson(True, "授权成功!") +# except Exception as e: +# print(e) +# return mw.returnJson(False, "授权失败2!:" + str(e)) +# return mw.returnJson(False, "授权失败!:" + str(e)) + +# url = 'http://localhost/?code=M.C106_BAY.2.310112f3-a158-c400-9667-d158cbd1de6c&state=jEJz0ucR9bpZYD9PGxp2GgRDotrzO6' +# token = set_auth_url(url) +# print(token) + +# token = msodc.get_token() +# print("token:", token) + +# t = gd.get_list('') +# print(t) + +# t = gd.get_id_list('1u7LjXGj1KoN-ltAdTRaib7IZJpsEnPdz') +# print(t) + +# t = gd.create_folder('backup_demo') +# print(t) + +# t = msodc.delete_object('backup') +# print(t) + + +# t = gd.upload_file('web_t1.cn_20230830_134549.tar.gz', 'site') +# print(t) +# print(gd.error_msg) + +# /Users/midoks/Desktop/mwdev/server/mdserver-web/paramiko.log +# backup/site/paramiko.log +# |-正在上传到 backup/site/paramiko.log... +# True + + +t = gd.upload_file( + 'db_zzzvps_20230830_210727.sql.gz', 'datebase') +print(t) diff --git a/plugins/msonedrive/class/msodclient.py b/plugins/msonedrive/class/msodclient.py index d17efab9c..2181caf66 100644 --- a/plugins/msonedrive/class/msodclient.py +++ b/plugins/msonedrive/class/msodclient.py @@ -406,6 +406,11 @@ class msodclient: "database": "db", "path": "path", } + + if not prefix_dict.get(data_type): + print("data_type 类型错误!!!") + exit(1) + file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+(?:\.|_)" sub_search = re.search(file_regx, file_name) sub_path_name = ""