def bucket_web_for_independent_domain(web_path=''): # 跟 bucket web 一样, 但是呈现的是独立域名, 放到最后被 app.route 添加,以避免影响其它 view 的逻辑 # 系统自带的前端资源 if re.match("/(_system|admin|service|bucket)/", request.path): abort( 404, "custom url is not allowed to startswith (_system|admin|service|bucket)" ) frontend_response = send_static_frontend_resource() if frontend_response: return frontend_response if not web_path and not get_buckets_size(): # 还没有 bucket 的时候,允许用户进行安装的操作 return p_redirect("/__create_bucket?code=admin") bucket = get_bucket_from_request() if bucket: if request.path in ['/robot.txt', '/robots.txt']: if not get_bucket_private_config( bucket, "enable_web_robots", default=True): # 禁用爬虫 robot_content = 'User-agent: *\nDisallow: /' return Response(robot_content, mimetype='text/plain') if request.url.startswith("http://") and get_bucket_private_config( bucket, "enable_https_force_redirect", default=False): # 强制跳转 https, 不用永久 301 code,避免用户自己切换后不成功 return redirect(request.url.replace('http://', 'https://', 1), code=302) set_site_in_request(get_bucket_site_configs(bucket)) return render_bucket(bucket, web_path) else: abort(404, 'no bucket found') ################# for web pages ends ##########
def get_doc(self, path, **kwargs): path = path.lower().strip('/').strip() if "?" in path: path = path.split("?")[0] if "#" in path: path = path.split("#")[0] doc = self.get_doc_by_url(path) or self.get_doc_by_path(path) or {} if path == 'settings.json': doc['raw_content'] = json.dumps(get_bucket_site_configs( self.bucket), indent=4, ensure_ascii=False) #ensure_ascii if not doc: return if doc.get("_zipped"): doc["raw_content"] = get_raw_content_by_record(doc) doc_type = doc.get('_type') or doc.get('type') if kwargs.get('type') and kwargs.get('type') != doc_type: return None as_context_doc = kwargs.get('as_context_doc') if as_context_doc and doc: set_doc_in_request(doc) return doc
def get_current_data_root(): bucket = get_bucket_in_request_context() if not bucket: return "" data_root = get_data_root_in_request() if data_root: return data_root site_configs = get_bucket_site_configs(bucket) if site_configs: data_root = smart_unicode(site_configs.get("posts_root") or "").strip().strip("/") return data_root else: return ""
def add_new_comment_web_view(): bucket = request.values.get("bucket") site_configs = get_bucket_site_configs(bucket) if not site_configs: comments_allowed = False else: comments_allowed = site_configs.get("comments") if not comments_allowed: abort(404, 'comment is not allowed in this site') new_comment_doc = add_comment() if new_comment_doc.get('error'): # 错误信息 return jsonify(new_comment_doc) if request.values.get('format') in ['json', 'JSON']: return jsonify(new_comment_doc) else: return render_api_template('comment.jade', comment=new_comment_doc)
def get_auto_nav_items(bucket): # 自动生成的 pages_configs = get_bucket_pages_configs(bucket) nav_items = [] homepage_url = '/' if request.args.get('status') == 'loaded': homepage_url = '/?status=loaded' homepage_nav_item = dict(name='Home', url=homepage_url) nav_items.append(homepage_nav_item) site_configs = get_bucket_site_configs(bucket) albums_root = smart_unicode(site_configs.get("albums_root", "")).strip() if albums_root: nav_items.append(dict(name="Album", url="/album")) wiki_configs = get_json_content_by_path(bucket, "__wiki.json", force_dict=True) wiki_root = wiki_configs.get("wiki_root") enable_wiki_nodes = auto_type(wiki_configs.get("enable_wiki_nodes", True)) if wiki_root: nav_items.append(dict(name="Wiki", url="/wiki")) if enable_wiki_nodes: nav_items.append(dict(name="Wiki Nodes", url="/wiki_nodes")) if 'categories.jade' in pages_configs: # 有 categories.jade 的呈现 nav_items.append(dict(name='Categories', url='/categories')) if 'archive.jade' in pages_configs: # archive 页面 nav_items.append(dict(name='Archive', url='/archive')) if has_markdown_record_by_path_prefix(bucket, "links"): nav_items.append(dict(name='Links', url='/__page/links')) if has_markdown_record_by_path_prefix(bucket, "about"): nav_items.append(dict(name='About', url='/__page/about')) if has_markdown_record_by_path_prefix(bucket, "contact"): nav_items.append(dict(name='Contact', url='/__page/contact')) if 'feed.jade' in pages_configs: nav_items.append(dict(name='Feed', url='/feed')) return nav_items
def render_as_static_file_for_farbox_bucket(path): if not path or path == "/": path = "index.html" bucket = get_bucket_in_request_context() if not bucket: return record = get_record_by_path(bucket, path) if path == "favicon.ico" and not record: record = get_record_by_path(bucket, "_direct/favicon.ico") # 兼容 Bitcron if not record: return record_path = get_path_from_record(record) if record_path and record_path.startswith("/_data/"): ext = os.path.splitext(record_path)[-1].strip(".").lower() if ext in ["csv"] and not is_bucket_login(bucket): return abort( 404, "csv under /_data is not allowed to download directly") set_context_value_from_request("is_static_file", True) if record.get('compiled_type') and record.get('compiled_content'): raw_content = record.get('compiled_content') content_type = record.get('compiled_type') raw_content = to_bytes(raw_content) mimetype = content_type or guess_type( path) or 'application/octet-stream' compiled_file_response = send_file(io.BytesIO(to_bytes(raw_content)), mimetype=mimetype) return compiled_file_response else: # 先对应是否防盗链的逻辑 site_configs = get_bucket_site_configs(bucket) anti_theft_chain = site_configs.get("anti_theft_chain", True) if anti_theft_chain and request.path.strip('/') in ['favicon.ico']: anti_theft_chain = False if anti_theft_chain and request.referrer: refer_host = get_host_from_url(request.referrer) if refer_host != request.host and "." in request.path: return abort(404, "this url is not allowed for outside") return storage.get_download_response_for_record(bucket=bucket, record_data=record, try_resized_image=True)
def comments_as_html(obj): doc = obj site_configs = get_bucket_site_configs() should_hide_comments = not get_value_from_data(site_configs, 'comments', True) third_party_comments_script = get_value_from_data( site_configs, 'third_party_comments_script') or '' third_party_comments_script = smart_unicode( third_party_comments_script.strip()) if third_party_comments_script: # 有第三方评论脚本,直接进行替换 should_hide_comments = True if not should_hide_comments and get_value_from_data( doc, 'metadata.comment') in [False, 'no', 'No']: # doc 本身不允许显示 should_hide_comments = True if should_hide_comments: # 不显示评论系统 return third_party_comments_script html = render_api_template('comments.jade', doc=doc) return html
def posts_root(self): if not self.bucket: return "" site_configs = get_bucket_site_configs(self.bucket) root = smart_unicode(site_configs.get("posts_root", "")).strip() return root
def site_configs(self): return get_bucket_site_configs()
def get_data(type='post', limit=None, page=None, path=None, level=None, level_start=None, level_end=None, excludes=None, status=None, with_page=True, pager_name=None, sort='desc', return_count=False, date_start=None, date_end=None, ignore_marked_id=None, prefix_to_ignore=None, keywords=None, min_limit=0, **kwargs): # 对 Bitcron 的兼容, tag for get_data tag_to_match = kwargs.get("tags") or kwargs.get("tag") if tag_to_match: if isinstance(tag_to_match, (list, tuple)): tag_to_match = tag_to_match[0] if tag_to_match: tag_match_records = get_records_by_tag( get_bucket_in_request_context(), tag=tag_to_match, sort_by="-date" if sort == "desc" else "date") if return_count: return len(tag_match_records) return tag_match_records if isinstance(type, (list, tuple)) and type: type = type[0] if isinstance(type, string_types ) and "+" in type: # 兼容旧的 Bitcron, 查询索引所限,只能单一 post type = type.split("+")[0].strip() if type == "all": type = None if level is not None and level_start is None and level_end is None: # 重新计算 level,如果给 level,是相当于 path 的相对路径 level_start, level_end = level_to_level_start_and_end(level=level, path=path) if prefix_to_ignore is None: if type in ['post']: prefix_to_ignore = '_' if ignore_marked_id is None: if type in ['post']: ignore_marked_id = True if status == "all": ignore_marked_id = False else: ignore_marked_id = False bucket = get_bucket_in_request_context() if type == "post" and path is None and bucket: # 尝试取得 posts_root,可以分离数据, 这是默认的情况, 即使指定了 path = "", 也不走这个逻辑 site_configs = get_bucket_site_configs(bucket) posts_root = smart_unicode(site_configs.get("posts_root", "")).strip() if posts_root: path = posts_root if keywords: obj_list = pg_with_keywords_search( bucket=bucket, keywords=keywords, limit=limit, with_page=with_page, page=page, pager_name=pager_name, path=path, excludes=excludes, status=status, sort_by=sort, return_total_count=return_count, date_start=date_start, date_end=date_end, min_limit=min_limit, ) else: obj_list = auto_pg( bucket=bucket, data_type=type, limit=limit, with_page=with_page, page=page, pager_name=pager_name, path=path, level_start=level_start, level_end=level_end, excludes=excludes, status=status, sort_by=sort, # for position 才特殊处理 return_total_count=return_count, date_start=date_start, date_end=date_end, ignore_marked_id=ignore_marked_id, prefix_to_ignore=prefix_to_ignore, min_limit=min_limit, ) fields = kwargs.get('fields') if fields and isinstance(fields, (list, tuple)) and obj_list and isinstance( obj_list, (list, tuple)): obj_list = [{ key: value for key, value in obj.items() if key in fields } for obj in obj_list if isinstance(obj, dict)] return obj_list
def show_albums_as_sub_site(): bucket = get_bucket_in_request_context() if not bucket: return request_path = get_request_path().strip("/") if not re.match("album(/|$)", request_path): return if "." in request_path and guess_type( request_path, default_type="").startswith("image/"): # 可能是直接的图片地址,避免被整个 album 给拦截了 return site_configs = get_bucket_site_configs(bucket) albums_root = smart_unicode(site_configs.get("albums_root", "")) if not albums_root: return albums_root = albums_root.strip("/") #todo 允许直接设定 / ? albums_home_sort = site_configs.get("albums_home_sort", "-date") album_items_sort = site_configs.get("album_items_sort", "-date") page_title = site_configs.get("albums_title") or get_just_name( albums_root, for_folder=True) if re.match("album/?$", request_path): # folders doc_type = "folder" doc_sort = albums_home_sort under = albums_root else: doc_type = "image" doc_sort = album_items_sort under = "%s/%s" % (albums_root, request_path.split("/", 1)[-1].strip("/")) folder_doc = get_record_by_path(bucket=bucket, path=under) if folder_doc: page_title = folder_doc.get("title") or get_just_name( folder_doc.get("path"), for_folder=True) else: page_title = get_just_name(under, for_folder=True) if doc_sort not in ["date", "-date"]: doc_sort = "-date" limit = 15 # todo 可以设定? doc_level = 1 # min_images_count = 1 docs = Data.get_data(path=under, type=doc_type, limit=limit, level=doc_level, sort=doc_sort, pager_name='album_docs_pager', exclude='default') if doc_type == "folder": for doc in docs: doc_path = get_path_from_record(doc, is_lower=True) relative_path = doc_path.replace(albums_root.lower(), "", 1).strip("/") doc["album_url"] = "/album/%s" % relative_path return render_api_template( "builtin_theme_album_waterfall.jade", docs=docs, page_title=page_title, )
def update_site_obj_first(self): site_obj = get_bucket_site_configs(self.bucket) if isinstance(site_obj, dict): self.update(site_obj)
def get_response_for_resized_image(bucket, record, storage): from farbox_bucket.server.helpers.file_manager import sync_file_by_server_side if not record or not isinstance(record, dict): return if record.get("_get_im_failed"): return #if not is_doc_modified(record, date_field='mtime'): #todo ???? # return get_304_response() if request.args.get('origin') in ['true']: return # ignore relative_path = record.get('path') if not relative_path: return file_id = get_file_id_from_record(record) if not file_id: return ext = os.path.splitext(relative_path)[-1].strip('.').lower() if ext not in ['png', 'jpg', 'jpeg', 'bmp', 'png']: return if not can_resize_by_system(): return site_settings = get_bucket_site_configs(bucket) image_max_width = to_int(site_settings.get('image_max_width'), default_if_fail=None) size_from_request = request.values.get("size") if size_from_request == "m": image_max_width = 600 if not isinstance(image_max_width, int) or image_max_width < 100: image_max_width = None image_max_height = to_int(site_settings.get('image_max_height'), default_if_fail=None) if not isinstance(image_max_height, int) or image_max_height < 100: image_max_height = None if not image_max_height and not image_max_width: return # 处理 image_max_type 允许的值 image_max_type = (get_site_config('image_max_type') or 'webp-jpg').lower().strip() if not isinstance(image_max_type, string_types): image_max_type = 'webp-jpg' if not re.match("[a-z-]+$", image_max_type): image_max_type = "jpg" if "webp" in image_max_type: request_client_accept = request.headers.get("Accept") or "" if "image/webp" not in request_client_accept: # webp 浏览器不支持的情况下 image_max_type = "png" if ext == "png" else "jpg" if image_max_type == 'webp-jpg': mimetype = 'image/webp' else: if '/' not in image_max_type: mimetype = 'image/%s' % image_max_type else: mimetype = image_max_type if mimetype == 'image/jpg': mimetype = 'image/jpeg' if "webp" not in mimetype: # 非 webp,并且文件中能读取到 size 的,判断是否需要进行缩略图的处理 image_width = record.get("image_width") image_height = record.get("image_height") if image_width and image_height: if image_max_width and image_width < image_max_width: return if image_max_height and image_height < image_max_height: return if "webp" in image_max_type: ext = "webp" cache_image_filepath = "_cache/images/%s/%s-%s-%s.%s" % ( file_id, image_max_type, image_max_width, image_max_height, ext) cache_image_record = get_record_by_path(bucket=bucket, path=cache_image_filepath) if cache_image_record: # 已经存在了 return storage.as_web_response(bucket=bucket, record=cache_image_record, mimetype=mimetype, try_resized_image=False) else: # todo 这里应该增加一个 time-block,避免潜在的攻击存在 # 构建一个缩略图 raw_content = storage.get_raw_content(bucket=bucket, record_data=record) if not raw_content: return try: im = get_im(raw_content) except: im = None if not im: # 标识,下次就不会尝试了 update_record(bucket, record_id=record.get("_id"), _get_im_failed=True) return degrees = to_int(record.get("degrees"), default_if_fail=0) if image_max_type == 'webp-jpg': #resized_jpg_content = resize_image(im, width=image_max_width, height=image_max_height, image_type='jpg') #resized_jpg_im = get_im(resized_jpg_content) resized_im_content = resize_image(im, width=image_max_width, height=image_max_height, image_type='webp', degrees=degrees) if not resized_im_content: update_record(bucket, record_id=record.get("_id"), _get_im_failed=True) return #del im, resized_im_content # resized_jpg_im, else: resized_im_content = resize_image(im, width=image_max_width, height=image_max_height, image_type=image_max_type, degrees=degrees) cache_image_record = sync_file_by_server_side( bucket=bucket, relative_path=cache_image_filepath, content=resized_im_content, is_dir=False, is_deleted=False, return_record=True) if cache_image_record: return storage.as_web_response(bucket=bucket, record=cache_image_record, mimetype=mimetype, try_resized_image=False)