class AccessCount(PluginBase): """ 记录与统计每天访问数据 """ pvKey = "EauDouce:AccessCount:pv:hash" ipKey = "EauDouce:AccessCount:ip:" + get_today("%Y%m%d") urlKey = uvKey def Record_ip_pv(self, **kwargs): """ 记录ip、ip """ data = kwargs.get("access_data") self.asyncQueue.enqueue(Click2Redis, data, self.pvKey, self.ipKey, self.urlKey) self.asyncQueueLow.enqueue(Click2MySQL, data) def register_cep(self): return {"after_request_hook": self.Record_ip_pv} def register_tep(self): """注册博客详情页功能区代码""" tep = { "blog_show_funcarea_string": "<scan id='AccessCount'></scan>", "blog_show_script_include": "AccessCountJs.html" } return tep def register_bep(self): """ 注册一个查询uv接口 """ return {"prefix": "/AccessCount", "blueprint": AccessCountBlueprint}
def blog_create(self, **kwargs): "创建博客文章接口" res = {"msg": None, "success": False, "code": 0} blog_title = kwargs.get('title') blog_content = kwargs.get('content') blog_tag = kwargs.get("tag") blog_catalog = kwargs.get("catalog", "未分类") blog_sources = kwargs.get("sources", "原创") blog_author = kwargs.get("author", "admin") blog_ctime = get_today() if blog_title and blog_content and blog_ctime and blog_author: sql = 'INSERT INTO blog_article (title,content,create_time,tag,catalog,sources,author) VALUES (%s, %s, %s, %s, %s, %s, %s)' logger.api.info(sql % (blog_title, blog_content, blog_ctime, blog_tag, blog_catalog, blog_sources, blog_author)) try: blog_id = self.mysql_write.insert(sql, blog_title, blog_content, blog_ctime, blog_tag, blog_catalog, blog_sources, blog_author) except Exception, e: logger.api.error(e, exc_info=True) res.update( msg="Blog article failed to write, please try to resubmit.", code=1000013) else: res.update(msg="blog write success.", success=True, data=blog_id)
class AccessCount(PluginBase): """ 记录与统计每天访问数据 """ pvKey = "EauDouce:AccessCount:pv:hash" ipKey = "EauDouce:AccessCount:ip:" + get_today("%Y%m%d") urlKey = uvKey def Record_ip_pv(self, **kwargs): """ 记录ip、ip """ data = { "status_code": kwargs['response'].status_code, "method": request.method, "ip": request.headers.get('X-Real-Ip', request.remote_addr), "url": request.url, "referer": request.headers.get('Referer'), "agent": request.headers.get("User-Agent"), "TimeInterval": "%0.2fs" % float(time.time() - g.startTime) } if request.endpoint in ("front.blogShow", "front.blogEnjoy"): self.asyncQueue.enqueue(Click2MySQL, data) def register_hep(self): return {"after_request_hook": self.Record_ip_pv} def register_tep(self): """注册博客详情页功能区代码""" tep = { "blog_show_funcarea": "<scan id='AccessCount'></scan>", "blog_show_script": "AccessCountJs.html" } return tep def register_bep(self): """ 注册一个查询uv接口 """ return {"prefix": "/AccessCount", "blueprint": AccessCountBlueprint}
def Record_ip_pv(self, *args, **kwargs): """ 记录ip、ip """ resp = kwargs.get("response") or args[0] pvKey = "EauDouce:AccessCount:pv:hash" pipe = pb.redis.pipeline() if request.endpoint in ("front.blogShow", "front.blogEnjoy") and resp.status_code == 200: pipe.hincrby(blogPvKey, request.path.split("/")[-1], 1) if request.endpoint == "api.wechatapplet" and request.args.get( "Action") == "get_blogId": pipe.hincrby(blogPvKey, "%s.html" % request.args.get("blogId"), 1) # pv pipe.hincrby(pvKey, get_today("%Y%m%d"), 1) try: pipe.execute() except: pass
def blog_update(self, **kwargs): "更新博客文章接口" res = {"msg": None, "success": False, "code": 0} blog_title = kwargs.get('title') blog_content = kwargs.get('content') blog_tag = kwargs.get("tag") blog_catalog = kwargs.get("catalog", "未分类") blog_sources = kwargs.get("sources", "原创") blog_author = kwargs.get("author", "admin") blog_blogId = kwargs.get("blogId") blog_utime = get_today() try: blog_blogId = int(blog_blogId) except ValueError, e: logger.api.error(e, exc_info=True) res.update(msg="blogId form error.", code=1000015)
def post_apply_author(self, username): """ 申请成为作者 #username(str): 用户名 """ res = {"msg": None, "code": 0} if username and username in self.user_get_list(True).get("data"): if username in self.user_get_authors().get("data"): res.update(msg="The current user is the author") else: sql = "INSERT INTO blog_applyauthor (username, req_time) VALUES(%s, %s)" try: mid = self.mysql_write.insert(sql, username, get_today()) except IntegrityError, e: logger.api.debug(e) res.update(msg="The author has applied.", code=400006) except Exception, e: logger.api.error(e, exc_info=True) res.update(msg="server error", code=400004) else: res.update(success=True, data=mid)
def upload(): """上传逻辑: 0. 判断是否登录,如果未登录则判断是否允许匿名上传 1. 获取上传的文件,判断允许格式 2. 生成文件名、唯一sha值、上传目录等,选择图片存储的后端钩子(单一) - 存储图片的钩子目前版本仅一个,默认是up2local(如果禁用则保存失败) 3. 解析钩子数据,钩子回调数据格式:{code=?, sender=hook_name, src=?} - 后端保存失败或无后端的情况都要立刻返回响应 4. 此时保存图片成功,持久化存储到全局索引、用户索引 5. 返回响应:{code:0, data={src=?, sender=success_saved_hook_name}} """ res = dict(code=1, msg=None) #: 匿名上传开关检测 if not is_true(g.cfg.anonymous) and not g.signin: res.update(code=403, msg="Anonymous user is not sign in") return res f = request.files.get('picbed') #: 实时获取后台配置中允许上传的后缀,如: jpg|jpeg|png allowed_suffix = partial(allowed_file, suffix=parse_valid_verticaline(g.cfg.upload_exts)) if f and allowed_suffix(f.filename): try: g.rc.ping() except RedisError as e: logger.error(e, exc_info=True) res.update(code=2, msg="Program data storage service error") return res stream = f.stream.read() suffix = splitext(f.filename)[-1] filename = secure_filename(f.filename) if "." not in filename: filename = "%s%s" % (generate_random(8), suffix) #: 根据文件名规则重定义图片名 upload_file_rule = g.cfg.upload_file_rule if upload_file_rule in ("time1", "time2", "time3"): filename = "%s%s" % (gen_rnd_filename(upload_file_rule), suffix) #: 上传文件位置前缀规则 upload_path_rule = g.cfg.upload_path_rule if upload_path_rule == 'date1': upload_path = get_today("%Y/%m/%d") elif upload_path_rule == 'date2': upload_path = get_today("%Y%m%d") else: upload_path = '' upload_path = join(g.userinfo.username or 'anonymous', upload_path) #: 定义文件名唯一索引 sha = "sha1.%s.%s" % (get_current_timestamp(True), sha1(filename)) #: 定义保存图片时仅使用某些钩子,如: up2local #: TODO 目前版本仅允许设置了一个,后续考虑聚合 includes = parse_valid_comma(g.cfg.upload_includes or 'up2local') if len(includes) > 1: includes = [choice(includes)] #: TODO 定义保存图片时排除某些钩子,如: up2local, up2other # excludes = parse_valid_comma(g.cfg.upload_excludes or '') #: 钩子返回结果(目前版本最终结果中应该最多只有1条数据) data = [] #: 保存图片的钩子回调 def callback(result): logger.info(result) if result["sender"] == "up2local": result["src"] = url_for("static", filename=join(UPLOAD_FOLDER, upload_path, filename), _external=True) data.append(dfr(result)) #: 调用钩子中upimg_save方法 current_app.extensions["hookmanager"].call( _funcname="upimg_save", _callback=callback, _include=includes, filename=filename, stream=stream, upload_path=upload_path, local_basedir=join(current_app.root_path, current_app.static_folder, UPLOAD_FOLDER)) #: 判定后端存储全部失败时,上传失败 if not data: res.update(code=1, msg="No valid backend storage service") return res if len(data) == len([i for i in data if i.get("code") != 0]): res.update( code=1, msg="All backend storage services failed to save pictures") return res #: 存储数据 defaultSrc = data[0]["src"] pipe = g.rc.pipeline() pipe.sadd(rsp("index", "global"), sha) if g.signin and g.userinfo.username: pipe.sadd(rsp("index", "user", g.userinfo.username), sha) pipe.hmset( rsp("image", sha), dict( sha=sha, filename=filename, upload_path=upload_path, user=g.userinfo.username if g.signin else 'anonymous', ctime=get_current_timestamp(), status='enabled', # disabled, deleted src=defaultSrc, sender=data[0]["sender"], senders=json.dumps(data))) try: pipe.execute() except RedisError as e: logger.error(e, exc_info=True) res.update(code=3, msg="Program data storage service error") else: res.update( code=0, filename=filename, sender=data[0]["sender"], api=url_for("api.shamgr", sha=sha, _external=True), ) #: format指定图片地址的显示字段,默认src,可以用点号指定 #: 比如data.src,那么返回格式{code, filename..., data:{src}, ...} fmt = request.form.get("format", request.args.get("format")) res.update(format_upload_src(fmt, defaultSrc)) else: res.update(msg="No file or image format allowed") return res
def upload(): """上传逻辑: 0. 判断是否登录,如果未登录则判断是否允许匿名上传 1. 获取上传的文件,判断允许格式 - 拦截下判断文件,如果为空,尝试获取body中提交的picbed - 如果picbed是合法base64,那么会返回Base64FileStorage类; 如果picbed是合法url[图片],那么服务端会自动下载,返回ImgUrlFileStorage类; 否则为空。 PS: 允许DATA URI形式, eg: data:image/png;base64,the base64 of image 2. 生成文件名、唯一sha值、上传目录等,选择图片存储的后端钩子(单一) - 存储图片的钩子目前版本仅一个,默认是up2local(如果禁用则保存失败) - 如果提交album参数会自动创建相册,否则归档到默认相册 3. 解析钩子数据,钩子回调数据格式:{code=?, sender=hook_name, src=?} - 后端保存失败或无后端的情况都要立刻返回响应 4. 此时保存图片成功,持久化存储到全局索引、用户索引 5. 返回响应:{code:0, data={src=?, sender=success_saved_hook_name}} - 允许使用一些参数调整响应数据、格式 """ res = dict(code=1, msg=None) #: 文件域或base64上传字段 FIELD_NAME = g.cfg.upload_field or "picbed" #: 匿名上传开关检测 if not is_true(g.cfg.anonymous) and not g.signin: res.update(code=403, msg="Anonymous user is not sign in") return res #: 相册名称,可以是任意字符串 album = (request.form.get("album") or getattr(g, "up_album", "")) if g.signin else 'anonymous' #: 实时获取后台配置中允许上传的后缀,如: jpg|jpeg|png allowed_suffix = partial(allowed_file, suffix=parse_valid_verticaline(g.cfg.upload_exts)) #: 尝试读取上传数据 fp = request.files.get(FIELD_NAME) #: 当fp无效时尝试读取base64或url if not fp: picstrurl = request.form.get(FIELD_NAME) filename = request.form.get("filename") if picstrurl: if picstrurl.startswith("http://") or \ picstrurl.startswith("https://"): fp = ImgUrlFileStorage(picstrurl, filename).getObj else: try: #: base64在部分场景发起http请求时,+可能会换成空格导致异常 fp = Base64FileStorage(picstrurl, filename) except ValueError as e: logger.debug(e) if fp and allowed_suffix(fp.filename): try: g.rc.ping() except RedisError as e: logger.error(e, exc_info=True) res.update(code=2, msg="Program data storage service error") return res stream = fp.stream.read() suffix = splitext(fp.filename)[-1] filename = secure_filename(fp.filename) if "." not in filename: filename = "%s%s" % (generate_random(8), suffix) #: 根据文件名规则重定义图片名 upload_file_rule = ( g.userinfo.ucfg_upload_file_rule or g.cfg.upload_file_rule) if is_true( g.cfg.upload_rule_overridden) else g.cfg.upload_file_rule if upload_file_rule in ("time1", "time2", "time3"): filename = "%s%s" % (gen_rnd_filename(upload_file_rule), suffix) #: 上传文件位置前缀规则 upload_path_rule = ( g.userinfo.ucfg_upload_path_rule or g.cfg.upload_path_rule) if is_true( g.cfg.upload_rule_overridden) else g.cfg.upload_path_rule if upload_path_rule == 'date1': upload_path = get_today("%Y/%m/%d") elif upload_path_rule == 'date2': upload_path = get_today("%Y%m%d") else: upload_path = '' upload_path = join(g.userinfo.username or 'anonymous', upload_path) #: 定义文件名唯一索引 sha = "sha1.%s.%s" % (get_current_timestamp(True), sha1(filename)) #: 定义保存图片时仅使用某些钩子,如: up2local #: TODO 目前版本仅允许设置了一个,后续聚合 includes = parse_valid_comma(g.cfg.upload_includes or 'up2local') if len(includes) > 1: includes = [choice(includes)] #: TODO 定义保存图片时排除某些钩子,如: up2local, up2other # excludes = parse_valid_comma(g.cfg.upload_excludes or '') #: 钩子返回结果(目前版本最终结果中应该最多只有1条数据) data = [] #: 保存图片的钩子回调 def callback(result): logger.info(result) if result["sender"] == "up2local": result["src"] = url_for("static", filename=join(UPLOAD_FOLDER, upload_path, filename), _external=True) data.append(dfr(result)) #: 调用钩子中upimg_save方法 current_app.extensions["hookmanager"].call( _funcname="upimg_save", _callback=callback, _include=includes, filename=filename, stream=stream, upload_path=upload_path, local_basedir=join(current_app.root_path, current_app.static_folder, UPLOAD_FOLDER)) #: 判定后端存储全部失败时,上传失败 if not data: res.update(code=1, msg="No valid backend storage service") return res if len(data) == len([i for i in data if i.get("code") != 0]): res.update( code=1, msg="All backend storage services failed to save pictures", errors={ i["sender"]: i["msg"] for i in data if i.get("code") != 0 }, ) return res #: 存储数据 defaultSrc = data[0]["src"] pipe = g.rc.pipeline() pipe.sadd(rsp("index", "global"), sha) if g.signin and g.userinfo.username: pipe.sadd(rsp("index", "user", g.userinfo.username), sha) pipe.hmset( rsp("image", sha), dict( sha=sha, album=album, filename=filename, upload_path=upload_path, user=g.userinfo.username if g.signin else 'anonymous', ctime=get_current_timestamp(), status='enabled', # disabled, deleted src=defaultSrc, sender=data[0]["sender"], senders=json.dumps(data), agent=request.form.get("origin", request.headers.get('User-Agent', '')), method=getUploadMethod(fp.__class__.__name__), )) try: pipe.execute() except RedisError as e: logger.error(e, exc_info=True) res.update(code=3, msg="Program data storage service error") else: res.update( code=0, filename=filename, sender=data[0]["sender"], api=url_for("api.shamgr", sha=sha, _external=True), ) #: format指定图片地址的显示字段,默认src,可以用点号指定 #: 比如data.src,那么返回格式{code, filename..., data:{src}, ...} #: 比如imgUrl,那么返回格式{code, filename..., imgUrl(=src), ...} fmt = request.form.get("format", request.args.get("format")) res.update(format_upload_src(fmt, defaultSrc)) else: res.update(msg="No file or image format error") return res
def upload(): #: 视频上传接口,上传流程: #: 1. 必须登录,通过固定的picbed字段获取上传内容 #: 2. 生成sha,文件名规则是年月日时分秒原名,上传到用户目录下 #: 3. 保存到后端,存储数据返回响应 res = dict(code=1, msg=None) if not g.signin: res.update(code=403, msg="Anonymous user is not sign in") return res if g.userinfo.status != 1: msg = ("Pending review, cannot upload pictures" if g.userinfo.status in (-2, -1) else "The user is disabled, no operation") res.update(code=403, msg=msg) return res allowed_suffix = partial(allowed_file, suffix=("mp4", "ogg", "ogv", "webm", "3gp", "mov")) fp = request.files.get("picbed") title = request.form.get("title") or "" if not fp or not allowed_suffix(fp.filename): res.update(msg="No file or image format error") return res suffix = splitext(fp.filename)[-1] filename = secure_filename(fp.filename) if "." not in filename: filename = "%s%s" % (generate_random(8), suffix) stream = fp.stream.read() upload_path = join(g.userinfo.username, get_today("%Y/%m/%d")) sha = "sha256.%s.%s" % (get_current_timestamp(True), sha256(filename)) includes = parse_valid_comma(g.cfg.upload_includes or 'up2local') if len(includes) > 1: includes = [choice(includes)] data = current_app.extensions["hookmanager"].call( _funcname="upimg_save", _include=includes, _kwargs=dict(filename=filename, stream=stream, upload_path=upload_path, local_basedir=join(current_app.root_path, current_app.static_folder, UPLOAD_FOLDER))) for i, result in enumerate(data): if result["sender"] == "up2local": data.pop(i) result["src"] = url_for("static", filename=join(UPLOAD_FOLDER, upload_path, filename), _external=True) data.insert(i, result) #: 判定后端存储全部失败时,上传失败 if not data: res.update(code=1, msg="No valid backend storage service") return res if is_all_fail(data): res.update( code=1, msg="All backend storage services failed to save pictures", ) return res #: 存储数据 defaultSrc = data[0]["src"] pipe = g.rc.pipeline() pipe.sadd(rsp("index", "video", g.userinfo.username), sha) pipe.hmset( rsp("video", sha), dict( sha=sha, user=g.userinfo.username, title=title, filename=filename, upload_path=upload_path, ctime=get_current_timestamp(), src=defaultSrc, sender=data[0]["sender"], senders=json.dumps(data), )) try: pipe.execute() except RedisError: res.update(code=3, msg="Program data storage service error") else: res.update( code=0, sender=data[0]["sender"], src=defaultSrc, ) return res
def post(self): """login and registry, with url args: 1. action=log/reg, default is log; post data: 1. username, 2. password, 3. email """ NULL = None res = {"url": request.url, "msg": None, "success": False} username = request.form.get("username") password = request.form.get("password") email = request.form.get("email", NULL) action = request.args.get("action") #log or reg (登录or注册) #chck username and password value if not username or not password: res.update(msg="Invaild username or password", code=10001) logger.api.debug(res) return res #check username and password length if 5 <= len(username) < 30 and 5 <= len(password) < 30: MD5password = md5(password) else: res.update({ 'msg': 'username or password length requirement is greater than or equal to 5 less than 30', 'code': 10002 }) logger.api.warn(res) return res #check username pattern if not user_pat.match(username): res.update({'msg': 'username is not valid', 'code': 10003}) logger.api.warn(res) return res if email and mail_pat.match(email) == None: res.update({'msg': "email format error", 'code': 10004}) logger.api.warn(res) return res #Start Action with (log, reg) if action == 'SignIn': logger.api.debug(RegisteredUser()) logger.api.debug( "MD5password: %s, DBpassword: %s, username: %s" % (MD5password, RegisteredUserInfo(username).get("lauth_password"), username)) if username in RegisteredUser(): if MD5password == RegisteredUserInfo(username).get( "lauth_password"): res.update({ 'msg': 'Password authentication success at sign in', 'code': 0, "success": True }) else: res.update({ 'msg': 'Password authentication failed at sign in', 'code': 10005, "success": False }) else: res.update({'msg': 'username not exists', 'code': 10006}) logger.api.debug(res) return res elif action == 'SignUp': try: AuthSQL = "INSERT INTO LAuth (lauth_username, lauth_password) VALUES(%s, %s)" logger.api.info(AuthSQL) mysql.insert(AuthSQL, username, MD5password) UserSQL = "INSERT INTO User (username, email, time, avatar) VALUES(%s, %s, %s, %s)" mysql.insert(UserSQL, username, email, get_today(), "/static/img/avatar/default.jpg") except IntegrityError, e: logger.api.error(e, exc_info=True) res.update({ 'msg': 'username already exists, cannot be registered!', 'code': 10007 }) logger.api.warn(res) return res except Exception, e: logger.api.error(e, exc_info=True) res.update(msg="server error", code=-1) logger.api.error(res) return res
except Exception, e: logger.error(e, exc_info=True) res.update(msg="System is abnormal") else: res.update( msg="Sent verification code, valid for 300 seconds", success=True) else: res.update( msg="Mail delivery failed, please try again later") elif phone_check(account): # 短信验证码,要求同个场景每个手机每天只能发送3次,每个场景验证码有效期5min,校验的话,libs.auth.Authentication类中`__check_sendVcode`方法 phone = account key = "passport:vcode:{}:{}".format(scene, phone) keyTimes = "passport:sendsms:{}:{}:{}".format(scene, phone, get_today("%Y%m%d")) try: hasKey = g.redis.exists(key) keyData = int(g.redis.get(keyTimes) or 0) except Exception, e: logger.error(e, exc_info=True) res.update(msg="System is abnormal") else: if hasKey: res.update( msg= "Have sent the verification code, please check the mobile phone" ) else: if keyData >= 3: res.update(
if result["success"]: try: g.redis.set(key, vcode) g.redis.expire(key, 300) except Exception, e: logger.error(e, exc_info=True) res.update(msg="System is abnormal") else: res.update(msg="Sent verification code, valid for 300 seconds", success=True) else: res.update(msg="Mail delivery failed, please try again later") elif phone_check(account): # 短信验证码,要求同个场景每个手机每天只能发送3次,每个场景验证码有效期5min,校验的话,libs.auth.Authentication类中`__check_sendVcode`方法 phone = account key = "passport:vcode:{}:{}".format(scene, phone) keyTimes = "passport:sendsms:{}:{}:{}".format(scene, phone, get_today("%Y%m%d")) try: hasKey = g.redis.exists(key) keyData = int(g.redis.get(keyTimes) or 0) except Exception, e: logger.error(e, exc_info=True) res.update(msg="System is abnormal") else: if hasKey: res.update(msg="Have sent the verification code, please check the mobile phone") else: if keyData >= 3: res.update(msg="Current scene The number of text messages sent by your mobile phone has reached the upper limit today") else: vcode = generate_digital_verification_code() result = SendSms(phone, vcode)