def get(self): """ * @api {get} /record/ 获取执行记录列表 * @apiPermission Role.Developer AND Owner * @apiDescription 用户只能查看自己设定的调度计划生成的记录 * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {String} [username] 用户名 * @apiParam {String} [idn] 用户 ID * @apiParam {String} [project] 项目名称 * @apiParam {Int} [version] 版本号 * @apiParam {String="cron", "interval", "date"} [mode] 时间类型 * @apiParam {Int} [limit=0] Limit * @apiParam {Int} [skip=0] Skip * @apiParam {String="create", "status", "role"} [order=create] 排序字段 * @apiParam {Int=1, -1} [sort=1] 排序方式 * @apiParamExample Param-Example /record?version=1576206368&order=version&sort=-1 * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 200, "data": { "data": [ { "create": "2019-12-13 09:20:00", "duration": "0:0:0", "end": "2019-12-13 09:20:00", "id": "5df2e74005db1557d78b16d7", "idn": "5df0ea19b46116479b46c27c", "inserted": "5df2e70a0286cdc6a686b9df", "jid": "5a36346b-1f28-41de-a94e-b28c550acc42", "job": "3e5aecfb-2f46-42ea-b492-bbcc1e1219ca", "mode": "cron", "project": "videos", "rule": { "hour": "*", "minute": "*", "second": "*/30" }, "start": "2019-12-13 09:20:00", "username": "******", "version": "1576199082" }, { "create": "2019-12-13 09:24:00", "duration": "0:0:0", "end": "2019-12-13 09:24:00", "id": "5df2e83017040767e3bd7076", "idn": "5df0ea19b46116479b46c27c", "inserted": "5df2e70a0286cdc6a686b9df", "jid": "5a36346b-1f28-41de-a94e-b28c550acc42", "job": "8fe33a65-c568-47af-8e0f-911c064d02a3", "mode": "cron", "project": "videos", "rule": { "hour": "*", "minute": "*", "second": "*/30" }, "start": "2019-12-13 09:24:00", "username": "******", "version": "1576199082" } ] }, "message": "success" } """ query = {} # 检查所有权 token = request.headers.get("Authorization") auth_idn, auth_username, auth_role = get_user_info(token) if auth_role != Role.SuperUser.value: username = auth_username idn = auth_idn else: username = request.args.get('username') idn = request.args.get('idn') project = request.args.get('project') version = request.args.get('version') mode = request.args.get('mode') order = request.args.get('order') or "create" sor = request.args.get('sort') or 1 limit = request.args.get('limit') or 0 skip = request.args.get('skip') or 0 if project: query["project"] = project if version: query["version"] = version if mode: query["mode"] = mode if username: query["username"] = username if idn: query["idn"] = idn # 允许用户自定义查询条件 result = databases.record.find(query).limit(int(limit)).skip( int(skip)).sort(order, int(sor)) information = [] for i in result: info = { "id": str(i.get('_id')), "project": i.get("project"), "version": i.get("version"), "mode": i.get("mode"), "rule": i.get("rule"), "job": i.get("job"), "jid": i.get("jid"), "start": i.get("start"), "duration": i.get("duration"), "end": i.get("end"), "inserted": i.get("inserted"), "idn": i.get("idn"), "username": i.get("username"), "create": i.get("create").strftime("%Y-%m-%d %H:%M:%S") } information.append(info) return {"message": "success", "data": information, "code": 200}
def delete(self): """ * @api {delete} /deploy/ 删除项目 * @apiPermission Role.Developer AND Owner * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {Json} query 删除指定文件或指定目录 @apiParamExample Param-Example: # 删除指定文件 {"query": {"project": "sail", "version": "1576199082"}} # 删除指定目录及目录下所有文件 {"query": {"project": "football"}} * @apiErrorExample {json} Error-Response: # status code: 403 { "code": 4002, "data": {}, "message": "is not yours" } * @apiSuccessExample {json} Success-Response: # status code: 200 # 0 代表删除数量,B 代表文件不存在无需删除 { "code": 204, "data": { "count": 0, "deleted": "B", "query": { "idn": "5df0ea19b46116479b46c27c", "project": "sail", "username": "******", "version": "1576199061" } }, "message": "success" } OR # 1 代表删除数量,A 代表文件存在 { "code": 204, "data": { "count": 1, "deleted": "A", "query": { "idn": "5df0ea19b46116479b46c27c", "project": "sail", "username": "******", "version": "1576199061" } }, "message": "success" } """ query = request.json.get('query') if query: project = query.get("project") or "" version = query.get("version") or "" if not project or "." in project or "/" in project or "." in version or "/" in version: # . or / 防止恶意参数删除系统目录 return {"message": StatusCode.PathError.value[0], "data": {}, "code": StatusCode.PathError.value[1] }, 400 # 检查所有权 token = request.headers.get("Authorization") idn, username, role = get_user_info(token) query["idn"] = idn query["username"] = username count = databases.deploy.count_documents(query) if not count and role != Role.SuperUser.value: return {"message": StatusCode.IsNotYours.value[0], "data": {}, "code": StatusCode.IsNotYours.value[1] }, 403 deleted = storages.delete(project, version) else: query = {} # 允许用户自定义删除条件 result = databases.deploy.delete_one(query) count = result.deleted_count return {"message": "success", "data": {"count": count, "deleted": deleted, "query": query}, "code": 204 }
def get(self): """ * @api {get} /deploy/ 获取项目列表 * @apiPermission Role.Developer AND Owner * @apiDescription 用户只能查看自己上传的项目 * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {String} [username] 用户名 * @apiParam {String} [project] 项目名称 * @apiParam {Int} [version] 版本号 * @apiParam {Int} [limit=0] Limit * @apiParam {Int} [skip=0] Skip * @apiParam {String="create", "status", "role"} [order=create] 排序字段 * @apiParam {Int=1, -1} [sort=1] 排序方式 * @apiParamExample Param-Example /deploy?project=football&order=version&sort=-1 * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 200, "data": { "data": [ { "create": "2019-12-13 09:04:15", "id": "5df2e38fed21d4d2879a762a", "idn": "5df0ea19b46116479b46c27c", "project": "football", "username": "******", "version": "1576199055" }, { "create": "2019-12-13 09:04:42", "id": "5df2e3aaed21d4d2879a762c", "idn": "5df0ea19b46116479b46c27c", "project": "videos", "username": "******", "version": "1576199082" } ] }, "message": "success" } """ query = {} # 检查所有权 token = request.headers.get("Authorization") auth_idn, auth_username, auth_role = get_user_info(token) if auth_role != Role.SuperUser.value: username = auth_username else: username = request.args.get("username") project = request.args.get('project') version = request.args.get('version') order = request.args.get('order') or "create" sor = request.args.get('sort') or 1 limit = request.args.get('limit') or 0 skip = request.args.get('skip') or 0 if project: query["project"] = project if version: query["version"] = version if username: query["username"] = username # 允许用户自定义查询条件 result = databases.deploy.find(query).limit(int(limit)).skip(int(skip)).sort(order, int(sor)) information = [] for i in result: info = { "id": str(i.get('_id')), "project": i.get("project"), "version": i.get("version"), "idn": i.get("idn"), "username": i.get("username"), "create": i.get("create").strftime("%Y-%m-%d %H:%M:%S")} information.append(info) return {"message": "success", "data": information, "code": 200}
def delete(self): """ * @api {delete} /logs/ 删除多个指定的日志文件 * @apiPermission Role.Superuser * @apiDescription 管理员才能删除日志 * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {Json} query 批量删除(指定的)日志文件 @apiParamExample Param-Example: # 删除指定文件 { "querys": [ { "project": "videos", "job": "3e5aecfb-2f46-42ea-b492-bbcc1e1219ca" }, { "project": "football", "job": "5i8a9cfb-2f46-42ea-b492-b07c1e1201h6" } ] } * @apiErrorExample {json} Error-Response: # status code: 400 { "code": 4003, "data": {}, "message": "parameter error" } * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 200, "data": { "count": 1, "path": [ "videos/3e5aecfb-2f46-42ea-b492-bbcc1e1219ca.log" ] }, "message": "success" } """ try: # 确保参数格式正确 querys = request.json.get("querys") token = request.headers.get("Authorization") idn, username, role = get_user_info(token) if role != Role.SuperUser.value: return { "message": StatusCode.NoAuth.value[0], "data": {}, "code": StatusCode.NoAuth.value[1] }, 403 except JSONDecodeError: return { "message": StatusCode.JsonDecodeError.value[0], "data": {}, "code": StatusCode.JsonDecodeError.value[1] }, 400 if not isinstance(querys, list): return { "message": StatusCode.ParameterError.value[0], "data": {}, "code": StatusCode.ParameterError.value[1] }, 400 for query in querys: # 检查文件是否均存在 project = query.get("project") job = query.get("job") if not project or not job: return { "message": StatusCode.ParameterError.value[0], "data": {}, "code": StatusCode.ParameterError.value[1] }, 400 filename = os.path.join(LOGPATH, project, "%s.log" % job) if not os.path.exists(filename): return { "message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400 result = [] for query in querys: # 确保文件均存在才删除 project = query.get("project") job = query.get("job") if "." in project or "/" in project or "." in job or "/" in job: # . or / 防止恶意参数删除系统目录 return { "message": StatusCode.PathError.value[0], "data": {}, "code": StatusCode.PathError.value[1] }, 400 filename = os.path.join(LOGPATH, project, "%s.log" % job) os.remove(filename) drop = "%s/%s.log" % (project, job) result.append(drop) return { "message": "success", "data": { "path": result, "count": len(result) }, "code": 200 }
def post(self): """项目部署 * @api {post} /deploy/ 项目部署 * @apiPermission Role.Developer * @apiParam {String} project 项目名称 * @apiParam {File} file 文件 * @apiParam {String} [remark] 备注 * @apiErrorExample {json} Error-Response: # status code: 400 { "code": 4001, "data": {}, "message": "missing parameter" } * @apiSuccessExample {json} Success-Response: # status code: 201 { "code": 201, "data": { "_id": "5df2e3637e0e5bf80cc5263e", "create": "Fri, 13 Dec 2019 09:03:31 GMT", "idn": "5df0ea19b46116479b46c27c", "project": "coders", "remark": "足球队", "username": "******", "version": "1576199011" }, "message": "success" } """ project = request.form.get('project') remark = request.form.get('remark') file = request.files.get('file') if not project or not file: # 确保参数和值存在 return {"message": StatusCode.MissingParameter.value[0], "data": {}, "code": StatusCode.MissingParameter.value[1] }, 400 filename = file.filename if not filename.endswith('.egg'): # 确保文件类型正确 return {"message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400 version = int(time.time()) content = file.stream.read() # 将文件存储到服务端 result = storages.put(project, version, content) if not result: # 存储失败则返回相关提示 return {"message": StatusCode.OperationError.value[0], "data": {}, "code": StatusCode.OperationError.value[1] }, 400 # 文件存储成功后在数据库中存储相关信息并返回给用户 # 增加所有权 token = request.headers.get("Authorization") idn, username, role = get_user_info(token) message = {"project": project, "version": str(version), "remark": remark or "Nothing", "idn": idn, "username": username, "create": datetime.now()} databases.deploy.insert_one(message).inserted_id message["_id"] = str(message.pop("_id")) return {"message": "success", "data": message, "code": 201}, 201
def get(self): """ * @api {get} /logs 获取指定日志的信息 * @apiPermission Role.Developer AND Owner * @apiDescription 用户只能查看自己设定的调度计划生成的日志 * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {String} project 项目名称 * @apiParam {String} job Job * @apiParamExample Param-Example /logs?project=videos&Job=3e5aecfb-2f46-42ea-b492-bbcc1e1219ca @apiErrorExample {json} Error-Response: # status code: 400 { "code": 4005, "data": {}, "message": "not found" } * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 200, "data": { "content": "Traceback (most recent call last):\n File \"/Users/async/Documents/GithubProject/sailboat/executor/boat.py\", line 46, in <module>\n main(project, version)\n File \"/Users/async/Documents/GithubProject/sailboat/executor/boat.py\", line 40, in main\n spider = import_module(MODULERNAME)\n File \"/Users/async/anaconda3/envs/slp/lib/python3.6/importlib/__init__.py\", line 126, in import_module\n return _bootstrap._gcd_import(name[level:], package, level)\n File \"<frozen importlib._bootstrap>\", line 994, in _gcd_import\n File \"<frozen importlib._bootstrap>\", line 971, in _find_and_load\n File \"<frozen importlib._bootstrap>\", line 953, in _find_and_load_unlocked\nModuleNotFoundError: No module named 'sail'\n", "size": "709 byte" }, "message": "success" } """ project = request.args.get("project") job = request.args.get("job") if not project or not job: return { "message": StatusCode.MissingParameter.value[0], "data": {}, "code": StatusCode.MissingParameter.value[1] }, 400 filename = os.path.join(LOGPATH, project, "%s.log" % job) if not os.path.exists(filename): return { "message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400 # 检查所有权 token = request.headers.get("Authorization") idn, username, role = get_user_info(token) count = 1 if role != Role.SuperUser.value: query = { "idn": idn, "username": username, "project": project, "job": job } count = databases.record.count_documents(query) if not count: return { "message": StatusCode.IsNotYours.value[0], "data": {}, "code": StatusCode.IsNotYours.value[1] }, 403 with open(filename, "r") as file: content = file.read() # 计算日志文件大小 unit = "byte" size = os.path.getsize(filename) if size > 1024: unit = "kb" size = size // 1024 if size > 1024: unit = "mb" size = size // 1024 return { "message": "success", "data": { "size": "%s %s" % (size, unit), "content": content }, "code": 200 }
def post(self): """ * @api {post} /login/ 用户登录 * @apiPermission Role.Anonymous * @apiParam {String} username 用户名 * @apiParam {String} password 密码 * @apiParamExample {json} Param-Example: { "username": "******", "password": "******" } * @apiErrorExample {json} Error-Response: # status code: 400 { "code": 4005, "data": {}, "message": "not found" } * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 200, "data": { "idn": "5df0ea19b46116479b46c27c", "role": 100, "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InNmaGZwYyIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJzdGF0dXMiOjEsInJvbGUiOjEwMCwiZXhwcmVzcyI6IjIwMTktMTItMTMgMTY6MDU6MjcifQ.KiPJvuQwKeZHof8CoAoppxNTsjNgCjpkkJT2Pi48XOs", "username": "******" }, "message": "success" } """ username = request.json.get("username") pwd = request.json.get("password") password = md5_encode(pwd) # 支持用户名或邮箱登录 query = {"username": username, "password": password} name_exit = databases.user.count_documents(query) if not name_exit: query = {"email": username, "password": password} result = databases.user.find_one(query) if not result: return { "message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400 status = result.get("status") if not status: return { "message": StatusCode.UserStatusOff.value[0], "data": {}, "code": StatusCode.UserStatusOff.value[1] }, 400 # 构造生成 Token 所用到的元素,Token 默认八小时过期 exp = datetime.now() + timedelta(hours=8) express = exp.strftime("%Y-%m-%d %H:%M:%S") payload = { "username": username, "password": password, "status": status, "role": result.get("role"), "express": express } token = str(jwt.encode(payload, SECRET, algorithm='HS256'), "utf8") idn, username, role = get_user_info(token) return { "message": "success", "data": { "idn": idn, "username": username, "role": role, "token": token }, "code": 200 }
def get(self): """ * @api {get} /timer/ 获取调度计划列表 * @apiPermission Role.Developer AND Owner * @apiDescription 用户只能查看自己设定的调度计划 * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {String} [username] 用户名 * @apiParam {String} [idn] 用户 ID * @apiParam {String} [project] 项目名称 * @apiParam {Int} [version] 版本号 * @apiParam {String="cron", "interval", "date"} [mode] 时间类型 * @apiParam {Int} [limit=0] Limit * @apiParam {Int} [skip=0] Skip * @apiParam {String="create", "status", "role"} [order=create] 排序字段 * @apiParam {Int=1, -1} [sort=1] 排序方式 * @apiParamExample Param-Example /timer?project=football&order=version&sort=-1 * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 200, "data": [ { "create": "2019-12-13 09:19:06", "id": "5df2e70a0286cdc6a686b9df", "idn": "5df0ea19b46116479b46c27c", "jid": "5a36346b-1f28-41de-a94e-b28c550acc42", "mode": "cron", "project": "videos", "rule": { "hour": "*", "minute": "*", "second": "*/30" }, "username": "******", "version": "1576199082" } ], "message": "success" } """ query = {} # 检查所有权 token = request.headers.get("Authorization") auth_idn, auth_username, auth_role = get_user_info(token) if auth_role != Role.SuperUser.value: username = auth_username idn = auth_idn else: username = request.args.get('username') idn = request.args.get('idn') project = request.args.get('project') version = request.args.get('version') mode = request.args.get('mode') order = request.args.get('order') or "create" sor = request.args.get('sort') or 1 limit = request.args.get('limit') or 0 skip = request.args.get('skip') or 0 if project: query["project"] = project if version: query["version"] = version if mode: query["mode"] = mode if username: query["username"] = username if idn: query["idn"] = idn # 从数据库中取出符合条件的数据 result = databases.timers.find(query).limit(int(limit)).skip(int(skip)).sort(order, int(sor)) # 构造返回信息 information = [] for i in result: info = { "id": str(i.get('_id')), "project": i.get("project"), "version": i.get("version"), "mode": i.get("mode"), "rule": i.get("rule"), "jid": i.get("jid"), "idn": i.get("idn"), "username": i.get("username"), "create": i.get("create").strftime("%Y-%m-%d %H:%M:%S") } information.append(info) return {"message": "success", "data": information, "code": 200}
def delete(self): """ * @api {delete} /timer/ 删除指定任务 * @apiPermission Role.Developer AND Owner * @apiDescription 用户只能删除自己设定的调度计划 * @apiHeader (Header) {String} Authorization Authorization value. * @apiParam {Json} jid 任务 ID * @apiParamExample Param-Example {"jid": "5a36346b-1f28-41de-a94e-b28c550acc342"} * @apiErrorExample {json} Error-Response: # status code: 403 { "code": 4002, "data": {}, "message": "is not yours" } * @apiSuccessExample {json} Success-Response: # status code: 200 { "code": 204, "data": { "Message": "Success", "count": 1, "create": "2019-19-12/13/19 09:19:06", "jid": "5a36346b-1f28-41de-a94e-b28c550acc42", "mode": "cron", "project": "videos", "rule": { "hour": "*", "minute": "*", "second": "*/30" }, "version": "1576199082" }, "message": "success" } """ jid = request.json.get('jid') if not jid: # 确保任务参数存在 return {"message": StatusCode.ParameterError.value[0], "data": {}, "code": StatusCode.ParameterError.value[1] }, 400 # 检查所有权 token = request.headers.get("Authorization") idn, username, role = get_user_info(token) query = {"jid": jid, "idn": idn, "username": username} count = databases.timers.count_documents(query) if not count and role != Role.SuperUser.value: return {"message": StatusCode.IsNotYours.value[0], "data": {}, "code": StatusCode.IsNotYours.value[1] }, 403 info = databases.timers.find_one(query) if not info: # 确保数据库中存储着对应任务 return {"message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400 # 如果指定的任务不存在会引发指定类型的异常 try: # 从数据库和任务列表中移除指定任务 scheduler.remove_job(jid) result = databases.timers.delete_one(query) message = {"count": result.deleted_count, "project": info.get("project"), "version": info.get("version"), "jid": jid, "mode": info.get("mode"), "rule": info.get("rule"), "create": info.get("create").strftime("%Y-%M-%D %H:%M:%S"), "Message": "Success"} return {"message": "success", "data": message, "code": 204}, 200 except JobLookupError: # 处理因任务不存在引发的异常,将相关提示返回给用户 return {"message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400
def post(self): """项目调度 * @api {post} /timer/ 项目调度 * @apiPermission Role.Developer AND Owner * @apiDescription 用户只能为属于自己的项目设定调度计划 * @apiParam {String} project 项目名称 - coders * @apiParam {String} version 项目版本号 - 1575734359 * @apiParam {String="cron", "interval", "date"} mode 周期类型参考 APScheduler * @apiParam {Dict} rule 周期规则参考 APScheduler * @apiParamExample Param-Example { "project": "lol", "version": "1576206368", "mode": "cron", "rule": {"hour": "*", "minute": "*/15"} } * @apiErrorExample {json} Error-Response: # status code: 400 { "code": 4002, "data": {}, "message": "not found" } * @apiSuccessExample {json} Success-Response: # status code: 201 { "code": 201, "data": { "inserted": "5df2e70a0286cdc6a686b9df", "jid": "5a36346b-1f28-41de-a94e-b28c550acc42", "project": "videos", "version": "1576199082" }, "message": "success" } """ project = request.json.get('project') version = request.json.get('version') mode = request.json.get('mode') rule = request.json.get('rule') if not project or not rule or not version: return {"message": StatusCode.ParameterError.value[0], "data": {}, "code": StatusCode.ParameterError.value[1] }, 400 if not storages.exists(project, version): return {"message": StatusCode.NotFound.value[0], "data": {}, "code": StatusCode.NotFound.value[1] }, 400 # 检查所有权 token = request.headers.get("Authorization") idn, username, role = get_user_info(token) count = 1 if role != Role.SuperUser.value: count = databases.deploy.count_documents( { "project": project, "version": version, "idn": idn, "username": username } ) if not count: return {"message": StatusCode.IsNotYours.value[0], "data": {}, "code": StatusCode.IsNotYours.value[1] }, 403 # 生成唯一值作为任务标识 jid = str(uuid4()) # 添加任务,这里用双星号传入时间参数 try: scheduler.add_job(performer, mode, id=jid, args=[project, version, mode, rule, jid, idn, username], **rule) except Exception as exc: return {"message": StatusCode.ParameterError.value[0], "data": {}, "code": StatusCode.ParameterError.value[1] }, 400 # 将信息保存到数据库 message = {"project": project, "version": version, "mode": mode, "rule": rule, "jid": jid, "idn": idn, "username": username, "create": datetime.now()} inserted = databases.timers.insert_one(message).inserted_id return {"message": "success", "data": {"project": project, "version": version, "jid": jid, "inserted": str(inserted)}, "code": 201}, 201