├── .gitignore ├── LICENSE ├── MANIFEST.in ├── farbox_bucket ├── LICENSE.py ├── __init__.py ├── bucket │ ├── __init__.py │ ├── clouds │ │ ├── __init__.py │ │ └── storage │ │ │ ├── __init__.py │ │ │ └── qcloud.py │ ├── create.py │ ├── defaults.py │ ├── delete.py │ ├── domain │ │ ├── __init__.py │ │ ├── info.py │ │ ├── register.py │ │ ├── ssl_utils.py │ │ ├── utils.py │ │ └── web_utils.py │ ├── helper │ │ ├── __init__.py │ │ └── files_related.py │ ├── invite.py │ ├── node.py │ ├── private_configs.py │ ├── record │ │ ├── __init__.py │ │ ├── create.py │ │ ├── get │ │ │ ├── __init__.py │ │ │ ├── folder.py │ │ │ ├── get.py │ │ │ ├── helper.py │ │ │ ├── mix.py │ │ │ ├── path_related.py │ │ │ ├── refer_doc_related.py │ │ │ ├── slash_related.py │ │ │ ├── tag_related.py │ │ │ └── utils.py │ │ ├── helper │ │ │ ├── __init__.py │ │ │ └── update_record.py │ │ ├── related │ │ │ ├── __init__.py │ │ │ ├── for_all.py │ │ │ ├── for_created.py │ │ │ ├── for_deleted.py │ │ │ └── sub │ │ │ │ ├── __init__.py │ │ │ │ ├── for_posts.py │ │ │ │ └── utils.py │ │ ├── reset.py │ │ ├── update.py │ │ └── utils.py │ ├── service │ │ ├── __init__.py │ │ ├── bucket_service_info.py │ │ └── yearly_bucket_by_alipay.py │ ├── status.py │ ├── storage │ │ ├── __init__.py │ │ ├── base.py │ │ ├── default.py │ │ ├── helpers │ │ │ ├── __init__.py │ │ │ ├── auto_resized_image.py │ │ │ └── before_store_image.py │ │ ├── local_file_system.py │ │ └── qcloud_storage.py │ ├── sync │ │ ├── __init__.py │ │ ├── node.py │ │ ├── remote.py │ │ └── sync_api.py │ ├── template_related │ │ ├── __init__.py │ │ ├── bucket_template_web_api.py │ │ └── load_theme_from_template_folder.py │ ├── token │ │ ├── __init__.py │ │ ├── bucket_api_token.py │ │ ├── bucket_signature_and_check.py │ │ ├── simple_encrypt_token.py │ │ └── utils.py │ ├── usage │ │ ├── __init__.py │ │ └── bucket_usage_utils.py │ ├── utils.py │ └── web_api │ │ ├── __init__.py │ │ ├── handler.py │ │ └── verify.py ├── client │ ├── __init__.py │ ├── action.py │ ├── debug_site.py │ ├── dump_template.py │ ├── message.py │ ├── project.py │ ├── run.py │ ├── sync │ │ ├── __init__.py │ │ ├── compiler │ │ │ ├── __init__.py │ │ │ ├── basic_compiler.py │ │ │ ├── comments_compiler.py │ │ │ ├── file_compiler.py │ │ │ ├── folder_compiler.py │ │ │ ├── post_compiler.py │ │ │ ├── utils.py │ │ │ └── visits_compiler.py │ │ ├── compiler_worker.py │ │ ├── site.py │ │ └── sync.py │ └── sync_from │ │ ├── __init__.py │ │ └── sync_from.py ├── clouds │ ├── __init__.py │ ├── dropbox │ │ └── __init__.py │ ├── qcloud.py │ └── wechat │ │ ├── __init__.py │ │ ├── bind_wechat.py │ │ ├── build_wechat_menu.py │ │ ├── utils │ │ ├── __init__.py │ │ ├── __message_template.py │ │ ├── _message_template.py │ │ ├── check.py │ │ ├── menu.py │ │ ├── message.py │ │ └── token.py │ │ ├── views.py │ │ ├── wechat_handler.py │ │ └── wechat_text_image_sync_worker.py ├── console.py ├── deploy │ ├── __init__.py │ ├── build │ │ ├── __init__.py │ │ ├── build_client_image.py │ │ └── build_image.py │ ├── deploy.py │ └── run │ │ ├── __init__.py │ │ ├── configs │ │ ├── __init__.py │ │ ├── backend_jobs.py │ │ ├── backup │ │ │ └── nginx_websocket.conf │ │ ├── gunicorn.conf.py │ │ ├── gunicorn_websocket.conf.py │ │ ├── memcached.conf │ │ ├── nginx │ │ │ ├── nginx.conf │ │ │ ├── nginx_body.conf │ │ │ ├── server.crt │ │ │ └── server.key │ │ ├── openresty │ │ │ ├── access.lua │ │ │ ├── auto_ssl.lua │ │ │ └── lib │ │ │ │ └── resty │ │ │ │ ├── http.lua │ │ │ │ └── http_headers.lua │ │ ├── ssdb.conf │ │ └── supervisord.conf │ │ ├── files.py │ │ └── run.sh ├── for_dev │ ├── __init__.py │ ├── check_alipay.py │ ├── check_encrypt_performance.py │ ├── jinja_template_source.py │ └── markdown_post_compile_info.py ├── i18n │ ├── __init__.py │ └── zh_cn.py ├── server │ ├── __init__.py │ ├── avatar.py │ ├── avatar_view.py │ ├── backend │ │ ├── __init__.py │ │ ├── backend_jobs.py │ │ ├── service.py │ │ ├── status │ │ │ ├── __init__.py │ │ │ ├── bucket_web_template.py │ │ │ └── server_status.py │ │ └── sync │ │ │ ├── __init__.py │ │ │ ├── buckets.py │ │ │ └── utils.py │ ├── bucket_render │ │ ├── __init__.py │ │ ├── _keys_config_info.py │ │ ├── builtin_theme │ │ │ ├── __init__.py │ │ │ ├── _render.py │ │ │ ├── album.py │ │ │ └── wiki.py │ │ ├── render.py │ │ └── static_file.py │ ├── comments │ │ ├── __init__.py │ │ ├── add.py │ │ ├── contacts.py │ │ ├── dump.py │ │ ├── notification.py │ │ ├── template.py │ │ └── utils.py │ ├── dangerous │ │ ├── __init__.py │ │ ├── install_py_package.py │ │ ├── log_rotate.py │ │ ├── restart.py │ │ └── start_elasticsearch_server.py │ ├── es │ │ ├── __init__.py │ │ ├── es_client.py │ │ ├── es_search.py │ │ ├── es_status.py │ │ ├── es_sync_db_data.py │ │ └── es_utils.py │ ├── helpers │ │ ├── __init__.py │ │ ├── bucket.py │ │ ├── file_manager.py │ │ ├── file_manager_downloader.py │ │ ├── markdown_doc_append_worker.py │ │ ├── smart_scss.py │ │ └── upload_static_files_to_cdn.py │ ├── realtime │ │ ├── __init__.py │ │ ├── server.py │ │ └── utils.py │ ├── static │ │ ├── __init__.py │ │ ├── api │ │ │ ├── html │ │ │ │ ├── auto_sidebar │ │ │ │ │ ├── auto_sidebar.coffee │ │ │ │ │ ├── auto_sidebar.css │ │ │ │ │ ├── auto_sidebar.js │ │ │ │ │ └── auto_sidebar.scss │ │ │ │ ├── auto_toc │ │ │ │ │ ├── affix.js │ │ │ │ │ ├── auto_toc.coffee │ │ │ │ │ ├── auto_toc.css │ │ │ │ │ ├── auto_toc.js │ │ │ │ │ ├── auto_toc.scss │ │ │ │ │ ├── mixed.js │ │ │ │ │ └── scrollspy.js │ │ │ │ ├── form │ │ │ │ │ ├── ajax.coffee │ │ │ │ │ ├── ajax.js │ │ │ │ │ ├── ajax_submit.coffee │ │ │ │ │ ├── ajax_submit.js │ │ │ │ │ ├── fields │ │ │ │ │ │ ├── color.coffee │ │ │ │ │ │ ├── color.js │ │ │ │ │ │ ├── form.coffee │ │ │ │ │ │ ├── form.css │ │ │ │ │ │ ├── form.js │ │ │ │ │ │ ├── form.scss │ │ │ │ │ │ ├── icon.coffee │ │ │ │ │ │ ├── icon.js │ │ │ │ │ │ ├── image.coffee │ │ │ │ │ │ └── image.js │ │ │ │ │ ├── file_field.coffee │ │ │ │ │ ├── file_field.js │ │ │ │ │ ├── grid_form.css │ │ │ │ │ ├── grid_form.scss │ │ │ │ │ ├── simple_form.css │ │ │ │ │ └── simple_form.scss │ │ │ │ └── js_view │ │ │ │ │ ├── colorbox.css │ │ │ │ │ ├── images │ │ │ │ │ ├── border1.png │ │ │ │ │ ├── border2.png │ │ │ │ │ ├── loading.gif │ │ │ │ │ └── readme.txt │ │ │ │ │ ├── jquery.colorbox.js │ │ │ │ │ ├── jquery.modal.css │ │ │ │ │ ├── jquery.modal.js │ │ │ │ │ ├── jquery.qrcode.js │ │ │ │ │ ├── jquery.qrcode.min.js │ │ │ │ │ ├── js_view.coffee │ │ │ │ │ └── js_view.js │ │ │ └── syntax │ │ │ │ ├── modal.coffee │ │ │ │ ├── modal.css │ │ │ │ ├── modal.js │ │ │ │ ├── modal.scss │ │ │ │ ├── page │ │ │ │ ├── load_pages.coffee │ │ │ │ └── load_pages.js │ │ │ │ ├── slider │ │ │ │ ├── slider.coffee │ │ │ │ ├── slider.css │ │ │ │ ├── slider.js │ │ │ │ └── slider.scss │ │ │ │ ├── tab.css │ │ │ │ └── tab.scss │ │ ├── basic │ │ │ ├── basic.css │ │ │ ├── basic.scss │ │ │ ├── footer.css │ │ │ ├── footer.scss │ │ │ ├── post_preview.css │ │ │ ├── post_preview.scss │ │ │ ├── simple_list.css │ │ │ └── simple_list.scss │ │ ├── builtin_themes │ │ │ ├── knowbase │ │ │ │ ├── particles.js │ │ │ │ ├── run_particles.js │ │ │ │ ├── style.css │ │ │ │ └── style.scss │ │ │ └── waterfall │ │ │ │ ├── responsive_waterfall.js │ │ │ │ ├── script.coffee │ │ │ │ ├── script.js │ │ │ │ ├── style.css │ │ │ │ └── style.scss │ │ ├── comments │ │ │ ├── script.coffee │ │ │ ├── script.js │ │ │ ├── style.css │ │ │ └── style.scss │ │ ├── datatables │ │ │ ├── DataTables-1.10.20 │ │ │ │ ├── css │ │ │ │ │ ├── dataTables.bootstrap.css │ │ │ │ │ ├── dataTables.bootstrap.min.css │ │ │ │ │ ├── dataTables.bootstrap4.css │ │ │ │ │ ├── dataTables.bootstrap4.min.css │ │ │ │ │ ├── dataTables.foundation.css │ │ │ │ │ ├── dataTables.foundation.min.css │ │ │ │ │ ├── dataTables.jqueryui.css │ │ │ │ │ ├── dataTables.jqueryui.min.css │ │ │ │ │ ├── dataTables.semanticui.css │ │ │ │ │ ├── dataTables.semanticui.min.css │ │ │ │ │ ├── jquery.dataTables.css │ │ │ │ │ └── jquery.dataTables.min.css │ │ │ │ ├── images │ │ │ │ │ ├── sort_asc.png │ │ │ │ │ ├── sort_asc_disabled.png │ │ │ │ │ ├── sort_both.png │ │ │ │ │ ├── sort_desc.png │ │ │ │ │ └── sort_desc_disabled.png │ │ │ │ └── js │ │ │ │ │ ├── dataTables.bootstrap.js │ │ │ │ │ ├── dataTables.bootstrap.min.js │ │ │ │ │ ├── dataTables.bootstrap4.js │ │ │ │ │ ├── dataTables.bootstrap4.min.js │ │ │ │ │ ├── dataTables.foundation.js │ │ │ │ │ ├── dataTables.foundation.min.js │ │ │ │ │ ├── dataTables.jqueryui.js │ │ │ │ │ ├── dataTables.jqueryui.min.js │ │ │ │ │ ├── dataTables.semanticui.js │ │ │ │ │ ├── dataTables.semanticui.min.js │ │ │ │ │ ├── jquery.dataTables.js │ │ │ │ │ └── jquery.dataTables.min.js │ │ │ ├── datatables.css │ │ │ ├── datatables.js │ │ │ ├── datatables.min.css │ │ │ └── datatables.min.js │ │ ├── defaults │ │ │ ├── admin.png │ │ │ ├── avatar.png │ │ │ ├── drop_image_here.png │ │ │ ├── favicon.ico │ │ │ ├── site_avatar.png │ │ │ └── visitor.png │ │ ├── donate │ │ │ ├── alipay.jpg │ │ │ └── wechat.jpg │ │ ├── gfonts │ │ │ ├── Josefin+Sans_400_normal.svg │ │ │ ├── Josefin+Sans_400_normal.ttf │ │ │ ├── Josefin+Sans_400_normal.woff │ │ │ ├── Julius+Sans+One_400_normal.svg │ │ │ ├── Julius+Sans+One_400_normal.ttf │ │ │ ├── Julius+Sans+One_400_normal.woff │ │ │ ├── Lato_400_normal.svg │ │ │ ├── Lato_400_normal.ttf │ │ │ ├── Lato_400_normal.woff │ │ │ ├── calligraffitti-regular-webfont.eot │ │ │ ├── calligraffitti-regular-webfont.svg │ │ │ ├── calligraffitti-regular-webfont.ttf │ │ │ ├── calligraffitti-regular-webfont.woff │ │ │ └── calligraffitti-regular-webfont.woff2 │ │ ├── images │ │ │ ├── border1.png │ │ │ ├── border2.png │ │ │ ├── loading.gif │ │ │ └── readme.txt │ │ ├── lib │ │ │ ├── animate.3.5.2.min.css │ │ │ ├── essage │ │ │ │ ├── essage.css │ │ │ │ └── essage.js │ │ │ ├── fontawesome │ │ │ │ ├── css │ │ │ │ │ ├── font-awesome.css │ │ │ │ │ ├── font-awesome.css.map │ │ │ │ │ └── font-awesome.min.css │ │ │ │ ├── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ │ └── selector │ │ │ │ │ ├── icon-selector.css │ │ │ │ │ └── icon-selector.js │ │ │ ├── fonts │ │ │ │ ├── merriweather.css │ │ │ │ ├── merriweather │ │ │ │ │ ├── 1.woff2 │ │ │ │ │ ├── 2.woff2 │ │ │ │ │ ├── 3.woff2 │ │ │ │ │ └── 4.woff2 │ │ │ │ └── open_sans.css │ │ │ ├── hint.min.css │ │ │ ├── jquery-linedtextarea.css │ │ │ ├── jquery-linedtextarea.js │ │ │ ├── jquery-ui.min.js │ │ │ ├── jquery.contextMenu.css │ │ │ ├── jquery.contextMenu.js │ │ │ ├── jquery.dateFormat-1.0.js │ │ │ ├── jquery.form.js │ │ │ ├── jquery.js │ │ │ ├── jquery.json.min.js │ │ │ ├── jquery.modal.css │ │ │ ├── jquery.modal.js │ │ │ ├── jquery.qrcode.js │ │ │ ├── jquery.qrcode.min.js │ │ │ ├── jquery.tabslet.min.js │ │ │ ├── js.cookie.js │ │ │ ├── jstree │ │ │ │ ├── jstree.js │ │ │ │ ├── jstree.min.js │ │ │ │ └── themes │ │ │ │ │ ├── default-dark │ │ │ │ │ ├── 32px.png │ │ │ │ │ ├── 40px.png │ │ │ │ │ ├── style.css │ │ │ │ │ ├── style.min.css │ │ │ │ │ └── throbber.gif │ │ │ │ │ └── default │ │ │ │ │ ├── 32px.png │ │ │ │ │ ├── 40px.png │ │ │ │ │ ├── style.css │ │ │ │ │ ├── style.min.css │ │ │ │ │ └── throbber.gif │ │ │ ├── mark │ │ │ │ ├── jquery.mark.js │ │ │ │ ├── jquery.mark.min.js │ │ │ │ ├── mark.js │ │ │ │ └── mark.min.js │ │ │ ├── markdown │ │ │ │ ├── basic.css │ │ │ │ ├── basic.scss │ │ │ │ ├── markdown.css │ │ │ │ └── markdown.scss │ │ │ ├── markdown_js │ │ │ │ ├── echarts.min.js │ │ │ │ ├── flowchart.js │ │ │ │ ├── flowchart.js.map │ │ │ │ ├── mathjax │ │ │ │ │ └── tex-svg.js │ │ │ │ ├── mermaid │ │ │ │ │ ├── mermaid.css │ │ │ │ │ ├── mermaid.dark.css │ │ │ │ │ ├── mermaid.forest.css │ │ │ │ │ └── mermaid.min.js │ │ │ │ ├── mindmap │ │ │ │ │ ├── d3-flextree.js │ │ │ │ │ ├── d3-tip.js │ │ │ │ │ ├── d3.min.js │ │ │ │ │ ├── d3_LICENSE.txt │ │ │ │ │ ├── demo.html │ │ │ │ │ ├── markmap_LICENSE.txt │ │ │ │ │ ├── md.css │ │ │ │ │ ├── readme.txt │ │ │ │ │ ├── run.js │ │ │ │ │ ├── view.mindmap.css │ │ │ │ │ └── view.mindmap.js │ │ │ │ └── raphael-min.js │ │ │ ├── menu │ │ │ │ ├── jquery-ui.custom.min.js │ │ │ │ ├── jquery.mjs.nestedSortable.js │ │ │ │ └── smartmenu │ │ │ │ │ ├── LICENSE-MIT │ │ │ │ │ ├── css │ │ │ │ │ ├── sm-base.css │ │ │ │ │ ├── sm-base.scss │ │ │ │ │ ├── sm-blue │ │ │ │ │ │ └── sm-blue.css │ │ │ │ │ ├── sm-clean │ │ │ │ │ │ └── sm-clean.css │ │ │ │ │ ├── sm-core-css.css │ │ │ │ │ ├── sm-mint │ │ │ │ │ │ └── sm-mint.css │ │ │ │ │ └── sm-simple │ │ │ │ │ │ └── sm-simple.css │ │ │ │ │ ├── jquery.smartmenus.js │ │ │ │ │ └── jquery.smartmenus.min.js │ │ │ ├── minicolors │ │ │ │ ├── jquery.minicolors.css │ │ │ │ ├── jquery.minicolors.js │ │ │ │ └── jquery.minicolors.min.js │ │ │ ├── pure.css │ │ │ ├── pure_patch.css │ │ │ ├── pure_patch.scss │ │ │ ├── textcomplete │ │ │ │ ├── jquery.textcomplete.js │ │ │ │ └── jquery.textcomplete.min.js │ │ │ ├── three │ │ │ │ ├── 3d-force-graph.min.js │ │ │ │ ├── three-spritetext.min.js │ │ │ │ └── three.js │ │ │ ├── unslider │ │ │ │ ├── css │ │ │ │ │ ├── unslider-dots.css │ │ │ │ │ └── unslider.css │ │ │ │ └── js │ │ │ │ │ ├── unslider-min.js │ │ │ │ │ └── unslider.js │ │ │ ├── video-js │ │ │ │ ├── font │ │ │ │ │ ├── VideoJS.eot │ │ │ │ │ ├── VideoJS.svg │ │ │ │ │ ├── VideoJS.ttf │ │ │ │ │ └── VideoJS.woff │ │ │ │ ├── video-js.css │ │ │ │ ├── video-js.min.css │ │ │ │ ├── video-js.swf │ │ │ │ ├── video.js │ │ │ │ ├── video.js.map │ │ │ │ ├── video.min.js │ │ │ │ └── video.min.js.map │ │ │ ├── webuploader.html5only.min.js │ │ │ └── wtf-forms.css │ │ ├── nav │ │ │ ├── jquery.smartmenus.js │ │ │ ├── jquery.smartmenus.min.js │ │ │ ├── menu │ │ │ │ ├── jquery-ui.custom.min.js │ │ │ │ ├── jquery.mjs.nestedSortable.js │ │ │ │ └── smartmenu │ │ │ │ │ ├── LICENSE-MIT │ │ │ │ │ ├── css │ │ │ │ │ ├── sm-base.css │ │ │ │ │ ├── sm-base.scss │ │ │ │ │ ├── sm-blue │ │ │ │ │ │ └── sm-blue.css │ │ │ │ │ ├── sm-clean │ │ │ │ │ │ └── sm-clean.css │ │ │ │ │ ├── sm-core-css.css │ │ │ │ │ ├── sm-mint │ │ │ │ │ │ └── sm-mint.css │ │ │ │ │ └── sm-simple │ │ │ │ │ │ └── sm-simple.css │ │ │ │ │ ├── jquery.smartmenus.js │ │ │ │ │ └── jquery.smartmenus.min.js │ │ │ ├── nav.css │ │ │ ├── nav.scss │ │ │ ├── run_menu.coffee │ │ │ ├── run_menu.js │ │ │ ├── sm-base.css │ │ │ ├── sm-base.scss │ │ │ └── sm-core-css.css │ │ ├── pages │ │ │ ├── bucket │ │ │ │ ├── modify_invite_bucket.css │ │ │ │ └── modify_invite_bucket.scss │ │ │ ├── code_editor │ │ │ │ ├── codemirror │ │ │ │ │ ├── codemirror.css │ │ │ │ │ ├── codemirror.js │ │ │ │ │ ├── mode │ │ │ │ │ │ ├── coffeescript.js │ │ │ │ │ │ ├── css.js │ │ │ │ │ │ ├── htmlmixed.js │ │ │ │ │ │ ├── jade.js │ │ │ │ │ │ ├── javascript.js │ │ │ │ │ │ ├── markdown.js │ │ │ │ │ │ └── xml.js │ │ │ │ │ └── solarized.css │ │ │ │ ├── editor.coffee │ │ │ │ ├── editor.css │ │ │ │ ├── editor.js │ │ │ │ └── editor.scss │ │ │ ├── csv_editor │ │ │ │ ├── csv.css │ │ │ │ └── csv.js │ │ │ ├── default_homepage │ │ │ │ ├── cover.css │ │ │ │ ├── cover.scss │ │ │ │ ├── markdown@2x.jpg │ │ │ │ ├── style.css │ │ │ │ └── style.scss │ │ │ ├── file_manager │ │ │ │ ├── tree.coffee │ │ │ │ ├── tree.css │ │ │ │ ├── tree.js │ │ │ │ └── tree.scss │ │ │ ├── markdown_page.css │ │ │ ├── page.css │ │ │ ├── site │ │ │ │ └── menu │ │ │ │ │ ├── editor.coffee │ │ │ │ │ ├── editor.js │ │ │ │ │ ├── menu_editor.css │ │ │ │ │ ├── menu_editor.scss │ │ │ │ │ ├── run.coffee │ │ │ │ │ └── run.js │ │ │ ├── table.css │ │ │ ├── table.scss │ │ │ ├── text_editor │ │ │ │ ├── text_editor.coffee │ │ │ │ ├── text_editor.css │ │ │ │ ├── text_editor.js │ │ │ │ └── text_editor.scss │ │ │ └── web_editor │ │ │ │ ├── jquery.dateFormat-1.0.js │ │ │ │ ├── jquery.min.js │ │ │ │ ├── knockout-min.js │ │ │ │ ├── web_editor.coffee │ │ │ │ ├── web_editor.css │ │ │ │ ├── web_editor.js │ │ │ │ └── web_editor.scss │ │ ├── realtime │ │ │ ├── debug_template.js │ │ │ └── realtime.js │ │ ├── static_render.py │ │ └── unsplash │ │ │ ├── 2.jpg │ │ │ ├── 5.jpg │ │ │ └── 6.jpg │ ├── statistics │ │ ├── __init__.py │ │ ├── after_request.py │ │ ├── post_visits.py │ │ └── usage_collect.py │ ├── template_system │ │ ├── __init__.py │ │ ├── api_template_render.py │ │ ├── app_functions │ │ │ ├── __init__.py │ │ │ ├── after_request │ │ │ │ ├── __init__.py │ │ │ │ ├── basic.py │ │ │ │ └── cache_page.py │ │ │ ├── before_and_after_request │ │ │ │ ├── __init__.py │ │ │ │ └── time_cost.py │ │ │ ├── before_request │ │ │ │ ├── __init__.py │ │ │ │ ├── basic.py │ │ │ │ └── site_visitor_password.py │ │ │ └── utils.py │ │ ├── attr_patch │ │ │ ├── __init__.py │ │ │ ├── all.py │ │ │ ├── array.py │ │ │ ├── dictionary.py │ │ │ ├── fake_fields.py │ │ │ └── strings.py │ │ ├── env.py │ │ ├── errors.py │ │ ├── exceptions.py │ │ ├── helper │ │ │ ├── __init__.py │ │ │ ├── get_post_with_greed.py │ │ │ ├── post_compile_url_for_wiki_links.py │ │ │ └── post_referred_docs.py │ │ ├── model │ │ │ ├── __init__.py │ │ │ ├── category.py │ │ │ ├── date.py │ │ │ └── text.py │ │ ├── namespace │ │ │ ├── __init__.py │ │ │ ├── bucket.py │ │ │ ├── built_in.py │ │ │ ├── data.py │ │ │ ├── html.py │ │ │ ├── post.py │ │ │ ├── record.py │ │ │ ├── request.py │ │ │ ├── response.py │ │ │ ├── site.py │ │ │ └── utils │ │ │ │ ├── __init__.py │ │ │ │ ├── form_utils.py │ │ │ │ ├── nav_utils.py │ │ │ │ └── post_utils.py │ │ ├── syntax_block │ │ │ ├── __init__.py │ │ │ ├── compatibility.py │ │ │ ├── lazy_html.py │ │ │ ├── modal.py │ │ │ ├── page.py │ │ │ ├── pure.py │ │ │ ├── refer.py │ │ │ └── slider.py │ │ ├── template_system_patch.py │ │ └── templates │ │ │ ├── __init__.py │ │ │ ├── build.py │ │ │ └── info.py │ ├── utils │ │ ├── __init__.py │ │ ├── cache_for_function.py │ │ ├── cookie.py │ │ ├── doc_url.py │ │ ├── func.py │ │ ├── lazy.py │ │ ├── record_and_paginator │ │ │ ├── __init__.py │ │ │ ├── base_paginator.py │ │ │ ├── paginator.py │ │ │ ├── paginator_for_record.py │ │ │ └── utils.py │ │ ├── request.py │ │ ├── request_context_vars.py │ │ ├── request_path.py │ │ ├── response.py │ │ ├── response_html.py │ │ ├── site_resource.py │ │ └── verification_code.py │ ├── views │ │ ├── __init__.py │ │ ├── api.py │ │ ├── bucket.py │ │ ├── file_manager.py │ │ ├── for_admin.py │ │ ├── install_ssl.py │ │ ├── my.py │ │ ├── system.py │ │ └── wiki_link_fallback.py │ └── web_app.py ├── settings.py ├── themes │ ├── Cais │ │ ├── archive.jade │ │ ├── base.jade │ │ ├── css │ │ │ ├── markdown.scss │ │ │ └── style.scss │ │ ├── feed.jade │ │ ├── index+tag.jade │ │ ├── post.jade │ │ └── readme.txt │ ├── Classify │ │ ├── archive.jade │ │ ├── base.jade │ │ ├── category.jade │ │ ├── css │ │ │ └── style.scss │ │ ├── feed.jade │ │ ├── index.jade │ │ └── post.jade │ ├── Esta │ │ ├── archive.jade │ │ ├── base.jade │ │ ├── index+tag.jade │ │ ├── mixins.jade │ │ ├── post.jade │ │ └── style.scss │ ├── Fexo │ │ ├── base.jade │ │ ├── category+categories.jade │ │ ├── index.jade │ │ ├── license.txt │ │ ├── mixins.jade │ │ ├── post.jade │ │ └── static │ │ │ └── style.scss │ ├── Puti │ │ ├── License.txt │ │ ├── archive.jade │ │ ├── base.jade │ │ ├── css │ │ │ ├── animate.3.5.2.min.css │ │ │ ├── markdown.scss │ │ │ └── style.scss │ │ ├── feed.jade │ │ ├── index+tag.jade │ │ ├── post.jade │ │ └── readme.txt │ ├── Sollrei │ │ ├── archive.jade │ │ ├── base.jade │ │ ├── css │ │ │ ├── fonts.css │ │ │ ├── reset.scss │ │ │ └── style.scss │ │ ├── index+category+tag.jade │ │ ├── js │ │ │ ├── index.js │ │ │ └── particles.js │ │ └── post.jade │ ├── Wiki │ │ ├── index+post.jade │ │ └── static │ │ │ ├── instantclick.js │ │ │ └── style.scss │ ├── __init__.py │ └── build_themes.py └── utils │ ├── __init__.py │ ├── async_block.py │ ├── cache.py │ ├── cli_color.py │ ├── client_sync │ ├── __init__.py │ ├── detect.py │ ├── log.py │ └── sync_utils.py │ ├── console_utils.py │ ├── convert │ ├── __init__.py │ ├── coffee2js.py │ ├── css.py │ ├── jade2jinja.py │ └── utils.py │ ├── data.py │ ├── date.py │ ├── encrypt │ ├── __init__.py │ ├── aes_encrypt.py │ ├── des_encrypt.py │ ├── key_encrypt.py │ └── simple.py │ ├── env.py │ ├── error.py │ ├── file_version.py │ ├── functional.py │ ├── gevent_run.py │ ├── gevent_utils.py │ ├── gzip_content.py │ ├── html.py │ ├── image │ ├── __init__.py │ ├── exif.py │ ├── resize.py │ └── utils.py │ ├── ip │ ├── __init__.py │ └── utils.py │ ├── ipfs_utils.py │ ├── logger.py │ ├── mail │ ├── __init__.py │ ├── basic_utils.py │ ├── system.py │ └── utils.py │ ├── md_related │ ├── __init__.py │ ├── filter_posts_info.py │ └── markdown_doc_links.py │ ├── memcache.py │ ├── memcache_block.py │ ├── mime.py │ ├── monkey │ ├── __init__.py │ └── http.py │ ├── objectid.py │ ├── path.py │ ├── pay │ ├── __init__.py │ ├── alipay.py │ └── alipay_api.py │ ├── simple_encrypt.py │ ├── ssdb_client.py │ ├── ssdb_utils.py │ ├── ssl_related.py │ ├── system_status_recorder.py │ ├── url.py │ └── web_utils │ ├── __init__.py │ ├── flask_httpauth.py │ ├── request.py │ └── response.py ├── readme.md └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pyc 3 | *.pyd 4 | *.DS_Store 5 | test[!s]* 6 | tmp* 7 | web/static/unsplash/origin 8 | draft/* 9 | configs/debug.txt 10 | project.dockerfile 11 | project.packages 12 | dist/* 13 | *.egg-info/* 14 | farbox_bucket/bitcron/* 15 | PKG-INFO 16 | *.cfg 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 hepochen@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | Any user logging into a system that embeds the code from the current project, 13 | must prominently inform the user that the supported client Apps include 14 | Metion (on the Apple App Store) and MarkEditor (https://markeditor.com). 15 | [任何用户登录到包含了当前项目代码的系统中,必须要在显著的位置告知用户对应支持的客户端中包括 16 | Metion (on Apple App Store) 以及 MarkEditor (https://markeditor.com)。] 17 | 18 | If the site is driven by current project, it would be appreciated if the 19 | `Powered by FarBox` signature could be retained at the bottom of the site. 20 | [如果网站由当前项目驱动,希望能在网站底部保留 `Powered by FarBox` 的签名,非常感谢。] 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include farbox_bucket/server/static * 2 | recursive-include farbox_bucket/themes * 3 | recursive-include farbox_bucket/deploy/run * -------------------------------------------------------------------------------- /farbox_bucket/LICENSE.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | 4 | 5 | LICENSE = """ 6 | MIT License 7 | 8 | Copyright (c) 2021 hepochen@gmail.com 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | Any user logging into a system that embeds the code from the current project, 18 | must prominently inform the user that the supported client Apps include 19 | Metion (on the Apple App Store) and MarkEditor (https://markeditor.com). 20 | [任何用户登录到包含了当前项目代码的系统中,必须要在显著的位置告知用户对应支持的客户端中包括 21 | Metion (on Apple App Store) 以及 MarkEditor (https://markeditor.com)。] 22 | 23 | If the site is driven by current project, it would be appreciated if the 24 | `Powered by FarBox` signature could be retained at the bottom of the site. 25 | [如果网站由当前项目驱动,希望能在网站底部保留 `Powered by FarBox` 的签名,非常感谢。] 26 | 27 | The above copyright notice and this permission notice shall be included in all 28 | copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36 | SOFTWARE. 37 | """ -------------------------------------------------------------------------------- /farbox_bucket/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | 4 | version = '0.2020' -------------------------------------------------------------------------------- /farbox_bucket/bucket/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | from .utils import * 4 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/clouds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/clouds/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/clouds/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/clouds/storage/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/defaults.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | 3 | # 基础结构 4 | zero_id = '0'*24 # 创世ID, 一旦创建,不可修改 5 | zero_id_for_user = '0'*23 + '1' # 用户的设定,会进行加密 6 | zero_id_for_site = '0'*23 + '3' # site configs 7 | zero_id_for_secret = '0'*23 + '4' # secret site configs,也会进行加密 8 | 9 | # 基础数据 10 | zero_id_for_pages = '0'*23 + '2' 11 | zero_id_for_files = '0'*23 + '6' 12 | zero_id_for_posts = '0'*23 + '7' 13 | 14 | # 变动支持 15 | zero_id_for_histories = '0'*22 + '11' 16 | zero_id_for_statistics = '0'*22 + '12' 17 | zero_id_for_orders = '0'*22 + '13' # 配合 zero_id_for_files 18 | zero_id_for_inbox = '0'*22 + '14' 19 | 20 | # todo 变动区域分为两种类型,一个是整个 bucket 内保持同步的(如何处理冲突呢?),一个是只在当前 node 生效的 21 | # todo 倾向于只有一种,就是只在当前 node 生效的 22 | 23 | 24 | # 主要是外部调用 get_records 查询的时候,给一个基准 id,可以避免 zero_ids 被查询到 25 | # 主要是模板引擎中调用 26 | zero_id_for_finder = '0'*22 + '99' 27 | 28 | zero_ids = [zero_id, zero_id_for_user, zero_id_for_site, zero_id_for_secret, 29 | zero_id_for_pages, zero_id_for_files, zero_id_for_posts, 30 | zero_id_for_histories, zero_id_for_statistics, 31 | zero_id_for_orders, zero_id_for_inbox] 32 | 33 | 34 | bucket_config_doc_id_names = { 35 | 'init': zero_id, 36 | 'user': zero_id_for_user, 37 | 'site': zero_id_for_site, 38 | 'secret': zero_id_for_secret, 39 | 'pages': zero_id_for_pages, 40 | 'files': zero_id_for_files, 41 | 'posts': zero_id_for_posts, 42 | 'histories': zero_id_for_histories, 43 | 'statistics': zero_id_for_statistics, 44 | 'orders': zero_id_for_orders, 45 | 'inbox': zero_id_for_inbox, 46 | } 47 | 48 | config_names_not_allowed_set_by_user = ['init', 'histories', 'statistics', 'inbox'] 49 | 50 | 51 | 52 | BUCKET_RECORD_SORT_TYPES = ('post', 'image', 'file', 'folder', 'comments') 53 | 54 | BUCKET_RECORD_SLASH_TYPES = ('post', 'image', 'file', 'folder', 'comments', "visits") -------------------------------------------------------------------------------- /farbox_bucket/bucket/delete.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.ssdb_utils import hdel, hclear 3 | from farbox_bucket.bucket.utils import remove_bucket_from_buckets, clear_related_buckets 4 | from farbox_bucket.bucket.record.get.helper import loop_records_for_bucket 5 | from farbox_bucket.bucket.storage.default import storage 6 | 7 | 8 | 9 | def delete_bucket(bucket, delete_files=True): 10 | if not bucket: 11 | return 12 | # 清空一个 bucket 的相关逻辑,但是要慎重使用,一般除了 本地Debug 之外,不进行这个操作 13 | clear_related_buckets(bucket) 14 | 15 | # after_record_created 中的一些数据统计清理 16 | hdel('_bucket_usage', bucket) 17 | hdel('_records_count', bucket) 18 | 19 | if delete_files: 20 | # 删除对应的文件 21 | loop_records_for_bucket(bucket, storage.when_record_deleted) 22 | 23 | # at last 24 | hclear(bucket) 25 | remove_bucket_from_buckets(bucket) -------------------------------------------------------------------------------- /farbox_bucket/bucket/domain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/domain/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/helper/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.encrypt.key_encrypt import create_private_public_keys 3 | 4 | 5 | def get_private_key_on_server_side(): 6 | private_key, public_key = create_private_public_keys() 7 | return private_key 8 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/record/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/get/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/record/get/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/get/helper.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from .get import get_records_for_bucket 3 | 4 | 5 | 6 | def loop_records_for_bucket(bucket, func_for_record, limit=100, raw=False): 7 | start_record_id = None 8 | while True: 9 | records = get_records_for_bucket( 10 | bucket = bucket, 11 | start_record_id = start_record_id, 12 | limit = limit, 13 | includes_start_record_id = False, 14 | raw = raw, 15 | ) 16 | for record in records: 17 | func_for_record(record) 18 | if not records or len(records) != limit: 19 | break 20 | last_record = records[-1] 21 | if isinstance(last_record, dict): 22 | start_record_id = last_record['_id'] 23 | else: 24 | start_record_id = last_record[0] -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/get/slash_related.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | from farbox_bucket.utils.ssdb_utils import zscan 4 | 5 | from farbox_bucket.bucket.utils import get_bucket_name_for_slash 6 | from farbox_bucket.utils import smart_unicode 7 | 8 | from .path_related import get_records_by_paths, filter_paths_under_path 9 | 10 | 11 | def get_paths_by_slash_number(bucket, level_start, level_end='', under=''): 12 | bucket_for_slash = get_bucket_name_for_slash(bucket) 13 | paths = [] 14 | raw_result = zscan(bucket_for_slash, score_start=level_start, score_end=level_end, limit=30000) 15 | for path, level_value in raw_result: 16 | paths.append(smart_unicode(path)) 17 | paths = filter_paths_under_path(paths, under=under) 18 | return paths 19 | 20 | 21 | 22 | 23 | def get_records_by_slash_number(bucket, level_start, level_end='', under=''): 24 | paths = get_paths_by_slash_number(bucket=bucket, level_start=level_start, level_end=level_end, under=under) 25 | records = get_records_by_paths(bucket=bucket, paths=paths) 26 | return records 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/helper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/record/helper/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/helper/update_record.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.bucket.utils import has_bucket 3 | from farbox_bucket.bucket.record.create import create_record, update_path_related_when_record_changed 4 | 5 | 6 | 7 | 8 | 9 | def update_record_directly(bucket, record): 10 | # 通过 record 来更新 record,不需要额外的编译 11 | # 如果有 path 路径,不要去更改它 12 | if not isinstance(record, dict): 13 | return False 14 | if not has_bucket(bucket): 15 | return False 16 | record['_auto_clean_bucket'] = True 17 | 18 | # 创建新的 record 19 | create_record(bucket, record) 20 | 21 | object_id = record.get("_id") 22 | if object_id: 23 | update_path_related_when_record_changed(bucket, object_id, record) 24 | 25 | return True 26 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/related/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/record/related/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/related/for_all.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | from farbox_bucket.utils.ssdb_utils import hincr, py_data_to_ssdb_data 4 | from farbox_bucket.bucket.utils import update_bucket_max_id 5 | from farbox_bucket.bucket.record.utils import get_path_from_record, get_data_type 6 | 7 | from .for_created import after_path_related_record_created 8 | from .for_deleted import after_path_related_record_deleted 9 | 10 | 11 | 12 | def update_path_related_when_record_changed(bucket, record_id, record_data): 13 | # 注意:!!!! 凡是 record 中有指定 path 段的,都是可以被更新的;如果不希望这个 record 被 删除,就不要赋予这个字段 14 | if not isinstance(record_data, dict): 15 | return 16 | if not bucket or not record_id: 17 | return 18 | is_deleted = record_data.get('is_deleted', False) 19 | 20 | 21 | # path 相关的逻辑 22 | path = get_path_from_record(record_data) 23 | if not path: 24 | return 25 | if is_deleted: 26 | # delete the record, 实际上都是增量,只是一个 action=delete 的标识而已; 但也有直接把 record 删除掉的 27 | after_path_related_record_deleted(bucket, record_data=record_data) 28 | else: # create or update 29 | after_path_related_record_created(bucket, record_id, record_data) 30 | 31 | 32 | 33 | def after_record_created(bucket, py_record_data, object_id, should_update_bucket_max_id=False): 34 | if should_update_bucket_max_id and object_id: 35 | # 由 API 构建的, 会产生一个新的 object_id 36 | update_bucket_max_id(bucket, object_id) 37 | 38 | byte_record_data = py_data_to_ssdb_data(py_record_data) # must be string_types 39 | 40 | hincr('_bucket_usage', bucket, len(byte_record_data)) # 容量增加 41 | hincr('_records_count', 'all', num=1) # 如果系统中 bucket 被删除,这个增量是不会减少的 42 | hincr('_records_count', bucket, num=1) 43 | 44 | update_path_related_when_record_changed(bucket, object_id, py_record_data) -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/related/sub/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/record/related/sub/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/related/sub/utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.bucket.record.utils import get_data_type 3 | from .for_posts import update_post_tags_words_info 4 | 5 | 6 | def update_tags_info_for_posts(bucket, record_data): 7 | # store the files info 8 | 9 | doc_type = get_data_type(record_data) 10 | if doc_type == 'post': 11 | update_post_tags_words_info(bucket=bucket, record_data=record_data) -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/reset.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from .get.helper import loop_records_for_bucket 3 | from farbox_bucket.bucket.defaults import zero_ids 4 | from farbox_bucket.utils.ssdb_utils import hdel, hdel_many 5 | from farbox_bucket.bucket.utils import get_bucket_files_info 6 | from farbox_bucket.bucket.record.get.path_related import get_record_id_by_path 7 | from farbox_bucket.bucket.record.related.for_all import update_path_related_when_record_changed 8 | 9 | 10 | 11 | def reset_related_records(bucket): 12 | def reset_record(record): 13 | record_id = record.get('_id') 14 | update_path_related_when_record_changed(bucket=bucket, record_id=record_id, record_data=record) 15 | loop_records_for_bucket(bucket, func_for_record=reset_record) 16 | 17 | 18 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/record/update.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.data import json_dumps, json_loads 3 | from .get.get import get_record 4 | from .get.path_related import get_record_id_by_path 5 | from farbox_bucket.utils.ssdb_utils import hset, ssdb_data_to_py_data, py_data_to_ssdb_data 6 | from farbox_bucket.bucket.record.related.for_created import update_record_order_value_to_related_db 7 | 8 | # 如非特殊,不要对一个 record 进行 update 9 | 10 | fields_not_allowed_to_update = ['_id', '_pre_id', 'id', 'path'] 11 | 12 | def update_record(bucket, record_id, **kwargs): 13 | if not record_id: 14 | return False 15 | if isinstance(record_id, dict): 16 | record_id = record_id.get("_id") 17 | record = get_record(bucket, record_id, force_dict=True) 18 | if not record: 19 | return False 20 | for field in fields_not_allowed_to_update: # 不允许更改的字段 21 | kwargs.pop(field, None) 22 | if not kwargs: 23 | return False 24 | record.update(kwargs) # update 25 | hset(bucket, record_id, record) 26 | 27 | order_value_in_kwargs = kwargs.get("_order") 28 | if isinstance(order_value_in_kwargs, (float, int)) and record.get("path"): 29 | # update path related _order value, specially for image 30 | update_record_order_value_to_related_db(bucket, record) 31 | 32 | return True 33 | 34 | 35 | 36 | def update_record_by_path(bucket, path, **kwargs): 37 | record_id = get_record_id_by_path(bucket, path) 38 | if record_id: 39 | return update_record(bucket, record_id, **kwargs) 40 | else: 41 | return False 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/service/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/service/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/storage/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/storage/default.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.bucket.clouds.storage.qcloud import qcloud_is_valid 3 | from .local_file_system import LocalStorage 4 | from .qcloud_storage import QCloudStorage 5 | 6 | 7 | def get_default_storage(): 8 | if qcloud_is_valid: 9 | return QCloudStorage() 10 | else: 11 | return LocalStorage() 12 | 13 | 14 | storage = get_default_storage() -------------------------------------------------------------------------------- /farbox_bucket/bucket/storage/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/storage/helpers/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/storage/helpers/before_store_image.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.image.utils import get_im, get_im_size 3 | from farbox_bucket.utils.image.exif import Exif 4 | from farbox_bucket.utils.date import get_image_date 5 | from farbox_bucket.bucket.utils import get_bucket_utc_offset 6 | from farbox_bucket.utils.date import date_to_timestamp 7 | 8 | # 在图片存储之前,解析图片的信息,包括旋转相关的逻辑 9 | 10 | 11 | def do_get_image_info_from_raw_content(raw_content): 12 | info = {} 13 | im = get_im(raw_content) 14 | if not im: 15 | return info 16 | exif_obj = Exif(im) 17 | exif = exif_obj.data # 获得exif信息,并且会自动旋转图片 18 | info['exif'] = exif 19 | info['degrees'] = exif_obj.degrees # 旋转角度多少才能正回来 20 | # 从exif中取日期 21 | utc_offset = get_bucket_utc_offset() 22 | image_date = get_image_date(exif, None, utc_offset) 23 | if image_date: 24 | info["date"] = image_date 25 | sort_value = date_to_timestamp(image_date, is_utc=True) 26 | info["_order"] = sort_value # 要处理图片的排序 27 | im_size = get_im_size(im) 28 | image_width, image_height = im_size 29 | if image_width and image_height: 30 | info['image_width'] = image_width 31 | info['image_height'] = image_height 32 | return info 33 | 34 | 35 | def get_image_info_from_raw_content(raw_content): 36 | try: 37 | return do_get_image_info_from_raw_content(raw_content) 38 | except: 39 | return {} 40 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/sync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/sync/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/template_related/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/template_related/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/token/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/token/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/token/bucket_signature_and_check.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import time 3 | from farbox_bucket.settings import server_secret_key 4 | from farbox_bucket.utils import get_md5, string_types, to_int 5 | 6 | 7 | 8 | def get_signature_for_bucket(bucket, timestamp=None, salt=None): 9 | # - 10 | timestamp = timestamp or int(time.time()) 11 | value_to_hash = "%s-%s-%s" % (timestamp, bucket, server_secret_key) 12 | if salt: 13 | value_to_hash = "%s-%s" % (value_to_hash, salt) 14 | signature_body = get_md5(value_to_hash) 15 | signature = "%s-%s" % (timestamp, signature_body) 16 | return signature 17 | 18 | 19 | 20 | def check_signature_for_bucket(bucket, signature, salt=None, hours=24): 21 | if not isinstance(signature, string_types): 22 | return False 23 | if signature.count("-") != 1: 24 | return False 25 | signature_timestamp, signature_body = signature.split("-", 1) 26 | signature_timestamp = to_int(signature_timestamp, default_if_fail=0) 27 | if not signature_timestamp: 28 | return False 29 | timestamp = int(time.time()) 30 | diff = timestamp - signature_timestamp 31 | if diff > hours*60*60: # 1 day by default 32 | return False 33 | signature_should_be = get_signature_for_bucket(bucket, signature_timestamp, salt=salt) 34 | if signature == signature_should_be: 35 | return True 36 | return False -------------------------------------------------------------------------------- /farbox_bucket/bucket/token/simple_encrypt_token.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils import string_types 3 | from farbox_bucket.utils.ssdb_utils import hset, hget, hexists 4 | from farbox_bucket.utils.encrypt.simple import simple_encrypt, simple_decrypt 5 | from farbox_bucket.utils.data import json_dumps, json_loads 6 | 7 | 8 | # simple_bucket_token 是唯一不可更改的,主要是做加密和解密的;不能暴露给任何人 9 | 10 | def get_simple_bucket_token(bucket): 11 | token = hget('_simple_bucket_token', bucket) or '' 12 | return token 13 | 14 | 15 | def set_simple_bucket_token(bucket, private_key_md5): 16 | if not bucket: 17 | return 18 | if isinstance(private_key_md5, string_types) and len(private_key_md5)<100: 19 | old_value = get_simple_bucket_token(bucket) 20 | if old_value == private_key_md5: 21 | return 22 | hset('_simple_bucket_token', bucket, private_key_md5) 23 | 24 | 25 | 26 | def get_normal_data_by_simple_token(bucket, encrypted_data, force_py_data=True, default_if_failed=None): 27 | # force_py_data = True 的时候,必须是 list/tuple/dict 的数据类型 28 | if not encrypted_data: 29 | return encrypted_data 30 | token = get_simple_bucket_token(bucket) 31 | if token and isinstance(encrypted_data, string_types): 32 | result = simple_decrypt(encrypted_data, token) 33 | try: result = json_loads(result) 34 | except: pass 35 | else: 36 | result = encrypted_data 37 | if force_py_data: 38 | if isinstance(result, (list, tuple, dict)): 39 | return result 40 | else: 41 | return default_if_failed 42 | else: 43 | return result 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /farbox_bucket/bucket/usage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/usage/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/bucket/web_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/bucket/web_api/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/client/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/client/sync/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from .site import sync_site_folder_simply as sync_to_farbox 3 | from farbox_bucket.client.sync_from.sync_from import sync_from_farbox -------------------------------------------------------------------------------- /farbox_bucket/client/sync/compiler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/client/sync/compiler/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/client/sync/compiler/comments_compiler.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import time, os 3 | from farbox_bucket.client.sync.compiler.basic_compiler import BasicSyncCompiler 4 | from farbox_bucket.utils.data import csv_to_list, csv_data_to_objects 5 | 6 | 7 | 8 | class CommentsSyncCompiler(BasicSyncCompiler): 9 | def __init__(self, *args, **kwargs): 10 | kwargs['doc_type'] = kwargs.get('doc_type') or 'comments' 11 | BasicSyncCompiler.__init__(self, *args, **kwargs) 12 | 13 | def get_compiled_data(self): 14 | data_list = csv_to_list(self.raw_content, 15 | max_rows=1000, # 1k条记录最多 16 | max_columns=50, 17 | auto_fill=True 18 | ) 19 | objects = csv_data_to_objects(data_list) 20 | _order_value = time.time() 21 | if self.abs_filepath and os.path.isfile(self.abs_filepath): 22 | _order_value = os.path.getmtime(self.abs_filepath) 23 | data = dict( 24 | _order = _order_value, 25 | objects = objects, 26 | raw_content = self.raw_content, 27 | data = data_list, 28 | ) 29 | return data 30 | -------------------------------------------------------------------------------- /farbox_bucket/client/sync/compiler/folder_compiler.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from farbox_bucket.client.sync.compiler.post_compiler import PostSyncCompiler 3 | import os 4 | 5 | 6 | # folder 从某种角度来说,只是记录这个数据,而不是更多的关系上的,因为无法从 path 上进行查询匹配 7 | # folder_doc 8 | 9 | class FolderSyncCompiler(PostSyncCompiler): 10 | def __init__(self, *args, **kwargs): 11 | kwargs['doc_type'] = kwargs.get('doc_type') or 'folder' 12 | PostSyncCompiler.__init__(self, *args, **kwargs) 13 | 14 | def get_compiled_data(self): 15 | data = PostSyncCompiler.get_compiled_data(self) 16 | if self.abs_filepath and os.path.isdir(self.abs_filepath): 17 | folder_relative_path = self.relative_path 18 | else: 19 | folder_relative_path = self.relative_path.strip('/') 20 | data['path'] = folder_relative_path 21 | data['relative_path'] = folder_relative_path 22 | data['is_dir'] = True 23 | data['slash_number'] = folder_relative_path.count('/') 24 | data['_type'] = 'folder' 25 | data['type'] = 'folder' 26 | return data 27 | -------------------------------------------------------------------------------- /farbox_bucket/client/sync/compiler/visits_compiler.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.client.sync.compiler.basic_compiler import BasicSyncCompiler 3 | from farbox_bucket.utils.data import csv_to_list, csv_data_to_objects 4 | 5 | # 解析 _data/visits.csv 文件,并转为相应的 data 数据 6 | # server 端对于这个 data,会截取前 1000,作为 visits 的逻辑 7 | 8 | class VisitsSyncCompiler(BasicSyncCompiler): 9 | def __init__(self, *args, **kwargs): 10 | kwargs['doc_type'] = kwargs.get('doc_type') or 'visits' 11 | BasicSyncCompiler.__init__(self, *args, **kwargs) 12 | 13 | def get_compiled_data(self): 14 | data_list = csv_to_list(self.raw_content, 15 | max_rows=10001, # 1w条记录最多 16 | max_columns=50, 17 | auto_fill=True 18 | ) 19 | objects = csv_data_to_objects(data_list) 20 | data = dict( 21 | objects = objects, 22 | raw_content = self.raw_content, 23 | data=data_list, 24 | ) 25 | return data 26 | 27 | -------------------------------------------------------------------------------- /farbox_bucket/client/sync_from/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from .sync_from import sync_from_farbox -------------------------------------------------------------------------------- /farbox_bucket/clouds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/clouds/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/clouds/dropbox/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | # Dropbox 这类的云端,不是优选项,相当于把整个 bucket 的逻辑彻底交给第三方更新,与 FarBox Bucket 的基本结构有冲突, 4 | # 在 Client 端**无法控制**同步的逻辑,并且是批量数据涌进来的,估计容易出问题。 5 | # 如果不处理批量涌入的问题,只处理 delta 可能会好一些,但是使用者是无法接受没有 『初始化』 数据的。 6 | # 再考虑到 FarBox 目前本身项目代码开放,自己跑一个脚本进行差量同步也是低成本的,而且**通用性**更高; 7 | # 通过 Dropbox 在服务端进行 Hook 的更新,考虑副作用的前提下,必要性不大。@2021 8 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/build_wechat_menu.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.clouds.wechat.utils.menu import create_menus 3 | 4 | 5 | def create_menus_on_wechat(): 6 | menus = { 7 | "button": [ 8 | { 9 | "name": u"+图", 10 | "sub_button": [ 11 | { 12 | "type": "pic_sysphoto", 13 | "name": u"拍摄照片", 14 | "key": "pic_sysphoto" 15 | }, 16 | { 17 | "type": "pic_weixin", 18 | "name": u"本地照片", 19 | "key": "pic_weixin" 20 | }, 21 | ], 22 | }, 23 | { 24 | "name": u"管理", 25 | "sub_button": [ 26 | { 27 | "type": "scancode_waitmsg", 28 | "name": u"扫码绑定", 29 | "key": "bind", 30 | }, 31 | { 32 | "type": "click", 33 | "name": u"解除绑定", 34 | "key": "unbind" 35 | }, 36 | { 37 | "type": "click", 38 | "name": u"当前状态", 39 | "key": "bind_status" 40 | }, 41 | ] 42 | }, 43 | ] 44 | } 45 | 46 | return create_menus(menus) 47 | 48 | 49 | if __name__ == '__main__': 50 | create_menus_on_wechat() -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'hepochen' 2 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/__message_template.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from farbox_bucket.utils.convert.jade2jinja import convert_jade_to_html 3 | 4 | SEND_MESSAGE_WECHAT_TEMPLATE = convert_jade_to_html(""" 5 | xml 6 | ToUserName 7 | FromUserName 8 | CreateTime= now.format('%s') 9 | MsgType text 10 | Content 11 | FuncFlag 0 12 | """) 13 | 14 | 15 | SEND_POSTS_WECHAT_TEMPLATE = convert_jade_to_html(""" 16 | xml 17 | ToUserName 18 | FromUserName 19 | CreateTime= now.format('%s') 20 | MsgType news 21 | ArticleCount= posts.length 22 | Articles 23 | for post in posts 24 | item 25 | Title 26 | Description 27 | img_get_vars = 'width=640&height=640&outbound_link_password='+get_outbound_link_password(days=7) 28 | if post.cover 29 | PicUrl 30 | elif loop.index==1 31 | PicUrl 32 | Url 33 | """) 34 | 35 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/_message_template.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | 3 | 4 | SEND_MESSAGE_WECHAT_TEMPLATE = """ 5 | 6 | 7 | 8 | 9 | %s 10 | 11 | text 12 | 13 | 14 | 15 | 0 16 | 17 | """ -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/check.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from flask import request, abort 3 | from farbox_bucket.utils import get_sha1, to_int 4 | import time 5 | 6 | 7 | def _is_from_wechat(token, ttl=120): 8 | # 这里的 token,实际上实在 wechat 的后台自己设置的 9 | rv = request.values 10 | nonce = rv.get('nonce') 11 | timestamp = rv.get('timestamp') 12 | if not nonce or not timestamp: 13 | return False 14 | values_to_sign = [token, nonce, timestamp] 15 | values_to_sign.sort() 16 | to_sign = ''.join(values_to_sign) 17 | signature = get_sha1(to_sign) 18 | if rv.get('signature') != signature: 19 | return False 20 | 21 | if ttl: 22 | timestamp = to_int(timestamp) 23 | now = time.time() 24 | if (now-timestamp) > ttl:# (默认)超过2分钟前就算请求过期了,校验失败 25 | return False 26 | 27 | return True # at last 28 | 29 | 30 | 31 | def check_is_from_wechat(token, ttl=120, raise_error=True): 32 | status = _is_from_wechat(token, ttl=ttl) 33 | if raise_error and not status: 34 | # 需要触发错误,并且 status==False 的情况下,直接400扔出 35 | abort(400, 'not allowed or expired') 36 | return False 37 | else: 38 | return status -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/menu.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | import requests 3 | import ujson as json 4 | from farbox_bucket.utils import string_types 5 | from .token import get_access_token, check_wechat_errors_and_update_token 6 | 7 | API_PREFIX = 'https://api.weixin.qq.com/cgi-bin/' 8 | 9 | def create_menus(menus): 10 | # wechat比较变态,menus需要是完整的json字符串 11 | url = API_PREFIX + 'menu/create' 12 | access_token = get_access_token(force_update=True) 13 | params = dict(access_token=access_token) 14 | #headers = {'content-type': 'application/json'} 15 | if isinstance(menus, string_types): 16 | data = menus 17 | else: 18 | data = json.dumps(menus, ensure_ascii=False) 19 | response = requests.post(url, data=data, params=params, verify=False) 20 | print response.json() 21 | try: 22 | json_data = response.json() 23 | check_wechat_errors_and_update_token(json_data) 24 | if json_data.get('errcode'): 25 | return False 26 | else: 27 | return True 28 | except: 29 | return False 30 | 31 | 32 | def get_menus(): 33 | url = API_PREFIX + 'menu/get' 34 | access_token = get_access_token() 35 | params = dict(access_token=access_token) 36 | response = requests.get(url, params=params, verify=False) 37 | return response.json() 38 | 39 | 40 | def delete_menus(): 41 | url = API_PREFIX + 'menu/delete' 42 | access_token = get_access_token() 43 | params = dict(access_token=access_token) 44 | response = requests.get(url, params=params, verify=False) 45 | return response.json() 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/message.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import time 3 | from jinja2 import Template 4 | from ._message_template import SEND_MESSAGE_WECHAT_TEMPLATE 5 | 6 | wechat_message_template = None 7 | def get_wechat_message_template(): 8 | global wechat_message_template 9 | if wechat_message_template is not None: 10 | return wechat_message_template 11 | wechat_message_template = Template(SEND_MESSAGE_WECHAT_TEMPLATE) 12 | return wechat_message_template 13 | 14 | 15 | def send_wechat_message(message, from_user_name="", to_user_name=""): 16 | # xml.FromUserName, xml.ToUserName 17 | # 被动地发送文本信息,即响应微信API的请求 18 | # set_content_type('application/xml') 19 | timestamp = str(int(time.time())) 20 | xml_content = SEND_MESSAGE_WECHAT_TEMPLATE % (from_user_name, to_user_name, timestamp, message) 21 | return xml_content 22 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/utils/token.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | import requests 3 | from farbox_bucket.utils.ssdb_utils import auto_cache_by_ssdb 4 | from farbox_bucket.utils.env import get_env 5 | 6 | WECHAT_APP_ID = get_env("wechat_app_id") 7 | WECHAT_APP_SECRET = get_env("wechat_app_secret") 8 | 9 | 10 | # 一个微信的open id 看起来可能是这样的: oZ454jpVhF15nHxs3ig-uCq_gqss 11 | 12 | API_PREFIX = 'https://api.weixin.qq.com/cgi-bin/' 13 | 14 | # 每天最多 2000 次 15 | def _get_access_token(): 16 | if not WECHAT_APP_ID or not WECHAT_APP_SECRET: 17 | return "" 18 | url = API_PREFIX + 'token' 19 | params = {'grant_type': 'client_credential', 'appid': WECHAT_APP_ID, 'secret': WECHAT_APP_SECRET} 20 | response = requests.get(url, params=params, verify=False) 21 | try: 22 | token = response.json().get('access_token') 23 | except: 24 | try: print(response.json().get("errmsg")) 25 | except: pass 26 | token = "" 27 | return token 28 | 29 | def get_access_token(force_update=False): 30 | # 7200s的有效期 31 | # # 一个小时更新一次 32 | return auto_cache_by_ssdb("wechat_token", value_func=_get_access_token, ttl=3600, force_update=force_update) 33 | 34 | 35 | def check_wechat_errors_and_update_token(json_data): 36 | if not json_data: 37 | return # ignore 38 | error_code = json_data.get("errcode") 39 | if error_code == 40001: # access_token失效了 40 | get_access_token(force_update=True) 41 | -------------------------------------------------------------------------------- /farbox_bucket/clouds/wechat/views.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from flask import abort 3 | from farbox_bucket.utils.env import get_env 4 | from farbox_bucket.server.web_app import app 5 | from farbox_bucket.bucket.token.utils import get_logined_bucket 6 | from farbox_bucket.clouds.wechat.bind_wechat import get_wechat_bind_code_for_bucket, get_wechat_user_docs_by_bucket 7 | from farbox_bucket.server.template_system.api_template_render import render_api_template_as_response 8 | from .wechat_handler import wechat_web_handler, WECHAT_TOKEN 9 | 10 | # 公众号后台获得公众号二维码,解析后是一个 URL 的字符串 11 | wechat_account_url = get_env("wechat_account_url") or "" 12 | wechat_account_url2 = get_env("wechat_account_url2") or "" 13 | 14 | @app.route("/__wechat_api", methods=["POST", "GET"]) 15 | def wechat_api_view(): 16 | return wechat_web_handler() 17 | 18 | 19 | @app.route("/__wechat_bind", methods=["POST", "GET"]) 20 | def wechat_bind_view(): 21 | if not WECHAT_TOKEN or not wechat_account_url: 22 | abort(404, "Wechat is not valid in current service") 23 | else: 24 | logined_bucket = get_logined_bucket(check=True) 25 | if not logined_bucket: 26 | return abort(404, "not login") 27 | bind_code = get_wechat_bind_code_for_bucket(logined_bucket) 28 | wechat_user_docs = get_wechat_user_docs_by_bucket(logined_bucket, with_name=True) 29 | response = render_api_template_as_response("wechat_bind.jade", 30 | wechat_user_docs=wechat_user_docs, 31 | bind_code=bind_code, 32 | wechat_account_url=wechat_account_url, 33 | wechat_account_url2 = wechat_account_url2) 34 | return response 35 | 36 | 37 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/build/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/deploy/build/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/deploy/build/build_client_image.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils import to_bytes, string_types, get_kwargs_from_console 3 | from xserver.docker_image.utils import build_docker_image 4 | 5 | 6 | docker_file_content = """FROM hepochen/pyweb:201908 7 | RUN apt-get -qq update 8 | ENV PYTHONIOENCODING=utf-8 9 | ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' 10 | RUN apt-get install -y locales && locale-gen en_US.UTF-8 11 | RUN pip install farbox_bucket 12 | CMD ["/usr/bin/python", "-m", "farbox_bucket.client.run"] 13 | """ 14 | 15 | 16 | 17 | # docker build -f Dockerfile -t hepochen/farbox_client:latest . 18 | 19 | def build_farbox_client_image(image_version): 20 | build_docker_image( 21 | image_name = 'farbox_client', 22 | image_version = image_version, 23 | docker_file_content = docker_file_content, 24 | ) 25 | 26 | 27 | # build_farbox_client version=202105 28 | def build_farbox_client_image_from_console(): 29 | kwargs = get_kwargs_from_console() 30 | image_version = kwargs.get('version') or '202105' 31 | build_farbox_client_image(image_version) -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/deploy/run/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/backend_jobs.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | import os 4 | os.environ['GEVENT_RESOLVER'] = 'ares' 5 | from gevent.monkey import patch_all; patch_all() 6 | import gevent 7 | import logging 8 | 9 | 10 | from farbox_bucket.server.backend.backend_jobs import backend_jobs 11 | 12 | if __name__ == '__main__': 13 | threads = [] 14 | for job in backend_jobs: 15 | job = gevent.spawn(job) 16 | threads.append(job) 17 | 18 | logging.info('start cronjob now!') 19 | 20 | gevent.joinall(threads) 21 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/backup/nginx_websocket.conf: -------------------------------------------------------------------------------- 1 | 2 | upstream websocket_server { 3 | server unix:/tmp/websocket_server.sock fail_timeout=0; 4 | } 5 | 6 | 7 | location /_realtime/ { 8 | access_log off; 9 | 10 | tcp_nodelay on; 11 | proxy_buffering off; 12 | 13 | proxy_pass http://websocket_server; 14 | proxy_http_version 1.1; 15 | proxy_set_header Upgrade $http_upgrade; 16 | proxy_set_header Connection $connection_upgrade; 17 | proxy_set_header X-Real-IP $remote_addr; 18 | proxy_set_header X-Real-Port $remote_port; 19 | proxy_set_header Host $host; 20 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 21 | 22 | proxy_read_timeout 1h; 23 | proxy_send_timeout 1h; 24 | } 25 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/gunicorn.conf.py: -------------------------------------------------------------------------------- 1 | #coding:utf8 2 | import os 3 | os.environ['GEVENT_RESOLVER'] = 'ares' 4 | import multiprocessing 5 | 6 | try: 7 | import psutil as pu 8 | except: 9 | pu = None 10 | 11 | cpu_cores = multiprocessing.cpu_count() 12 | if cpu_cores >=8 : 13 | workers = cpu_cores 14 | else: 15 | workers = cpu_cores*2 + 1 16 | if workers > 11: 17 | workers = 11 # max workers 18 | 19 | if pu: 20 | mem_info = pu.virtual_memory() 21 | if mem_info.total < 5*1024*1024*1024: # 5G以下 22 | workers = 2 23 | 24 | bind = "unix:/tmp/web_server.sock" 25 | daemon = False 26 | worker_class = 'gevent' 27 | worker_connections = 1000 # worker的并发数,1k为默认值 28 | max_requests = 6000 29 | #preload = True 30 | timeout = 30 31 | graceful_timeout = 30 32 | pidfile = '/tmp/web_server.pid' 33 | proc_name = 'fb_bucket' 34 | 35 | limit_request_line = 6500 36 | # in bytes just 0.5k #目前并没有什么大的数据上的交互,但会影响url的长度限制 37 | # updated 201-3-20 支付宝这边回调的URL有点长 1300+ 38 | 39 | # timeout 固定多少时间重启worker 40 | # graceful_timeout 接到重启后,多少时间后重启 -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/gunicorn_websocket.conf.py: -------------------------------------------------------------------------------- 1 | #coding:utf8 2 | import os 3 | os.environ['GEVENT_RESOLVER'] = 'ares' 4 | workers = 1 5 | bind = "unix:/tmp/websocket_server.sock" 6 | daemon = False 7 | worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' 8 | #worker_connections = 1000 9 | #max_requests = 5000 #默认为0,不然worker会被重启 10 | keepalive = 5 11 | timeout = 300 12 | graceful_timeout = 30 13 | pidfile = '/tmp/websocket_server.pid' 14 | limit_request_line = 500 #in bytes just 0.5k #目前并没有什么大的数据上的交互,但会影响url的长度限制 15 | proc_name = 'websocket' 16 | # timeout 固定多少时间重启worker 17 | # graceful_timeout 接到重启后,多少时间后重启 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/memcached.conf: -------------------------------------------------------------------------------- 1 | # memcached default config file 2 | # 2003 - Jay Bonci 3 | # This configuration file is read by the start-memcached script provided as 4 | # part of the Debian GNU/Linux distribution. 5 | 6 | # Run memcached as a daemon. This command is implied, and is not needed for the 7 | # daemon to run. See the README.Debian that comes with this package for more 8 | # information. 9 | -d 10 | 11 | # Log memcached's output to /var/log/memcached 12 | logfile /var/log/memcached.log 13 | 14 | # Be verbose 15 | # -v 16 | 17 | # Be even more verbose (print client commands as well) 18 | # -vv 19 | 20 | # Start with a cap of 64 megs of memory. It's reasonable, and the daemon default 21 | # Note that the daemon will grow to this size, but does not start out holding this much 22 | # memory 23 | # 1G 24 | -m 1024 25 | 26 | # Default connection port is 11211 27 | -p 11211 28 | 29 | # Run the daemon as root. The start-memcached will default to running as root if no 30 | # -u command is present in this config file 31 | -u root 32 | 33 | # Specify which IP address to listen on. The default is to listen on all IP addresses 34 | # This parameter is one of the only security measures that memcached has, so make sure 35 | # it's listening on a firewalled interface. 36 | -l 127.0.0.1 37 | 38 | # Limit the number of simultaneous incoming connections. The daemon default is 1024 39 | -c 5000 40 | 41 | # Lock down all paged memory. Consult with the README and homepage before you do this 42 | # -k 43 | 44 | # Return error when memory is exhausted (rather than removing items) 45 | # -M 46 | 47 | # Maximize core file limit 48 | # -r -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/nginx/nginx_body.conf: -------------------------------------------------------------------------------- 1 | 2 | types_hash_max_size 2048; 3 | 4 | # sendfile() is more efficient than the combination of read(2) and write(2) 5 | sendfile on; 6 | tcp_nopush on; 7 | 8 | client_max_body_size 300M; 9 | 10 | proxy_buffering off; 11 | proxy_buffer_size 128k; 12 | proxy_buffers 4 256k; 13 | proxy_busy_buffers_size 256k; 14 | #buffer large proxied requests to the filesystem 15 | proxy_temp_path /tmp/nginx-tmp; 16 | 17 | gzip on; 18 | gzip_min_length 1k; 19 | gzip_buffers 4 16k; 20 | gzip_http_version 1.0; 21 | gzip_comp_level 6; 22 | gzip_types text/plain application/x-javascript application/javascript text/css application/xml application/json font/ttf font/opentype application/vnd.ms-fontobject image/svg+xml; 23 | gzip_vary on; 24 | gzip_disable "msie6"; 25 | 26 | 27 | location ~* \.(wmv|exe|asp|php)$ { 28 | access_log off; 29 | error_log /dev/null; 30 | deny all; 31 | } 32 | 33 | location / { 34 | 35 | access_by_lua_file /mt/web/configs/openresty/access.lua; 36 | 37 | if ($http_user_agent ~* monitor ) { 38 | return 200; 39 | access_log off; 40 | } 41 | 42 | # if ($request_method !~ ^(GET|POST|PUT)$) { 43 | # return 405; 44 | # access_log off; 45 | #} 46 | 47 | #if ($http_user_agent ~* (ChinaCache|Webluker) ) { 48 | # access_log off; 49 | # return 403; 50 | #} 51 | 52 | #proxy_request_buffering off; 53 | access_log /mt/web/log/nginx_web_server.access.log main; 54 | proxy_set_header X-Forwarded-For $remote_addr; 55 | proxy_set_header Host $http_host; 56 | proxy_redirect off; 57 | proxy_set_header X-Protocol $scheme; 58 | proxy_pass http://app_server; 59 | 60 | #limit_conn conn_limit_per_ip 50; 61 | } 62 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/nginx/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDlefPmmwVOHE4l 3 | VFUfpFGbElPMKETK6G00fcdElS5ArSVyfi0w5kwz4Xxsu/1kA/Eenc0zWOQuMkaZ 4 | gbPx3Oe3tTlmg8//DjNuh5P2mSnPlHw3fiXA3/h9vVqGjQKqZxdXyWSrUUzh7493 5 | M2ZjUgT2il9ZkhJ+nho9u8H0wFI6g1PYvjc8v+AQCkk7fXSqRlvJREt6DfAHaWEL 6 | UJjg101L2ZBysLRgY7wYnVIl2VagF41WSfEF9mGUBlRhitxJq5laJvyWkCdbp4wh 7 | /OSwX8PokmfYr9v5K9rFWv08FsHJRzdqZfOfca97oWROMzYcOgFMqFijjIN/qZF5 8 | Ep7ro82pAgMBAAECggEAX/426BWbD4TJr9XqUfUz6fGtui5yMIsv+5BOBkN9eLK1 9 | goKDtvvvjphZq0asheUcPVl3mG4r7aM8Y0SdmKR3DiMyIs8q+B/hpe/zFmoV0m7j 10 | 03biph9K3JaUQsrD/bJoUmG9yeiVh8HlutB2mT+bMeLKZjazo+HRSle7l/3HqA+y 11 | 0OHqAbFw/2befMEIvvjuPxpf0s485JHrA6fARLdVmLyAQqcYifdO5LdySaxzqr4Z 12 | KsRlxGRBOA0fk6UlYZa+t2Idhgid/NoqL2/SP3cyY5QqmcuIq3ZVDgIbZSoUPJWo 13 | dA8GY8+7OWSIdPmu6d3RG/6ahFLl+OLSiIu9RWc6XQKBgQD8+u29EAz1NMR2l2LS 14 | Zi2dtmOift68+NbI+txqYvXka1AOSSuenAl9OrU1ss4VfOSPom2XoBn0rTR0fvHQ 15 | 9rthevvey0bzBWzDkoxSnkYR/RpAskzKoU9teIgPsK1EkGf7LkARLeRh6AmWDg9p 16 | yEdNYFUrulwCEN0uWyw6Z3xzewKBgQDoNzMkoh6WPy5GBjjuS6nVY+p3MumnNqmI 17 | KmzEmrN99idR5JMNhWD4X9dUrnHzOESOer6aU5+x6mSfvE4zRjNAqFZocxs5YmB6 18 | +/vqGzNik8pE63HO22U7hCshZUyovXs073EKorl4ofdj7uX4/Hj9/lmOzSTLBJTB 19 | zz5sWXS4KwKBgCaf0ShqiO/vLIfGuUnSW+iWbkPjBvLnMzPgSULc9Rn44HIt7cD7 20 | pd8+1WfrcteJCAR+EilyQkQ/JaEbuKPk59sMQeRUOPLlwyNg/pemnqAkepuiHWNa 21 | mZvnKS7sFKhBO/73osR8sz/Xg48remL2NxyzNo3EmEOge4SWWvxXzIJ9AoGBAMrw 22 | 6u5aDLcsEkm6SGjhkVRflNqFkTPEjtbklNghhzpPNL/aLjYD2eJXCD4GkqmIBEos 23 | tYT0Dj2T5kq/a+xdro8UTyfLaQB2nHD+5YMMMJU3Ke+fgH/St58S/AqcmClkJ1f0 24 | 5mrwGX12t1kLwMogA7GeZzBo0n3mzAaxExHoAByvAoGBANSn/Y6GV6ct+BQIuJPj 25 | XHHKdKdTjD0CDpZJK7TAxHYvrNim+XL572xPXlgZMogEGCKwd0N/xxb9aOSuQk3R 26 | 0jFjTS6Rp8cNesqfWasfa1AIdWJjgha2uqWa+6U4E/XQ5zRMVSziEworMb8Uy6Nl 27 | 0kNd1qbTdCvSXY/4139IfnvF 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/openresty/access.lua: -------------------------------------------------------------------------------- 1 | local log = ngx.log 2 | local memcached = require "resty.memcached" 3 | local memc, err = memcached:new() 4 | if not memc then 5 | log(ngx.ERR, "failed to instantiate memc: ", err) 6 | return 7 | end 8 | 9 | memc:set_timeout(1000) -- 1 sec 10 | 11 | local ok, err = memc:connect("127.0.0.1", 11211) 12 | if not ok then 13 | log(ngx.ERR, "failed to connect: ", err) 14 | return 15 | end 16 | 17 | local client_ip = ngx.var.remote_addr 18 | 19 | local ip_block_id = "block-" .. client_ip 20 | local ip_block_value, flags, err = memc:get(ip_block_id) 21 | 22 | if err then 23 | log(ngx.ERR, "memcached error: ", err) 24 | return 25 | end 26 | 27 | -- put it into the connection pool of size 100, 28 | -- with 10 seconds max idle timeout 29 | local ok, err = memc:set_keepalive(10000, 100) 30 | if not ok then 31 | log(ngx.ERR, "cannot set keepalive: ", err) 32 | end 33 | 34 | 35 | if ip_block_value then 36 | ngx.exit(ngx.HTTP_FORBIDDEN) 37 | end 38 | 39 | 40 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/configs/ssdb.conf: -------------------------------------------------------------------------------- 1 | # ssdb-server config 2 | # MUST indent by TAB! 3 | 4 | # absolute path, or relative to path of this file, directory must exists 5 | work_dir = /mt/ssdb/data 6 | pidfile = /var/run/ssdb.pid 7 | 8 | server: 9 | ip: 0.0.0.0 10 | port: 8888 11 | # bind to public ip 12 | #ip: 0.0.0.0 13 | # format: allow|deny: all|ip_prefix 14 | # multiple allows or denys is supported 15 | #deny: all 16 | #allow: 127.0.0.1 17 | #allow: 192.168 18 | # auth password must be at least 32 characters 19 | #auth: very-strong-password 20 | #readonly: yes 21 | # in ms, to log slowlog with WARN level 22 | #slowlog_timeout: 5 23 | 24 | replication: 25 | binlog: yes 26 | # Limit sync speed to *MB/s, -1: no limit 27 | sync_speed: -1 28 | slaveof: 29 | # to identify a master even if it moved(ip, port changed) 30 | # if set to empty or not defined, ip:port will be used. 31 | #id: svc_2 32 | # sync|mirror, default is sync 33 | #type: sync 34 | #host: localhost 35 | #port: 8889 36 | 37 | logger: 38 | level: info 39 | output: /mt/web/log/ssdb_log.txt 40 | rotate: 41 | size: 1000000000 42 | 43 | leveldb: 44 | # in MB 45 | cache_size: 500 46 | # in MB 47 | write_buffer_size: 24 48 | # in MB/s 49 | compaction_speed: 1000 50 | # yes|no 51 | compression: yes 52 | 53 | # compression 10 times? 54 | # normally, cache_size + write_buffer_size*4 + 32 = 628 MB, about 1GB in fact 55 | # max in short time, cache_size + 10*write_buffer_size*66 + 32 = 16 GB 56 | -------------------------------------------------------------------------------- /farbox_bucket/deploy/run/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run -d \ 3 | -p 7788:80 -p 443:443 -p 80:80 \ 4 | -v /home/run/$name$/configs:/mt/web/configs \ 5 | -v /data/log/$name$:/mt/web/log \ 6 | -v /data/$name$:/mt/web/data \ 7 | -v /data/$name$_ssdb:/mt/ssdb/data \ 8 | -v /static/$name$:/mt/web/static \ 9 | -v /log/docker:/mt/docker/log \ 10 | hepochen/farbox_bucket:latest -------------------------------------------------------------------------------- /farbox_bucket/for_dev/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/for_dev/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/for_dev/check_alipay.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.pay.alipay_api import AlipayAPI 3 | 4 | def check_qrcode_alipay(app_id, private_key): 5 | alipay = AlipayAPI(app_id=app_id, private_key=private_key) 6 | info = alipay.pay(0.01, 'hello', 'world', qrcode=True) 7 | print(info) 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /farbox_bucket/for_dev/check_encrypt_performance.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.encrypt.des_encrypt import encrypt_des, decrypt_des 3 | from farbox_bucket.utils.encrypt.aes_encrypt import encrypt_aes, decrypt_aes 4 | import time 5 | 6 | content = 'hello world this is test' 7 | password = 'passwordhere' 8 | 9 | def check_encrypt_performance(): 10 | t1 = time.time() 11 | for i in range(10000): 12 | c = encrypt_aes(content, password, encode_type='raw') 13 | d = decrypt_aes(c, password, encode_type='raw') 14 | print("original_size is %s, encrypted_content size is %s, is equal: %s" % (len(content), len(c), d==content)) 15 | print("encrypt & decrypt costs %s seconds" % (time.time() - t1)) 16 | 17 | 18 | 19 | t1 = time.time() 20 | for i in range(10000): 21 | c = encrypt_des(content, password, ) 22 | d = decrypt_des(c, password, ) 23 | print("encrypt & decrypt costs %s seconds, in base64 " % (time.time() - t1)) 24 | 25 | 26 | 27 | if __name__ == "__main__": 28 | check_encrypt_performance() -------------------------------------------------------------------------------- /farbox_bucket/for_dev/jinja_template_source.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from jinja2.sandbox import SandboxedEnvironment 3 | from farbox_bucket.utils.convert.jade2jinja import convert_jade_to_html 4 | 5 | def get_jinja_source_code_from_jade(jade_content, with_lines=True): 6 | source_code_lines = [] 7 | html_content = convert_jade_to_html(jade_content) 8 | env = SandboxedEnvironment() 9 | #env.compile(html_content) 10 | source = env._parse(html_content, None, None) 11 | python_code = env._generate(source, None, None, False) 12 | python_lines = python_code.split("\n") 13 | for (i, python_line) in enumerate(python_lines): 14 | if with_lines: 15 | source_code_lines.append("%s:%s" % (i+1, python_line)) 16 | else: 17 | source_code_lines.append(python_line) 18 | return "\n".join(source_code_lines) 19 | 20 | 21 | def print_jinja_source_code_from_jade_file(jade_filepath): 22 | with open(jade_filepath) as f: 23 | jade_content = f.read() 24 | source_code = get_jinja_source_code_from_jade(jade_content, with_lines=True) 25 | print(source_code) -------------------------------------------------------------------------------- /farbox_bucket/for_dev/markdown_post_compile_info.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | import os 3 | from farbox_bucket.settings import MAX_RECORD_SIZE 4 | from farbox_bucket.client.sync.compiler_worker import get_compiler_data_directly 5 | from farbox_bucket.utils.data import json_dumps 6 | 7 | def get_markdown_post_compiled_info(md_filepath, content_times = 1): 8 | with open(md_filepath, "rb") as f: 9 | raw_content = f.read() 10 | content = raw_content * content_times 11 | if len(content) > MAX_RECORD_SIZE: 12 | print("content size too big") 13 | filename = os.path.split(md_filepath)[-1] 14 | compiled_data = get_compiler_data_directly(filename, content=content) 15 | record_size = len(json_dumps(compiled_data)) 16 | print("file_type: %s, zipped:%s, file_size:%s, record_size:%s" %(compiled_data.get("type"), 17 | compiled_data.get("_zipped"), 18 | compiled_data.get("size"), 19 | record_size)) 20 | 21 | 22 | -------------------------------------------------------------------------------- /farbox_bucket/i18n/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import zh_cn 3 | 4 | 5 | default_i18n_data = { 6 | 'zh_cn': zh_cn.data, 7 | 8 | } 9 | -------------------------------------------------------------------------------- /farbox_bucket/server/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/avatar.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import requests 3 | from farbox_bucket.utils import get_md5, string_types 4 | from farbox_bucket.utils.ssdb_utils import hexists 5 | 6 | 7 | def get_avatar_id(email): # 根据邮件地址,或者对应的m5值作为avatar_id 8 | if isinstance(email, string_types) and email: 9 | email = email.lower().strip() 10 | if '@' in email: 11 | return get_md5(email) 12 | else: 13 | return email 14 | return None # if not 15 | 16 | 17 | 18 | def get_gavatar_image_content(email): 19 | avatar_id = get_avatar_id(email) 20 | gravatar_url = 'https://en.gravatar.com/%s.json' % avatar_id 21 | response = requests.get(gravatar_url, verify=False, timeout=10) 22 | if response.status_code != 200: 23 | return '' 24 | response_info = response.json() 25 | gravatar_image_url = response_info['entry'][0]['thumbnailUrl'] 26 | if gravatar_image_url: 27 | try: 28 | image_response = requests.get(gravatar_image_url, timeout=10, verify=False) 29 | if image_response.status_code > 300: 30 | return '' # ignore 31 | else: 32 | return image_response.content 33 | except: 34 | return '' 35 | 36 | def has_avatar(email_md5): 37 | if '@' in email_md5: 38 | email_md5 = get_avatar_id(email_md5) 39 | if not isinstance(email_md5, string_types): 40 | return False 41 | if len(email_md5) > 64: 42 | return False 43 | email_md5 = email_md5.lower().strip() 44 | return hexists('_avatar', email_md5) 45 | 46 | 47 | def get_avatar_url(email): 48 | avatar_id = get_avatar_id(email) 49 | url = '/service/avatar/%s' % avatar_id 50 | return url 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /farbox_bucket/server/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/backend/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/backend/backend_jobs.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | from farbox_bucket.utils.env import get_env 4 | from farbox_bucket.server.backend.service import keep_watch_nginx, restart_backend_per_day, keep_watch_memcache, run_logrotate 5 | 6 | should_sync_buckets = get_env("should_sync_buckets_in_backend") 7 | if should_sync_buckets == "no": 8 | should_sync_buckets = False 9 | 10 | backend_jobs = [ 11 | keep_watch_nginx, 12 | keep_watch_memcache, 13 | restart_backend_per_day, 14 | run_logrotate, 15 | ] 16 | 17 | if should_sync_buckets: 18 | # 减少 backend 的内存占用,如果只是守护性质的话 19 | from farbox_bucket.server.backend.sync.buckets import sync_buckets_from_remote_marked, sync_buckets_from_remote_nodes 20 | backend_jobs.append(sync_buckets_from_remote_marked) 21 | backend_jobs.append(sync_buckets_from_remote_nodes) -------------------------------------------------------------------------------- /farbox_bucket/server/backend/status/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/backend/status/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/backend/sync/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/backend/sync/buckets.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | 4 | from farbox_bucket.utils.gevent_run import run_long_time 5 | 6 | from farbox_bucket.bucket.sync.node import sync_buckets_from_remote_nodes_by_gevent 7 | from farbox_bucket.bucket.sync.remote import sync_buckets_from_remote_marked_by_gevent 8 | 9 | 10 | 11 | @run_long_time(wait=10, sleep_at_end=10*60, log='sync from remote nodes') 12 | def sync_buckets_from_remote_nodes(): 13 | sync_buckets_from_remote_nodes_by_gevent(pool_size=20) 14 | 15 | 16 | 17 | 18 | @run_long_time(wait=10, sleep_at_end=60, log='sync buckets marked from remote') 19 | def sync_buckets_from_remote_marked(): 20 | sync_buckets_from_remote_marked_by_gevent(pool_size=30) 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /farbox_bucket/server/backend/sync/utils.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | import os 4 | 5 | 6 | def get_node_url(node, route='farbox_bucket_message_api'): 7 | node = node or '' 8 | if '://' in node: 9 | node = node.split('://')[-1] 10 | node = node.strip().strip('/').strip() 11 | if not node: 12 | node = os.environ.get('DEFAULT_NODE') or '127.0.0.1:7788' 13 | if ':' not in node: 14 | node = '%s:7788' % node 15 | node_url = 'http://%s/%s' % (node, route.lstrip('/')) 16 | return node_url -------------------------------------------------------------------------------- /farbox_bucket/server/bucket_render/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/bucket_render/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/bucket_render/builtin_theme/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/bucket_render/builtin_theme/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/bucket_render/builtin_theme/_render.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | from .wiki import show_wiki_as_sub_site, show_wiki_nodes_as_sub_site 4 | from .album import show_albums_as_sub_site 5 | 6 | 7 | sub_site_funcs = [ 8 | show_albums_as_sub_site, 9 | show_wiki_as_sub_site, 10 | show_wiki_nodes_as_sub_site, 11 | ] 12 | 13 | def show_builtin_theme_as_sub_site(): 14 | for sub_site_func in sub_site_funcs: 15 | sub_site_html = sub_site_func() 16 | if sub_site_html: 17 | return sub_site_html 18 | -------------------------------------------------------------------------------- /farbox_bucket/server/comments/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/comments/contacts.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from farbox_bucket.utils import smart_unicode, get_value_from_data 3 | from farbox_bucket.server.comments.utils import get_comments 4 | 5 | 6 | 7 | def get_contacts_from_comments(comments): 8 | # 从评论中获取name + email的字典 unicode 9 | # comments 是一个 dict 类型组成的 list 10 | contacts = dict() 11 | for comment in comments: 12 | if not isinstance(comment, dict): 13 | continue 14 | author = comment.get('author') 15 | email = comment.get('email') 16 | if author and email: 17 | contacts[smart_unicode(author)] = email.lower() 18 | return contacts 19 | 20 | 21 | 22 | def get_default_contacts_from_post(post): 23 | contacts = {} 24 | if isinstance(post, dict) and post.get('type') == 'post': 25 | from_address = get_value_from_data(post, 'metadata.from') 26 | from_author_name = get_value_from_data(post, 'metadata.author') 27 | if from_address and from_author_name: 28 | contacts[from_author_name] = from_address 29 | return contacts 30 | 31 | 32 | def get_all_contacts_from_post(post, comments=None): 33 | if not post: 34 | return {} 35 | if comments is None: 36 | comments = get_comments(post) 37 | 38 | contacts = get_contacts_from_comments(comments) # it's a dict 39 | 40 | 41 | # 从 post 的 meta中处理, 一般都是 Bitcron Mail 产生的post,才有这样的属性 42 | contacts.update(get_default_contacts_from_post(post)) 43 | 44 | # name 全部 lower 化,这样才能@的时候,不敏感 45 | lower_name_contacts = {smart_unicode(k).lower():v for k, v in contacts.items()} 46 | return lower_name_contacts 47 | 48 | 49 | 50 | def get_contacts_from_new_comment(new_comment): 51 | # new_comment 是 web.utils.comment.add.NewComment 的实例化对象 52 | return get_all_contacts_from_post(new_comment.parent_obj) 53 | 54 | 55 | -------------------------------------------------------------------------------- /farbox_bucket/server/dangerous/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/dangerous/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/dangerous/install_py_package.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import os 3 | import re 4 | from gevent import spawn_later 5 | 6 | 7 | def restart_web_service(): 8 | try: 9 | with os.popen("kill -HUP `cat /tmp/web_server.pid`") as f: 10 | f.read() 11 | except: 12 | pass 13 | 14 | def install_py_package_by_web(url): 15 | if not "://" in url: 16 | return 17 | # https://files.pythonhosted.org/packages/57/43/8d7120e714fbaa6d44ab381333f8b3a2960f2bdfebe3091c68c42d89ee14/farbox_bucket-0.1880.tar.gz 18 | if not re.match("[:/a-z0-9\-_.]+$", url): 19 | return "this url is invalid" 20 | try: 21 | with os.popen("pip install %s" % url) as f: 22 | result = f.read() 23 | except: 24 | result = "" 25 | spawn_later(3, restart_web_service) 26 | return result 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /farbox_bucket/server/dangerous/log_rotate.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import os 3 | 4 | def do_install_project_log_rotate(force=False): 5 | config_folder = "/etc/logrotate.d" 6 | if not os.path.isdir(config_folder): 7 | return 8 | project_log_rotate_config_filepath = os.path.join(config_folder, "farbox_bucket") 9 | if not force and os.path.isfile(project_log_rotate_config_filepath): 10 | return 11 | project_log_rotate_content = """/mt/web/log/nginx_web_server.access.log { 12 | rotate 7 13 | size 5k 14 | dateext 15 | dateformat -%Y-%m-%d 16 | missingok 17 | compress 18 | sharedscripts 19 | postrotate 20 | test -r /var/run/nginx.pid && kill -USR1 `cat /var/run/nginx.pid` 21 | endscript 22 | } 23 | """ 24 | with open(project_log_rotate_config_filepath, "wb") as f: 25 | f.write(project_log_rotate_content) 26 | 27 | 28 | def install_project_log_rotate(force=False): 29 | try: 30 | do_install_project_log_rotate(force=force) 31 | except: 32 | pass -------------------------------------------------------------------------------- /farbox_bucket/server/dangerous/restart.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import os 3 | from .log_rotate import install_project_log_rotate 4 | 5 | def try_to_reload_web_app(restart_backend=False): 6 | install_project_log_rotate(force=True) 7 | if not os.path.isfile("/tmp/web_server.pid"): 8 | return 9 | try: 10 | with os.popen("kill -HUP `cat /tmp/web_server.pid`") as f: 11 | f.read() 12 | except: 13 | pass 14 | 15 | if restart_backend: 16 | try: 17 | with os.popen("/usr/local/bin/supervisorctl restart farbox_bucket_backend") as f: 18 | f.read() 19 | except: 20 | pass -------------------------------------------------------------------------------- /farbox_bucket/server/es/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/es/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/es/es_status.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.bucket.utils import get_bucket_last_record_id 3 | from .es_client import get_es_client, doc_fields_in_es, make_sure_es_indexes 4 | from .es_utils import get_es_index_doc, search_es 5 | 6 | 7 | 8 | 9 | def get_es_status_for_bucket(bucket): 10 | es = get_es_client() 11 | status = dict( 12 | db_last_id = get_bucket_last_record_id(bucket), 13 | es_is_valid = False if not es else True, 14 | ) 15 | if not es: 16 | return status 17 | es_info = get_es_index_doc('info', bucket) 18 | if es_info: 19 | status["es_cursor"] = es_info.get("cursor") 20 | try: 21 | es_count_result = es.count(q='bucket:"%s"' % bucket, routing=bucket) 22 | es_count = es_count_result.get("count") or 0 23 | except: 24 | es_count = 0 25 | status["es_count"] = es_count 26 | status["es_indexes_ok"] = es.indices.exists("info") and es.indices.exists("doc") 27 | 28 | return status -------------------------------------------------------------------------------- /farbox_bucket/server/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/helpers/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/realtime/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | # 不能把app变量放到这里来,因为app本身会开启一些loop的命令,会导致其它地方调用websocket_server.xxx下的utils,会强启app内的命令 -------------------------------------------------------------------------------- /farbox_bucket/server/static/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/auto_toc/auto_toc.coffee: -------------------------------------------------------------------------------- 1 | 2 | get_toc_default_top = -> 3 | return $('.doc_toc').offset().top or 0 4 | 5 | $(document).ready => 6 | toc_selector = '.doc_toc' 7 | current_active_class = 'current_active' 8 | toc_element = $(toc_selector) 9 | 10 | $('.doc_toc a').click -> 11 | toc_content_dom_id = $(this).attr('href').slice(1) 12 | if toc_content_dom_id 13 | toc_content_dom = $('#'+toc_content_dom_id) 14 | y = toc_content_dom.offset().top 15 | scroll_to_y = y 16 | if scroll_to_y < 0 17 | scroll_to_y = 0 18 | $('body,html').animate {scrollTop: scroll_to_y }, 800 19 | new_hash_name = '#'+toc_content_dom_id 20 | if history.pushState 21 | history.pushState(null, null, new_hash_name); 22 | else 23 | window.location.hash = new_hash_name 24 | return false 25 | 26 | remove_current_toc_active_class = -> 27 | $(toc_selector+' .'+current_active_class).removeClass(current_active_class) 28 | 29 | toc_element.on 'activate.bs.scrollspy', -> 30 | current_active_element = $(toc_selector+' .active').last() 31 | remove_current_toc_active_class() 32 | current_active_element.addClass(current_active_class) 33 | 34 | toc_element.on 'clear.bs.scrollspy', remove_current_toc_active_class 35 | 36 | 37 | $('.doc_toc_container').affix({ 38 | offset: { 39 | top: (toc_offset_top if toc_offset_top?) or get_toc_default_top(), 40 | bottom: (toc_offset_bottom if toc_offset_bottom?) or 0 41 | } 42 | }); 43 | 44 | $(document).on 'affixed.bs.affix', -> 45 | height = (document.body.clientHeight - 100) or 'auto' 46 | $('.doc_toc').css('max-height', height) 47 | 48 | 49 | $('body').scrollspy({target: toc_selector}) 50 | 51 | if $(window).width()< 480 52 | $('.doc_toc').css('display', 'none') 53 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/auto_toc/auto_toc.css: -------------------------------------------------------------------------------- 1 | .doc_toc { 2 | position: static; 3 | font-size: 12px; } 4 | .doc_toc a { 5 | text-decoration: none; 6 | line-height: 1.1; 7 | color: #555555; } 8 | .doc_toc ul { 9 | line-height: 1.8em; 10 | list-style: none; 11 | padding-left: 20px; } 12 | .doc_toc ul li { 13 | list-style: none; } 14 | .doc_toc ul ul { 15 | padding-left: 16px; } 16 | .doc_toc ul ul ul { 17 | padding-left: 12px; } 18 | .doc_toc li { 19 | margin: 0.5em auto; } 20 | .doc_toc li ul { 21 | padding: 0 0 0 1em; } 22 | .doc_toc li > ul { 23 | margin-top: -5px; 24 | margin-bottom: 6px; } 25 | .doc_toc .active > a { 26 | color: #000000; 27 | font-weight: bold; } 28 | 29 | .affix { 30 | position: fixed !important; } 31 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/auto_toc/auto_toc.scss: -------------------------------------------------------------------------------- 1 | $link_color: #555555; 2 | $active_color: #000000; 3 | 4 | 5 | .doc_toc{ 6 | position: static; 7 | font-size:12px; 8 | // width: 200px; 9 | 10 | a{ 11 | text-decoration: none; 12 | line-height: 1.1; 13 | color: $link_color; 14 | } 15 | 16 | 17 | ul { 18 | line-height: 1.8em; 19 | list-style: none; 20 | padding-left: 20px; 21 | li { 22 | list-style: none; 23 | } 24 | 25 | ul{ 26 | padding-left: 16px; 27 | } 28 | ul ul{ 29 | padding-left: 12px; 30 | } 31 | } 32 | 33 | 34 | li{ 35 | margin: 0.5em auto; 36 | 37 | ul{ 38 | padding: 0 0 0 1em; 39 | } 40 | 41 | & > ul{ 42 | margin-top: -5px; 43 | margin-bottom: 6px; 44 | } 45 | } 46 | 47 | .active{ 48 | & > a{ 49 | color: $active_color; 50 | font-weight: bold; 51 | } 52 | } 53 | 54 | } 55 | 56 | .affix { 57 | position: fixed !important; 58 | } 59 | 60 | 61 | 62 | /* 63 | @media screen and (max-width: 680px) { 64 | .toc_container{ 65 | display: none; 66 | } 67 | } 68 | */ -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/form/ajax.coffee: -------------------------------------------------------------------------------- 1 | @run_ajax_backend = (url, method, callback_func, data)-> 2 | if typeof(data)=='string' 3 | data = $.parseJSON(data) 4 | if typeof(callback_func)=='string' 5 | callback_func = window[callback_func] 6 | ajax_data = 7 | url: url 8 | method: method 9 | success: (response_data)-> 10 | if callback_func 11 | callback_func(true, response_data) 12 | error: (response_data)-> 13 | if callback_func 14 | callback_func(false, response_data) 15 | if data 16 | $.extend(ajax_data, data) 17 | $.ajax(ajax_data) 18 | return false 19 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/form/ajax.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | this.run_ajax_backend = function(url, method, callback_func, data) { 4 | var ajax_data; 5 | if (typeof data === 'string') { 6 | data = $.parseJSON(data); 7 | } 8 | if (typeof callback_func === 'string') { 9 | callback_func = window[callback_func]; 10 | } 11 | ajax_data = { 12 | url: url, 13 | method: method, 14 | success: function(response_data) { 15 | if (callback_func) { 16 | return callback_func(true, response_data); 17 | } 18 | }, 19 | error: function(response_data) { 20 | if (callback_func) { 21 | return callback_func(false, response_data); 22 | } 23 | } 24 | }; 25 | if (data) { 26 | $.extend(ajax_data, data); 27 | } 28 | $.ajax(ajax_data); 29 | return false; 30 | }; 31 | 32 | }).call(this); 33 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/form/fields/color.coffee: -------------------------------------------------------------------------------- 1 | 2 | @when_mini_colors_show = -> 3 | if after_mini_colors_show? 4 | after_mini_colors_show() 5 | 6 | @when_mini_colors_hide = -> 7 | if after_mini_colors_hide? 8 | after_mini_colors_hide() 9 | 10 | 11 | @install_mini_colors = -> 12 | $('.color_input').minicolors({show:when_mini_colors_show, hide: when_mini_colors_hide}) 13 | 14 | $(document).ready -> 15 | install_mini_colors() -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/form/fields/color.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | this.when_mini_colors_show = function() { 4 | if (typeof after_mini_colors_show !== "undefined" && after_mini_colors_show !== null) { 5 | return after_mini_colors_show(); 6 | } 7 | }; 8 | 9 | this.when_mini_colors_hide = function() { 10 | if (typeof after_mini_colors_hide !== "undefined" && after_mini_colors_hide !== null) { 11 | return after_mini_colors_hide(); 12 | } 13 | }; 14 | 15 | this.install_mini_colors = function() { 16 | return $('.color_input').minicolors({ 17 | show: when_mini_colors_show, 18 | hide: when_mini_colors_hide 19 | }); 20 | }; 21 | 22 | $(document).ready(function() { 23 | return install_mini_colors(); 24 | }); 25 | 26 | }).call(this); 27 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/form/fields/icon.coffee: -------------------------------------------------------------------------------- 1 | 2 | 3 | @pick_icon_for_field = (click_dom)-> 4 | parent_dom = $(click_dom).parent() 5 | icon_input_dom = parent_dom.find('.icon_input') 6 | icon_input_dom.inputIconSelector() 7 | 8 | @when_icon_inserted = (icon_input_dom)-> 9 | if icon_input_dom.hasClass('icon_input') 10 | field_dom = icon_input_dom.parent() 11 | i_dom = field_dom.find('span i') 12 | i_dom.html('') 13 | i_dom.attr('class', icon_input_dom.val()) 14 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/form/fields/icon.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | this.pick_icon_for_field = function(click_dom) { 4 | var icon_input_dom, parent_dom; 5 | parent_dom = $(click_dom).parent(); 6 | icon_input_dom = parent_dom.find('.icon_input'); 7 | return icon_input_dom.inputIconSelector(); 8 | }; 9 | 10 | this.when_icon_inserted = function(icon_input_dom) { 11 | var field_dom, i_dom; 12 | if (icon_input_dom.hasClass('icon_input')) { 13 | field_dom = icon_input_dom.parent(); 14 | i_dom = field_dom.find('span i'); 15 | i_dom.html(''); 16 | return i_dom.attr('class', icon_input_dom.val()); 17 | } 18 | }; 19 | 20 | }).call(this); 21 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/js_view/images/border1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/api/html/js_view/images/border1.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/js_view/images/border2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/api/html/js_view/images/border2.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/js_view/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/api/html/js_view/images/loading.gif -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/html/js_view/images/readme.txt: -------------------------------------------------------------------------------- 1 | for color box mainly -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/modal.coffee: -------------------------------------------------------------------------------- 1 | g = this 2 | @current_modal_click_button_value = null 3 | @current_modal_click_button_value2 = null 4 | @current_modal_click_button_value3 = null 5 | 6 | $(document).ready -> 7 | $(document).on 'modal:close', (event, modal)-> 8 | try Essage.hide() 9 | 10 | 11 | $('input.modal_with_selector').each (i,dom)-> 12 | dom = $(dom) 13 | modal_dom = $(dom.attr('data-modal-dom')) 14 | click_dom = $(dom.attr('data-click-dom')) 15 | click_dom.click -> 16 | modal_dom.modal() 17 | current_click_dom = $(this) 18 | g.current_modal_click_button_value = current_click_dom.attr('data-value') 19 | g.current_modal_click_button_value2 = current_click_dom.attr('data-value2') 20 | g.current_modal_click_button_value3 = current_click_dom.attr('data-value3') 21 | if after_modal_display? 22 | try after_modal_display(current_click_dom, modal_dom) 23 | return false 24 | 25 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/modal.css: -------------------------------------------------------------------------------- 1 | .modal_wrap { 2 | width: 70%; } 3 | 4 | .modal_container { 5 | max-height: 550px; 6 | overflow-y: auto; 7 | overflow-x: hidden; 8 | color: #333; 9 | font-size: 14px; } 10 | 11 | .modal_wrap ::-webkit-scrollbar { 12 | width: 4px; 13 | margin: 0 1px; 14 | background: #fdfdfd; } 15 | .modal_wrap ::-webkit-scrollbar:horizontal { 16 | height: 4px; } 17 | .modal_wrap ::-webkit-scrollbar-button { 18 | height: 0; } 19 | .modal_wrap ::-webkit-scrollbar-track { 20 | border-radius: 2px; } 21 | .modal_wrap ::-webkit-scrollbar-track-piece { 22 | background-color: #fdfdfd; 23 | border-radius: 2px; } 24 | .modal_wrap ::-webkit-scrollbar-thumb { 25 | width: 8px; 26 | margin: 0 1px; 27 | border-radius: 2px; 28 | background: #c4c4c4; } 29 | .modal_wrap ::-webkit-scrollbar-thumb:hover { 30 | background: #909090; } 31 | 32 | .modal { 33 | font-size: 14px; } 34 | .modal form { 35 | color: #555; } 36 | .modal form h2 { 37 | color: #111; 38 | margin-top: 1em !important; } 39 | .modal form .fields .field { 40 | margin: 0.8em auto 1.2em auto !important; } 41 | 42 | @media screen and (min-width: 48em) { 43 | .modal .pure-u-md-1-3, .modal .pure-u-md-8-24 { 44 | width: 50%; } 45 | .modal .pure-u-lg-1-3, .modal .pure-u-lg-8-24 { 46 | width: 50%; } 47 | } 48 | 49 | @media screen and (max-width: 48em) { 50 | .modal { 51 | width: auto !important; } 52 | form .field { 53 | margin: 0.5em auto !important; 54 | padding-right: 0 !important; } 55 | } 56 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/modal.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | var g; 4 | 5 | g = this; 6 | 7 | this.current_modal_click_button_value = null; 8 | 9 | this.current_modal_click_button_value2 = null; 10 | 11 | this.current_modal_click_button_value3 = null; 12 | 13 | $(document).ready(function() { 14 | $(document).on('modal:close', function(event, modal) { 15 | try { 16 | return Essage.hide(); 17 | } catch (_error) {} 18 | }); 19 | return $('input.modal_with_selector').each(function(i, dom) { 20 | var click_dom, modal_dom; 21 | dom = $(dom); 22 | modal_dom = $(dom.attr('data-modal-dom')); 23 | click_dom = $(dom.attr('data-click-dom')); 24 | return click_dom.click(function() { 25 | var current_click_dom; 26 | modal_dom.modal(); 27 | current_click_dom = $(this); 28 | g.current_modal_click_button_value = current_click_dom.attr('data-value'); 29 | g.current_modal_click_button_value2 = current_click_dom.attr('data-value2'); 30 | g.current_modal_click_button_value3 = current_click_dom.attr('data-value3'); 31 | if (typeof after_modal_display !== "undefined" && after_modal_display !== null) { 32 | try { 33 | after_modal_display(current_click_dom, modal_dom); 34 | } catch (_error) {} 35 | } 36 | return false; 37 | }); 38 | }); 39 | }); 40 | 41 | }).call(this); 42 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/modal.scss: -------------------------------------------------------------------------------- 1 | .modal_wrap{ 2 | width: 70%; 3 | } 4 | .modal_container{ 5 | max-height: 550px; 6 | overflow-y: auto; 7 | overflow-x: hidden; 8 | color: #333; 9 | font-size: 14px; 10 | } 11 | 12 | 13 | .modal_wrap{ 14 | ::-webkit-scrollbar { 15 | width:4px; 16 | margin:0 1px; 17 | background: #fdfdfd; 18 | } 19 | ::-webkit-scrollbar:horizontal{ 20 | height: 4px; 21 | } 22 | 23 | ::-webkit-scrollbar-button{ 24 | height: 0; 25 | } 26 | 27 | ::-webkit-scrollbar-track { 28 | border-radius: 2px; 29 | } 30 | ::-webkit-scrollbar-track-piece { 31 | background-color: #fdfdfd; 32 | border-radius: 2px; 33 | } 34 | ::-webkit-scrollbar-thumb { 35 | width:8px; 36 | margin: 0 1px; 37 | border-radius: 2px; 38 | background: #c4c4c4; 39 | } 40 | ::-webkit-scrollbar-thumb:hover { 41 | background: #909090; 42 | } 43 | } 44 | 45 | 46 | .modal{ 47 | font-size: 14px; 48 | 49 | 50 | 51 | form{ 52 | color: #555; 53 | h2{ 54 | color: #111; 55 | margin-top: 1em !important; 56 | } 57 | 58 | .fields .field { 59 | margin: 0.8em auto 1.2em auto !important; 60 | } 61 | } 62 | } 63 | 64 | 65 | @media screen and (min-width: 48em){ 66 | .modal{ 67 | .pure-u-md-1-3, .pure-u-md-8-24 { 68 | width: 50%; 69 | } 70 | .pure-u-lg-1-3, .pure-u-lg-8-24{ 71 | width: 50%; 72 | } 73 | } 74 | } 75 | 76 | @media screen and (max-width: 48em){ 77 | .modal{ 78 | width: auto !important; 79 | } 80 | form .field { 81 | margin: 0.5em auto !important; 82 | padding-right: 0 !important; 83 | } 84 | 85 | } 86 | 87 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/page/load_pages.coffee: -------------------------------------------------------------------------------- 1 | on_page_loading = false 2 | finish_page_loading = false 3 | next_page_number = 2 4 | 5 | load_next_page = (container_id, loading_id, callback)=> 6 | container_dom_id = '#' + container_id 7 | loading_dom_id = '#' + loading_id 8 | on_page_loading = true 9 | $(loading_dom_id).css('display', 'block') 10 | url = location.pathname+'/page/'+next_page_number+location.search 11 | url = url.replace(/\/{2,}/g, '/') 12 | 13 | if auto_load_page_callback? and not callback 14 | callback = auto_load_page_callback 15 | 16 | $.get url, {pjax: true}, (data)-> 17 | $(loading_dom_id).css('display', 'none') 18 | if data.length < 20 19 | finish_page_loading = true 20 | else 21 | try 22 | $(container_dom_id).append(data) 23 | next_page_number += 1 24 | on_page_loading = false 25 | if callback 26 | callback() 27 | 28 | auto_load_pages = (container_id, loading_id, callback)-> 29 | bottom_diff = $(document).height() - $(window).height() - $(window).scrollTop() 30 | if bottom_diff < 80 and not on_page_loading and not finish_page_loading and location.search.indexOf('?s=') == -1 31 | load_next_page(container_id, loading_id, callback) 32 | 33 | load_more = -> 34 | if auto_load_page_container_id? 35 | dom_id = auto_load_page_container_id or 'list_container' 36 | else 37 | dom_id = 'list_container' 38 | auto_load_pages(dom_id, 'on_loading') 39 | 40 | $(document).ready -> 41 | $(window).scroll(load_more) 42 | load_more() -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/slider/slider.coffee: -------------------------------------------------------------------------------- 1 | @install_sliders = -> 2 | $('.bitcron_slider').each -> 3 | slider_dom = $(this) 4 | autoplay = if slider_dom.attr('data-autoplay')=='true' then true else false 5 | show_arrows = if slider_dom.attr('data-show-arrows')=='true' then true else false 6 | animation = slider_dom.attr('data-animation') or 'horizontal' 7 | unslider_configs = {autoplay:autoplay, arrows:show_arrows, animation:animation} 8 | slider_dom.unslider(unslider_configs) 9 | 10 | $(document).ready -> 11 | install_sliders() -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/slider/slider.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | this.install_sliders = function() { 4 | return $('.bitcron_slider').each(function() { 5 | var animation, autoplay, show_arrows, slider_dom, unslider_configs; 6 | slider_dom = $(this); 7 | autoplay = slider_dom.attr('data-autoplay') === 'true' ? true : false; 8 | show_arrows = slider_dom.attr('data-show-arrows') === 'true' ? true : false; 9 | animation = slider_dom.attr('data-animation') || 'horizontal'; 10 | unslider_configs = { 11 | autoplay: autoplay, 12 | arrows: show_arrows, 13 | animation: animation 14 | }; 15 | return slider_dom.unslider(unslider_configs); 16 | }); 17 | }; 18 | 19 | $(document).ready(function() { 20 | return install_sliders(); 21 | }); 22 | 23 | }).call(this); 24 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/tab.css: -------------------------------------------------------------------------------- 1 | .tabs_set{margin-top: 50px;} 2 | .tabs_set a { 3 | text-decoration: none; } 4 | .tabs_set .tab_item { 5 | display: none; } 6 | .tabs_set .tab_item:first-child { 7 | display: block; } 8 | .tabs_set ul.horizontal { 9 | list-style: none; 10 | text-align: center; 11 | margin-top: 0; 12 | padding: 0 0 0 0; 13 | border-bottom: 1px solid #eee; 14 | line-height: 25px; } 15 | .tabs_set ul.horizontal li { 16 | display: inline; 17 | margin: 0 10px; } 18 | .tabs_set ul.horizontal li a { 19 | font-size: 14px; 20 | padding: 5px 10px; 21 | color: #666; 22 | bottom: 1px; 23 | position: relative; 24 | border: 1px solid #eee; 25 | } 26 | .tabs_set ul.horizontal li.active a, .tabs_set ul.horizontal li a:hover { 27 | color: #4296DB; border-bottom: 2px solid white;} 28 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/api/syntax/tab.scss: -------------------------------------------------------------------------------- 1 | $high_color: #e7645c; 2 | $high_color2: #4296DB; 3 | 4 | 5 | .tabs_set{ 6 | margin-top: 50px; 7 | a{ 8 | text-decoration: none; 9 | } 10 | 11 | .tab_item{ 12 | display: none; 13 | } 14 | 15 | .tab_item:first-child{ 16 | display: block; 17 | } 18 | 19 | ul.horizontal{ 20 | list-style: none; 21 | text-align: center; 22 | margin-top: 0; 23 | padding: 0 0 0 0; 24 | border-bottom: 1px solid #eee; 25 | line-height: 25px; 26 | 27 | li{ 28 | display: inline; 29 | margin: 0 10px; 30 | 31 | a{ 32 | font-size: 14px; 33 | padding: 5px 10px; 34 | color: #666; 35 | bottom: 1px; 36 | position: relative; 37 | border: 1px solid #eee; 38 | } 39 | 40 | &.active a, a:hover{ 41 | color: $high_color2; 42 | border-bottom: 2px solid white; 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/basic/footer.css: -------------------------------------------------------------------------------- 1 | .site_footer_wrap .site_footer { 2 | clear: both; 3 | margin: 50px 0 15px 0; 4 | padding-top: 15px; 5 | border-top: 1px solid #eee; 6 | text-align: center; 7 | color: #999; 8 | font-size: 12px; 9 | font-family: "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", 10 | SimSun, sans-serif; } 11 | .site_footer_wrap .site_footer a { 12 | font-size: 12px; 13 | margin: 0 12px; 14 | color: #999; } 15 | .site_footer_wrap .site_footer a:hover { 16 | color: #333; 17 | border-bottom: 1px dashed #ccc; } 18 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/basic/footer.scss: -------------------------------------------------------------------------------- 1 | .site_footer_wrap{ 2 | .site_footer{ 3 | clear: both; 4 | margin: 50px 0 15px 0; 5 | padding-top: 15px; 6 | border-top: 1px solid #eee; 7 | text-align: center; 8 | 9 | color: #999; 10 | font-size: 12px; 11 | font-family: "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif; 12 | 13 | a{ 14 | font-size: 12px; 15 | margin: 0 12px; 16 | color: #999; 17 | &:hover{ 18 | color: #333; 19 | border-bottom: 1px dashed #ccc; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/basic/post_preview.css: -------------------------------------------------------------------------------- 1 | .post_preview { 2 | position: relative; 3 | display: flex; 4 | background: #fff; 5 | border-radius: 2px; 6 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 7 | margin-bottom: 15px; 8 | } 9 | .post_preview .post_preview__meta { 10 | width: 100%; 11 | padding: 25px; 12 | } 13 | .post_preview .post_preview__meta .post-preview__middle { 14 | position: relative; 15 | top: 50%; 16 | transform: translateY(-50%); 17 | } 18 | .post_preview .post_preview__meta .post_preview__title { 19 | font-size: 16px; 20 | margin: 0 0 10px 0; 21 | } 22 | .post_preview .post_preview__meta .post_preview__title a { 23 | text-decoration: none; 24 | } 25 | .post_preview .post_preview__meta .post_preview__date { 26 | font-size: 14px; 27 | color: #999; 28 | margin: 0 0 8px 0; 29 | display: block; 30 | } 31 | .post_preview .post_preview__meta.with_bg { 32 | width: 75%; 33 | } 34 | .post_preview .post_preview__summary { 35 | font-size: 14px; 36 | line-height: 1.825; 37 | } 38 | .post_preview .post_preview__summary p { 39 | margin-bottom: 0; 40 | } 41 | .post_preview .post_preview__image { 42 | width: 25%; 43 | float: right; 44 | background-size: cover; 45 | background-position: center center; 46 | border-top-right-radius: 2px; 47 | border-bottom-right-radius: 2px; 48 | } 49 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/basic/post_preview.scss: -------------------------------------------------------------------------------- 1 | .post_preview { 2 | position: relative; 3 | display: flex; 4 | background: #fff; 5 | border-radius: 2px; 6 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 7 | margin-bottom: 15px; 8 | 9 | .post_preview__meta { 10 | width: 100%; 11 | padding: 25px; 12 | 13 | .post-preview__middle { 14 | position: relative; 15 | top: 50%; 16 | transform: translateY(-50%); 17 | } 18 | 19 | .post_preview__title { 20 | font-size: 16px; 21 | margin: 0 0 10px 0; 22 | a { 23 | text-decoration: none; 24 | } 25 | } 26 | 27 | .post_preview__date { 28 | font-size: 14px; 29 | color: #999; 30 | margin: 0 0 8px 0; 31 | display: block; 32 | } 33 | 34 | &.with_bg{ 35 | width: 75%; 36 | } 37 | } 38 | 39 | .post_preview__summary { 40 | font-size: 14px; 41 | line-height: 1.825; 42 | p{ 43 | margin-bottom: 0; 44 | } 45 | } 46 | 47 | .post_preview__image { 48 | width: 25%; 49 | float: right; 50 | background-size: cover; 51 | background-position: center center; 52 | border-top-right-radius: 2px; 53 | border-bottom-right-radius: 2px; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/builtin_themes/waterfall/script.coffee: -------------------------------------------------------------------------------- 1 | 2 | 3 | waterfall = new Waterfall({ 4 | containerSelector: '#waterfall', 5 | boxSelector: '.item', 6 | minBoxWidth: 250 7 | }) 8 | 9 | @auto_load_page_callback = -> 10 | new_items = $('#waterfall > .item') 11 | for item in new_items 12 | waterfall.addBox(item) -------------------------------------------------------------------------------- /farbox_bucket/server/static/builtin_themes/waterfall/script.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | var waterfall; 4 | 5 | waterfall = new Waterfall({ 6 | containerSelector: '#waterfall', 7 | boxSelector: '.item', 8 | minBoxWidth: 250 9 | }); 10 | 11 | this.auto_load_page_callback = function() { 12 | var item, new_items, _i, _len, _results; 13 | new_items = $('#waterfall > .item'); 14 | _results = []; 15 | for (_i = 0, _len = new_items.length; _i < _len; _i++) { 16 | item = new_items[_i]; 17 | _results.push(waterfall.addBox(item)); 18 | } 19 | return _results; 20 | }; 21 | 22 | }).call(this); 23 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_asc.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_asc_disabled.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_both.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_desc.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/datatables/DataTables-1.10.20/images/sort_desc_disabled.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/defaults/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/defaults/admin.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/defaults/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/defaults/avatar.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/defaults/drop_image_here.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/defaults/drop_image_here.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/defaults/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/defaults/favicon.ico -------------------------------------------------------------------------------- /farbox_bucket/server/static/defaults/site_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/defaults/site_avatar.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/defaults/visitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/defaults/visitor.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/donate/alipay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/donate/alipay.jpg -------------------------------------------------------------------------------- /farbox_bucket/server/static/donate/wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/donate/wechat.jpg -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/Josefin+Sans_400_normal.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/Josefin+Sans_400_normal.ttf -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/Josefin+Sans_400_normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/Josefin+Sans_400_normal.woff -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/Julius+Sans+One_400_normal.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/Julius+Sans+One_400_normal.ttf -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/Julius+Sans+One_400_normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/Julius+Sans+One_400_normal.woff -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/Lato_400_normal.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/Lato_400_normal.ttf -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/Lato_400_normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/Lato_400_normal.woff -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.eot -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.ttf -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.woff -------------------------------------------------------------------------------- /farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/gfonts/calligraffitti-regular-webfont.woff2 -------------------------------------------------------------------------------- /farbox_bucket/server/static/images/border1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/images/border1.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/images/border2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/images/border2.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/images/loading.gif -------------------------------------------------------------------------------- /farbox_bucket/server/static/images/readme.txt: -------------------------------------------------------------------------------- 1 | for color box mainly -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/essage/essage.css: -------------------------------------------------------------------------------- 1 | /* Essage - a more elegant way to show message 2 | * https://github.com/sofish/Essage 3 | */ 4 | .essage{font-size:16px;position:fixed;_position:absolute;z-index:100010;background:#fff;background:rgba(255,255,255,0.9);color:#666;text-align:center;line-height:1.8;padding:10px 20px;*padding:10px 0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;top:9999em;left:0;width:100%;border-bottom:1px solid #ddd;border-top:1px solid #ddd;} 5 | .essage-success{background:#eafbe3;background:rgba(221, 242, 210, 0.9);border-bottom-color:#bfd5a9;border-top-color:#bfd5a9;color:#3fb16f;} 6 | .essage-error{background:#f2e1e4;background:rgba(242, 225, 228, 0.9);border-bottom-color:#e0cdcd;border-top-color:#e0cdcd;color:#dc584b;} 7 | .essage-warning{background:#fcf8e3;background:rgba(252, 248, 227, 0.9);border-bottom-color:#fbeed5;border-top-color:#fbeed5;color:#c09853;} 8 | .essage .close{font:400 normal 22px/1.3 arial, sans-serif;float:right;*margin-right:15px;display:inline;color:#e74c3c;opacity:0.6;cursor:pointer;} 9 | .essage .close:hover{opacity:1;} -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fontawesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fontawesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fontawesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fonts/merriweather.css: -------------------------------------------------------------------------------- 1 | /* cyrillic-ext */ 2 | @font-face { 3 | font-family: 'Merriweather'; 4 | font-style: normal; 5 | font-weight: 300; 6 | src: local('Merriweather Light'), local('Merriweather-Light'), url(merriweather/1.woff2) format('woff2'); 7 | unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F; 8 | } 9 | /* cyrillic */ 10 | @font-face { 11 | font-family: 'Merriweather'; 12 | font-style: normal; 13 | font-weight: 300; 14 | src: local('Merriweather Light'), local('Merriweather-Light'), url(merriweather/2.woff2) format('woff2'); 15 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 16 | } 17 | /* latin-ext */ 18 | @font-face { 19 | font-family: 'Merriweather'; 20 | font-style: normal; 21 | font-weight: 300; 22 | src: local('Merriweather Light'), local('Merriweather-Light'), url(merriweather/3.woff2) format('woff2'); 23 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 24 | } 25 | /* latin */ 26 | @font-face { 27 | font-family: 'Merriweather'; 28 | font-style: normal; 29 | font-weight: 300; 30 | src: local('Merriweather Light'), local('Merriweather-Light'), url(merriweather/4.woff2) format('woff2'); 31 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 32 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fonts/merriweather/1.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fonts/merriweather/1.woff2 -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fonts/merriweather/2.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fonts/merriweather/2.woff2 -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fonts/merriweather/3.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fonts/merriweather/3.woff2 -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/fonts/merriweather/4.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/fonts/merriweather/4.woff2 -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jquery-linedtextarea.css: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery Lined Textarea Plugin 3 | * http://alan.blog-city.com/jquerylinedtextarea.htm 4 | * 5 | * Copyright (c) 2010 Alan Williamson 6 | * 7 | * Released under the MIT License: 8 | * http://www.opensource.org/licenses/mit-license.php 9 | * 10 | * Usage: 11 | * Displays a line number count column to the left of the textarea 12 | * 13 | * Class up your textarea with a given class, or target it directly 14 | * with JQuery Selectors 15 | * 16 | * $(".lined").linedtextarea({ 17 | * selectedLine: 10, 18 | * selectedClass: 'lineselect' 19 | * }); 20 | * 21 | */ 22 | 23 | .linedwrap { 24 | border: 1px solid #c0c0c0; 25 | padding: 3px; 26 | } 27 | 28 | .linedtextarea { 29 | padding: 0px; 30 | margin: 0px; 31 | } 32 | 33 | .linedtextarea textarea, .linedwrap .codelines .lineno { 34 | font-size: 10pt; 35 | font-family: monospace; 36 | line-height: normal !important; 37 | } 38 | 39 | .linedtextarea textarea { 40 | padding-right:0.3em; 41 | padding-top:0.3em; 42 | border: 0; 43 | } 44 | 45 | .linedwrap .lines { 46 | margin-top: 0px; 47 | width: 50px; 48 | float: left; 49 | overflow: hidden; 50 | border-right: 1px solid #c0c0c0; 51 | margin-right: 10px; 52 | } 53 | 54 | .linedwrap .codelines { 55 | padding-top: 5px; 56 | } 57 | 58 | .linedwrap .codelines .lineno { 59 | color:#AAAAAA; 60 | padding-right: 0.5em; 61 | padding-top: 0.0em; 62 | text-align: right; 63 | white-space: nowrap; 64 | } 65 | 66 | .linedwrap .codelines .lineselect { 67 | color: red; 68 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jstree/themes/default-dark/32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/jstree/themes/default-dark/32px.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jstree/themes/default-dark/40px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/jstree/themes/default-dark/40px.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jstree/themes/default-dark/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/jstree/themes/default-dark/throbber.gif -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jstree/themes/default/32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/jstree/themes/default/32px.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jstree/themes/default/40px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/jstree/themes/default/40px.png -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/jstree/themes/default/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/jstree/themes/default/throbber.gif -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/markdown_js/mindmap/d3_LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010-2017 Mike Bostock 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/markdown_js/mindmap/markmap_LICENSE.txt: -------------------------------------------------------------------------------- 1 | https://github.com/dundalek/markmap 2 | 3 | Copyright 2015 Jakub Dundalek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/markdown_js/mindmap/readme.txt: -------------------------------------------------------------------------------- 1 | https://github.com/dundalek/markmap -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/markdown_js/mindmap/view.mindmap.css: -------------------------------------------------------------------------------- 1 | html { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | position: absolute; 9 | top: 0; 10 | bottom: 0; 11 | right: 0; 12 | left: 0; 13 | } 14 | 15 | .mindmap_container{ 16 | margin: 0 auto; 17 | } 18 | 19 | svg#mindmap { 20 | width: 100%; 21 | height: 100%; 22 | 23 | display: block; 24 | margin: 0 auto; 25 | padding: 20px; 26 | } 27 | .markmap-node { 28 | cursor: pointer; 29 | } 30 | 31 | .markmap-node-circle { 32 | fill: #fff; 33 | stroke-width: 1.5px; 34 | } 35 | 36 | .markmap-node-text { 37 | fill: #333; 38 | font: 16px sans-serif; 39 | } 40 | 41 | .markmap-link { 42 | fill: none; 43 | } 44 | 45 | 46 | 47 | 48 | .d3-tip { 49 | line-height: 1.35; 50 | font-weight: bold; 51 | padding: 12px; 52 | background: rgba(247, 245, 233, 0.9); 53 | color: #eee; 54 | min-width: 300px; 55 | max-width: 600px; 56 | border-radius: 8px; 57 | border: 1px solid #dfdfdf; 58 | } 59 | 60 | .d3_tip_header{ 61 | font-weight: bold; 62 | color: indianred; 63 | font-size: 14px; 64 | } 65 | 66 | /* Creates a small triangle extender for the tooltip */ 67 | .d3-tip:after { 68 | box-sizing: border-box; 69 | display: inline; 70 | font-size: 10px; 71 | width: 100%; 72 | line-height: 10px; 73 | color: rgba(0, 0, 0, 0.8); 74 | content: "\25BC"; 75 | position: absolute; 76 | text-align: center; 77 | } 78 | 79 | /* Style northward tooltips differently */ 80 | .d3-tip.n:after { 81 | margin: -1px 0 0 0; 82 | top: 100%; 83 | left: 0; 84 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/menu/smartmenu/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Vasil Dinkov, Vadikom Web Ltd. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/menu/smartmenu/css/sm-core-css.css: -------------------------------------------------------------------------------- 1 | /* Mobile first layout SmartMenus Core CSS (it's not recommended editing these rules) 2 | You need this once per page no matter how many menu trees or different themes you use. 3 | -------------------------------------------------------------------------------------------*/ 4 | 5 | .sm{position:relative;z-index:9999;} 6 | .sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0);} 7 | .sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right;} 8 | .sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0;} 9 | .sm ul{display:none;} 10 | .sm li,.sm a{position:relative;} 11 | .sm a{display:block;} 12 | .sm a.disabled{cursor:not-allowed;} 13 | .sm:after{content:"\00a0";display:block;height:0;font:0px/0 serif;clear:both;visibility:hidden;overflow:hidden;} 14 | .sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;} -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/pure_patch.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 35.5em) { 2 | .pure-u-0-24 { 3 | display: none; } 4 | } 5 | 6 | @media screen and (max-width: 48em) { 7 | .pure-u-sm-0-24 { 8 | display: none; } 9 | } 10 | 11 | @media screen and (max-width: 64em) { 12 | .pure-u-md-0-24 { 13 | display: none; } 14 | } 15 | 16 | @media screen and (max-width: 80em) { 17 | .pure-u-lg-0-24 { 18 | display: none; } 19 | } 20 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/pure_patch.scss: -------------------------------------------------------------------------------- 1 | // sm 2 | @media screen and (max-width: 35.5em){ 3 | .pure-u-0-24{ 4 | display: none; 5 | } 6 | } 7 | 8 | // md 9 | @media screen and (max-width: 48em){ 10 | .pure-u-sm-0-24{ 11 | display: none; 12 | } 13 | } 14 | 15 | // lg 16 | @media screen and (max-width: 64em){ 17 | .pure-u-md-0-24{ 18 | display: none; 19 | } 20 | } 21 | 22 | 23 | // xl 24 | @media screen and (max-width: 80em){ 25 | .pure-u-lg-0-24{ 26 | display: none; 27 | } 28 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/unslider/css/unslider-dots.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Here's where everything gets included. You don't need 3 | * to change anything here, and doing so might break 4 | * stuff. Here be dragons and all that. 5 | */ 6 | /** 7 | * Default variables 8 | * 9 | * While these can be set with JavaScript, it's probably 10 | * better and faster to just set them here, compile to 11 | * CSS and include that instead to use some of that 12 | * hardware-accelerated goodness. 13 | */ 14 | .unslider-nav ol { 15 | list-style: none; 16 | text-align: center; 17 | } 18 | .unslider-nav ol li { 19 | display: inline-block; 20 | width: 6px; 21 | height: 6px; 22 | margin: 0 4px; 23 | background: transparent; 24 | border-radius: 5px; 25 | overflow: hidden; 26 | text-indent: -999em; 27 | border: 2px solid #fff; 28 | cursor: pointer; 29 | } 30 | .unslider-nav ol li.unslider-active { 31 | background: #fff; 32 | cursor: default; 33 | } 34 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/unslider/css/unslider.css: -------------------------------------------------------------------------------- 1 | .unslider { 2 | overflow: auto; 3 | margin: 0; 4 | padding: 0 5 | } 6 | 7 | .unslider-wrap { 8 | position: relative 9 | } 10 | 11 | .unslider-wrap.unslider-carousel > li { 12 | float: left 13 | } 14 | 15 | .unslider-vertical > ul { 16 | height: 100% 17 | } 18 | 19 | .unslider-vertical li { 20 | float: none; 21 | width: 100% 22 | } 23 | 24 | .unslider-fade { 25 | position: relative 26 | } 27 | 28 | .unslider-fade .unslider-wrap li { 29 | position: absolute; 30 | left: 0; 31 | top: 0; 32 | right: 0; 33 | z-index: 8 34 | } 35 | 36 | .unslider-fade .unslider-wrap li.unslider-active { 37 | z-index: 10 38 | } 39 | 40 | .unslider li, .unslider ol, .unslider ul { 41 | list-style: none; 42 | margin: 0; 43 | padding: 0; 44 | border: none 45 | } 46 | 47 | .unslider-arrow { 48 | position: absolute; 49 | left: 10px; 50 | z-index: 2; 51 | cursor: pointer 52 | } 53 | 54 | .unslider-arrow.next { 55 | left: auto; 56 | right: 10px 57 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/video-js/font/VideoJS.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/video-js/font/VideoJS.eot -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/video-js/font/VideoJS.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/video-js/font/VideoJS.ttf -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/video-js/font/VideoJS.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/video-js/font/VideoJS.woff -------------------------------------------------------------------------------- /farbox_bucket/server/static/lib/video-js/video-js.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/lib/video-js/video-js.swf -------------------------------------------------------------------------------- /farbox_bucket/server/static/nav/menu/smartmenu/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Vasil Dinkov, Vadikom Web Ltd. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/nav/menu/smartmenu/css/sm-core-css.css: -------------------------------------------------------------------------------- 1 | /* Mobile first layout SmartMenus Core CSS (it's not recommended editing these rules) 2 | You need this once per page no matter how many menu trees or different themes you use. 3 | -------------------------------------------------------------------------------------------*/ 4 | 5 | .sm{position:relative;z-index:9999;} 6 | .sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0);} 7 | .sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right;} 8 | .sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0;} 9 | .sm ul{display:none;} 10 | .sm li,.sm a{position:relative;} 11 | .sm a{display:block;} 12 | .sm a.disabled{cursor:not-allowed;} 13 | .sm:after{content:"\00a0";display:block;height:0;font:0px/0 serif;clear:both;visibility:hidden;overflow:hidden;} 14 | .sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;} -------------------------------------------------------------------------------- /farbox_bucket/server/static/nav/run_menu.coffee: -------------------------------------------------------------------------------- 1 | @run_menu = => 2 | $('.farbox_nav .sm').smartmenus 3 | markCurrentItem: true, 4 | subMenusSubOffsetX: 1, 5 | subMenusSubOffsetY: -8 6 | 7 | $('.farbox_nav').each -> 8 | nav_dom = $(this) 9 | pre_dom = nav_dom.prev() 10 | if not pre_dom.hasClass('menu_toggle') 11 | return false 12 | menu = pre_dom.find('.menu_state') 13 | if menu.length 14 | menu.change (e)-> 15 | if this.checked 16 | nav_dom.hide().slideDown 250, -> 17 | nav_dom.css('display', 'block') 18 | nav_dom.find('.site_nav_wrap').css('padding-right', '40px') 19 | else 20 | nav_dom.show().slideUp 250, -> 21 | nav_dom.css('display', '') 22 | nav_dom.find('.site_nav_wrap').css('padding-right', '0') 23 | 24 | 25 | $(document).ready -> 26 | try run_menu() 27 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/nav/run_menu.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | this.run_menu = (function(_this) { 4 | return function() { 5 | $('.farbox_nav .sm').smartmenus({ 6 | markCurrentItem: true, 7 | subMenusSubOffsetX: 1, 8 | subMenusSubOffsetY: -8 9 | }); 10 | return $('.farbox_nav').each(function() { 11 | var menu, nav_dom, pre_dom; 12 | nav_dom = $(this); 13 | pre_dom = nav_dom.prev(); 14 | if (!pre_dom.hasClass('menu_toggle')) { 15 | return false; 16 | } 17 | menu = pre_dom.find('.menu_state'); 18 | if (menu.length) { 19 | return menu.change(function(e) { 20 | if (this.checked) { 21 | nav_dom.hide().slideDown(250, function() { 22 | return nav_dom.css('display', 'block'); 23 | }); 24 | return nav_dom.find('.site_nav_wrap').css('padding-right', '40px'); 25 | } else { 26 | nav_dom.show().slideUp(250, function() { 27 | return nav_dom.css('display', ''); 28 | }); 29 | return nav_dom.find('.site_nav_wrap').css('padding-right', '0'); 30 | } 31 | }); 32 | } 33 | }); 34 | }; 35 | })(this); 36 | 37 | $(document).ready(function() { 38 | try { 39 | return run_menu(); 40 | } catch (_error) {} 41 | }); 42 | 43 | }).call(this); 44 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/nav/sm-core-css.css: -------------------------------------------------------------------------------- 1 | /* Mobile first layout SmartMenus Core CSS (it's not recommended editing these rules) 2 | You need this once per page no matter how many menu trees or different themes you use. 3 | -------------------------------------------------------------------------------------------*/ 4 | 5 | .sm{position:relative;z-index:9999;} 6 | .sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0);} 7 | .sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right;} 8 | .sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0;} 9 | .sm ul{display:none;} 10 | .sm li,.sm a{position:relative;} 11 | .sm a{display:block;} 12 | .sm a.disabled{cursor:not-allowed;} 13 | .sm:after{content:"\00a0";display:block;height:0;font:0px/0 serif;clear:both;visibility:hidden;overflow:hidden;} 14 | .sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;} -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/code_editor/editor.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 12px; 3 | width: 100%; 4 | overflow: hidden; 5 | background: #fbfbfb; } 6 | 7 | #editor .CodeMirror { 8 | position: absolute; 9 | right: 0; 10 | top: 0; 11 | z-index: 9; 12 | padding: 0; 13 | height: 100%; 14 | width: 100%; 15 | font-family: Verdana, Arial, sans-serif; 16 | font-size: 14px; 17 | line-height: 2.1em; 18 | border: none; } 19 | 20 | #save_button { 21 | position: fixed; 22 | top: 6px; 23 | right: 2px; 24 | z-index: 10000; } 25 | #save_button a { 26 | text-decoration: none; 27 | padding: 5px 20px; 28 | background: #b4b4b4; 29 | border-radius: 2px; 30 | color: #fff; } 31 | #save_button a:hover { 32 | background: #d42128; } 33 | #save_button .precess { 34 | position: absolute; 35 | left: 0; 36 | bottom: -5px; 37 | background: #d42128; 38 | height: 2px; } 39 | 40 | .cm-s-solarized .CodeMirror-gutters { 41 | border-right: 1px solid #ddd !important; 42 | background: #fdfdfd !important; } 43 | 44 | .cm-s-solarized .cm-strong { 45 | color: indianred !important; } 46 | 47 | .CodeMirror-linenumber { 48 | text-align: center !important; } 49 | 50 | .CodeMirror-cursor { 51 | border-left: 2px solid #16b0ff !important; 52 | z-index: 3; } 53 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/code_editor/editor.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 12px; 3 | width: 100%; 4 | overflow: hidden; 5 | background: #fbfbfb; 6 | } 7 | 8 | 9 | #editor { 10 | 11 | .CodeMirror{ 12 | position: absolute; 13 | right: 0; 14 | top: 0; 15 | z-index: 9; 16 | padding: 0; 17 | height: 100%; 18 | width: 100%; 19 | font-family: Verdana, Arial, sans-serif; 20 | font-size: 14px; 21 | line-height: 2.1em; 22 | border: none; 23 | 24 | 25 | } 26 | } 27 | 28 | 29 | #save_button{ 30 | position: fixed; 31 | top: 6px; 32 | right: 2px; 33 | z-index: 10000; 34 | 35 | a{ 36 | text-decoration: none; 37 | padding: 5px 20px; 38 | background: #b4b4b4; 39 | border-radius: 2px; 40 | color: #fff; 41 | 42 | &:hover{ 43 | background: #d42128; 44 | } 45 | } 46 | 47 | .precess{ 48 | position: absolute; 49 | left: 0; 50 | bottom: -5px; 51 | background: #d42128; 52 | height: 2px; 53 | 54 | } 55 | } 56 | 57 | 58 | 59 | 60 | .cm-s-solarized .CodeMirror-gutters{ 61 | border-right: 1px solid #ddd !important; 62 | background: #fdfdfd !important; 63 | } 64 | 65 | .cm-s-solarized .cm-strong { 66 | color: indianred !important; 67 | } 68 | 69 | .CodeMirror-linenumber{ 70 | text-align: center !important; 71 | } 72 | 73 | /* CURSOR */ 74 | 75 | .CodeMirror-cursor { 76 | border-left: 2px solid #16b0ff !important; 77 | z-index: 3; 78 | } 79 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/default_homepage/markdown@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/pages/default_homepage/markdown@2x.jpg -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/site/menu/run.coffee: -------------------------------------------------------------------------------- 1 | @run_menu = => 2 | $('.bitcron_nav .sm').smartmenus 3 | markCurrentItem: true, 4 | subMenusSubOffsetX: 1, 5 | subMenusSubOffsetY: -8 6 | 7 | $('.bitcron_nav').each -> 8 | nav_dom = $(this) 9 | pre_dom = nav_dom.prev() 10 | if not pre_dom.hasClass('menu_toggle') 11 | return false 12 | menu = pre_dom.find('.menu_state') 13 | if menu.length 14 | menu.change (e)-> 15 | if this.checked 16 | nav_dom.hide().slideDown 250, -> 17 | nav_dom.css('display', 'block') 18 | nav_dom.find('.site_nav_wrap').css('padding-right', '40px') 19 | else 20 | nav_dom.show().slideUp 250, -> 21 | nav_dom.css('display', '') 22 | nav_dom.find('.site_nav_wrap').css('padding-right', '0') 23 | 24 | 25 | $(document).ready -> 26 | try run_menu() 27 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/site/menu/run.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | (function() { 3 | this.run_menu = (function(_this) { 4 | return function() { 5 | $('.bitcron_nav .sm').smartmenus({ 6 | markCurrentItem: true, 7 | subMenusSubOffsetX: 1, 8 | subMenusSubOffsetY: -8 9 | }); 10 | return $('.bitcron_nav').each(function() { 11 | var menu, nav_dom, pre_dom; 12 | nav_dom = $(this); 13 | pre_dom = nav_dom.prev(); 14 | if (!pre_dom.hasClass('menu_toggle')) { 15 | return false; 16 | } 17 | menu = pre_dom.find('.menu_state'); 18 | if (menu.length) { 19 | return menu.change(function(e) { 20 | if (this.checked) { 21 | nav_dom.hide().slideDown(250, function() { 22 | return nav_dom.css('display', 'block'); 23 | }); 24 | return nav_dom.find('.site_nav_wrap').css('padding-right', '40px'); 25 | } else { 26 | nav_dom.show().slideUp(250, function() { 27 | return nav_dom.css('display', ''); 28 | }); 29 | return nav_dom.find('.site_nav_wrap').css('padding-right', '0'); 30 | } 31 | }); 32 | } 33 | }); 34 | }; 35 | })(this); 36 | 37 | $(document).ready(function() { 38 | try { 39 | return run_menu(); 40 | } catch (_error) {} 41 | }); 42 | 43 | }).call(this); 44 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/table.css: -------------------------------------------------------------------------------- 1 | table { 2 | -moz-box-sizing: border-box; 3 | box-sizing: border-box; 4 | margin: 1em 0; 5 | width: 100%; 6 | max-width: 100%; 7 | border-width: 1px; 8 | border-style: solid; 9 | background-color: transparent; } 10 | table img { 11 | max-width: 100%; } 12 | 13 | table, table tr, table tr td, table tr th { 14 | border-color: #e5e5e5; } 15 | 16 | table th { 17 | color: #666666; 18 | background-color: #fdfdfd; } 19 | 20 | tr th { 21 | border-bottom-width: 1px; 22 | border-bottom-style: solid; 23 | text-align: left; } 24 | 25 | tr th, tr td { 26 | padding: 5px 20px; 27 | border-right: 1px solid; 28 | font-size: 1rem; } 29 | 30 | tr th:last-child, tr td:last-child { 31 | border-right: 0px; } 32 | 33 | table th { 34 | font-weight: bold; 35 | font-size: 85%; 36 | color: #000; } 37 | 38 | table tbody > tr:nth-child(odd) > td, table tbody > tr:nth-child(odd) > th { 39 | background-color: #f9f9f9; } -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/table.scss: -------------------------------------------------------------------------------- 1 | 2 | table { 3 | -moz-box-sizing: border-box; 4 | box-sizing: border-box; 5 | margin: 1em 0; 6 | width: 100%; 7 | max-width: 100%; 8 | border-width: 1px; border-style: solid; 9 | background-color: transparent; 10 | 11 | img{ 12 | max-width: 100%; 13 | } 14 | } 15 | 16 | table, table tr, table tr td, table tr th { 17 | border-color: #e5e5e5; 18 | } 19 | 20 | table th { 21 | color: #666666; 22 | background-color: #fdfdfd; 23 | } 24 | 25 | tr th { 26 | border-bottom-width: 1px; 27 | border-bottom-style: solid; 28 | text-align: left; 29 | } 30 | 31 | tr th, tr td { 32 | padding: 5px 20px; 33 | border-right: 1px solid; 34 | font-size: 1rem; 35 | } 36 | 37 | tr th:last-child, tr td:last-child { 38 | border-right: 0px; 39 | } 40 | 41 | table th { 42 | font-weight: bold; 43 | font-size: 85%; 44 | color: #000; 45 | } 46 | 47 | table tbody > tr:nth-child(odd) > td, 48 | table tbody > tr:nth-child(odd) > th { 49 | background-color: #f9f9f9; 50 | } -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/text_editor/text_editor.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 14px; 3 | font-family: 'Georgia', "Hiragino Sans GB", serif; 4 | width: 100%; 5 | overflow: hidden; } 6 | 7 | #main { 8 | height: 100%; } 9 | 10 | #save_button { 11 | position: fixed; 12 | top: 6px; 13 | right: 2px; 14 | z-index: 10000; } 15 | #save_button a { 16 | text-decoration: none; 17 | padding: 5px 20px; 18 | background: #b4b4b4; 19 | border-radius: 2px; 20 | color: #fff; } 21 | #save_button a:hover { 22 | background: #d42128; } 23 | #save_button .precess { 24 | position: absolute; 25 | left: 0; 26 | bottom: -5px; 27 | background: #d42128; 28 | height: 2px; } 29 | 30 | #editor textarea { 31 | background: #fbfbfb; 32 | position: absolute; 33 | right: 0; 34 | top: 0; 35 | padding-top: 10px; 36 | padding-left: 15px; 37 | padding-right: 15px; 38 | z-index: 9; 39 | height: 100%; 40 | width: 100%; 41 | font-family: "Hiragino Sans GB", "Microsoft Yahei", Verdana, Arial, sans-serif; 42 | font-size: 13pt; 43 | line-height: 1.8em; 44 | resize: none; 45 | outline: none; 46 | display: block; 47 | border: none; 48 | white-space: pre-wrap; 49 | word-wrap: break-word; 50 | box-sizing: border-box; } 51 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/pages/text_editor/text_editor.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 14px; 3 | font-family: 'Georgia', "Hiragino Sans GB", serif; 4 | width: 100%; 5 | overflow: hidden; 6 | } 7 | 8 | #main { 9 | height: 100%; 10 | } 11 | 12 | 13 | #save_button{ 14 | position: fixed; 15 | top: 6px; 16 | right: 2px; 17 | z-index: 10000; 18 | 19 | a{ 20 | text-decoration: none; 21 | padding: 5px 20px; 22 | background: #b4b4b4; 23 | border-radius: 2px; 24 | color: #fff; 25 | 26 | &:hover{ 27 | background: #d42128; 28 | } 29 | } 30 | 31 | .precess{ 32 | position: absolute; 33 | left: 0; 34 | bottom: -5px; 35 | background: #d42128; 36 | height: 2px; 37 | 38 | } 39 | } 40 | 41 | 42 | 43 | #editor { 44 | textarea { 45 | background: #fbfbfb; 46 | position: absolute; 47 | right: 0; 48 | top: 0; 49 | padding-top: 10px; 50 | padding-left: 15px; 51 | padding-right: 15px; 52 | 53 | z-index: 9; 54 | height: 100%; 55 | width: 100%; 56 | font-family: "Hiragino Sans GB", "Microsoft Yahei", Verdana, Arial, sans-serif; 57 | font-size: 13pt; 58 | line-height: 1.8em; 59 | 60 | resize: none; 61 | outline: none; 62 | display: block; 63 | 64 | border: none; 65 | 66 | white-space: pre-wrap; 67 | word-wrap: break-word; 68 | box-sizing: border-box; 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /farbox_bucket/server/static/unsplash/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/unsplash/2.jpg -------------------------------------------------------------------------------- /farbox_bucket/server/static/unsplash/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/unsplash/5.jpg -------------------------------------------------------------------------------- /farbox_bucket/server/static/unsplash/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/static/unsplash/6.jpg -------------------------------------------------------------------------------- /farbox_bucket/server/statistics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/statistics/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/statistics/after_request.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from gevent import spawn 3 | from farbox_bucket.settings import sentry_client 4 | from .post_visits import update_post_visits_for_response 5 | from .usage_collect import update_usage_statistics 6 | 7 | def after_request_func_for_statistics(response): 8 | try: 9 | update_post_visits_for_response(response) # 因为调用了 request/g,不能放在 spawn 中处理 10 | except: 11 | if sentry_client: sentry_client.captureException() 12 | 13 | update_usage_statistics(response) 14 | 15 | return response 16 | 17 | -------------------------------------------------------------------------------- /farbox_bucket/server/statistics/usage_collect.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from flask import Response 3 | from gevent import spawn 4 | from farbox_bucket.bucket.utils import get_bucket_in_request_context 5 | from farbox_bucket.bucket.usage.bucket_usage_utils import increase_request_for_bucket, increase_bandwidth_for_bucket 6 | 7 | 8 | def update_usage_statistics(response): 9 | bucket = get_bucket_in_request_context() 10 | if not bucket: 11 | return 12 | 13 | if not isinstance(response, Response): 14 | return 15 | # requests + 1 16 | spawn(increase_request_for_bucket, bucket) 17 | 18 | if response.status_code not in [301, 302, ]: 19 | bandwidth = response.content_length 20 | if bandwidth: 21 | spawn(increase_bandwidth_for_bucket, bucket, bandwidth) 22 | 23 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/after_request/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/before_and_after_request/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/before_and_after_request/time_cost.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | from flask import request 4 | import time 5 | 6 | def time_cost_handler(response=None): 7 | if not response: # before request 8 | request.environ['request_at'] = time.time() 9 | else: # after request 10 | request_at = request.environ.get('request_at') 11 | if request_at: 12 | time_cost = time.time() - request_at 13 | response.headers['x-render-time'] = str(time_cost) # 渲染消耗的时间,单位秒 14 | return response 15 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/before_request/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/before_request/basic.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from flask import request 3 | 4 | def basic_before_request(): 5 | if request.environ.get('HTTP_X_PROTOCOL') == 'https': # ssl 6 | request.url = request.url.replace('http://', 'https://', 1) 7 | request.url_root = request.url_root.replace('http://', 'https://', 1) -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/app_functions/utils.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | 4 | def apply_middleware(app, middleware): 5 | # 处理中间件 & before_request & after_request 6 | app.wsgi_app = middleware(app.wsgi_app) 7 | before_request = getattr(middleware, 'before_request',None) 8 | after_request = getattr(middleware, 'after_request',None) 9 | if before_request: 10 | app.before_request_funcs.setdefault(None,[]).append(before_request) 11 | if after_request: 12 | app.after_request_funcs.setdefault(None, []).append(after_request) 13 | 14 | 15 | 16 | def apply_before_request(app, func): 17 | app.before_request_funcs.setdefault(None,[]).append(func) 18 | 19 | def apply_after_request(app, func): 20 | app.after_request_funcs.setdefault(None,[]).append(func) -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/attr_patch/fake_fields.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | 4 | 5 | def get_value_for_fake_field_for_doc(obj, attr): 6 | 7 | return None # by default -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | 4 | class TemplateDebugException(Exception): 5 | pass -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/helper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/template_system/helper/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/template_system/model/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/namespace/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | from farbox_bucket.server.utils.cache_for_function import cache_result 4 | from .data import data, paginator, get_data 5 | from .request import request 6 | from .html import html, Html 7 | from .post import posts, post 8 | from .site import site 9 | from .response import response 10 | from .bucket import bucket 11 | 12 | namespace_functions = { 13 | 'd': data, 14 | 'data': data, 15 | 'request': request, 16 | 'html': html, 17 | 'h': html, 18 | 'paginator': paginator, 19 | 'post': post, 20 | 'posts': posts, 21 | 'p': posts, 22 | 'site': site, 23 | 'response': response, 24 | 'bucket': bucket, 25 | 'b': bucket, 26 | } 27 | 28 | 29 | 30 | # shortcuts 31 | 32 | 33 | @cache_result 34 | def i18n(key, *args): # i18n 专用函数名 35 | return Html.i18n(key, *args) 36 | 37 | 38 | namespace_shortcuts = { 39 | "_": i18n, 40 | "i18n": i18n, 41 | "get_data": get_data, 42 | "load": Html.load 43 | 44 | } 45 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/namespace/built_in.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | import re 4 | 5 | # 必须要有的一个系统公共函数 6 | 7 | def set_property(parent, property_name, value): 8 | # 这个函数很重要,是在 jade 模板中被调用的,不能孔雀 9 | # 这里要隔绝 parent,不能传递特别的东西,不然会有安全隐患 10 | if hasattr(value, 'core'): # 可能某些 model 处理过的,比如Text 11 | value = value.core 12 | if parent is not None and isinstance(property_name, (str, unicode)) and re.match('[a-z_]\w*$', property_name, re.I): 13 | if isinstance(parent, dict): # 字典的处理 14 | parent[property_name] = value 15 | if getattr(parent, 'set_property_allowed', None) and not hasattr(value, '__call__'): 16 | # 不能赋函数值 17 | setattr(parent, property_name, value) 18 | return '' 19 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/namespace/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/template_system/namespace/utils/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/namespace/utils/post_utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/syntax_block/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from .pure import pure 3 | from .lazy_html import tab 4 | from .refer import refer 5 | from .compatibility import syntax_block_compatibility 6 | from .page import page 7 | 8 | syntax_blocks = { 9 | 'pure': pure, 10 | 'tab': tab, 11 | "refer": refer, 12 | "page": page, 13 | 14 | # compatibility for Bitcron 15 | "footer": syntax_block_compatibility, 16 | "browser": syntax_block_compatibility, 17 | "font": syntax_block_compatibility, 18 | } -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/syntax_block/compatibility.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | #+font 4 | 5 | 6 | def syntax_block_compatibility(**kwargs): 7 | caller = kwargs.pop('caller', None) 8 | if not caller or not hasattr(caller, '__call__'): 9 | return "" 10 | else: 11 | try: 12 | return caller() 13 | except: 14 | return "" -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/syntax_block/lazy_html.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import re 3 | import uuid 4 | from farbox_bucket.server.utils.cache_for_function import cache_result 5 | from farbox_bucket.server.template_system.api_template_render import render_api_template 6 | from farbox_bucket.utils import get_random_html_dom_id 7 | 8 | 9 | 10 | def tab(keys, active=1, **kwargs): 11 | # +tab(keys=[a, b, c], active=1) 12 | # #tab 13 | # #tab 14 | # #tab.etc 15 | 16 | caller = kwargs.pop('caller', None) 17 | if not caller or not hasattr(caller, '__call__'): 18 | return '' 19 | if not isinstance(keys, (list, tuple)) or not keys: 20 | return '' 21 | dom_id = get_random_html_dom_id() 22 | inner_html = caller() 23 | real_tabs_count = inner_html.count('
') 24 | keys = keys[:real_tabs_count] 25 | if len(keys) <=1: # 只有一个,没有处理为tab的需要 26 | return inner_html 27 | for i in range(len(keys)): 28 | tab_dom_id = '%s-%s' % (dom_id, i) 29 | # 替换模板默认的tab id 30 | inner_html = re.sub(r'(
]*?id=[\'"])(tab\d*)([\'"])', '\g<1> class=tab_item \g<2>%s\g<4>'%tab_dom_id, inner_html, count=1) 31 | html = render_api_template('api_tab.jade', 32 | keys=keys, dom_id=dom_id, active=active, inner_html=inner_html, 33 | return_html=True, **kwargs) 34 | 35 | return html 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/syntax_block/modal.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils import get_random_html_dom_id 3 | from farbox_bucket.server.utils.cache_for_function import cache_result 4 | from farbox_bucket.server.template_system.api_template_render import render_api_template 5 | 6 | 7 | def modal(trigger='', *args, **kwargs): 8 | # trigger 如果是对innerHTML的处理,就是一个click_dom_id(selector), 如果是一个url,则是超级链接的title 9 | caller = kwargs.pop('caller', None) 10 | if not caller or not hasattr(caller, '__call__'): 11 | # 不是作为block模式,而是直接调用,可能是GET方式打开一个URL 12 | url = kwargs.get('url') or '' 13 | if not url and args: 14 | url = args[0] 15 | title = trigger 16 | if isinstance(url, (str, unicode)): # 创建一个GET模式的modal的a元素 17 | dom_id = kwargs.get('id') 18 | return render_api_template('api_syntax_modal.jade', ajax=True, return_html=True, url=url, title=title, 19 | dom_id=dom_id, **kwargs) 20 | return '' 21 | inner_html = caller() 22 | dom_id = get_random_html_dom_id() 23 | if trigger and not trigger.startswith('#') and not trigger.startswith('.'): # id 类型的补足 24 | trigger = '#' + trigger 25 | html = render_api_template('api_syntax_modal.jade', inner_html=inner_html, dom_id=dom_id, selector=trigger, 26 | return_html=True, **kwargs) 27 | return html 28 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/syntax_block/page.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from flask import request 3 | from farbox_bucket.server.utils.cache_for_function import cache_result 4 | from farbox_bucket.server.utils.response import force_response 5 | from farbox_bucket.server.template_system.api_template_render import render_api_template 6 | 7 | 8 | 9 | 10 | def page(*sub_args, **kwargs): 11 | caller = kwargs.pop('caller', None) 12 | if caller and request.args.get('pjax', '').lower() == 'true' and hasattr(caller, '__call__'): 13 | # pjax 下的时候,仅仅处理 caller 下的内容,这样就不会引入冗余的 html,从而直接 ajax append 即可 14 | return force_response(caller()) 15 | html = render_api_template('api_syntax_page.jade', caller=caller, return_html=True, **kwargs) 16 | return html 17 | -------------------------------------------------------------------------------- /farbox_bucket/server/template_system/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/template_system/templates/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/utils/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/utils/lazy.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | 4 | class LazyDict(dict): 5 | # 非常有用的一个处理,可以让dict 之类的数据,可以直接调用 property 的写法 6 | def __getitem__(self, item): 7 | try: 8 | value = dict.__getitem__(self, item) 9 | except: 10 | value = LazyDict() 11 | return value 12 | 13 | def __getattr__(self, item): 14 | return self.__getitem__(item) 15 | 16 | def __repr__(self): 17 | return '' 18 | 19 | def __nonzero__(self): 20 | return 0 21 | -------------------------------------------------------------------------------- /farbox_bucket/server/utils/record_and_paginator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/utils/record_and_paginator/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/server/utils/response_html.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import re 3 | from farbox_bucket.utils import get_value_from_data, str_type, unicode_type, string_types, to_bytes, to_unicode 4 | from farbox_bucket.server.utils.request_context_vars import get_no_html_inject_in_request 5 | 6 | 7 | def insert_into_footer(to_insert, html, force=False): 8 | # 插入到 html 页面的尾部 9 | if not force and get_no_html_inject_in_request(): 10 | return html 11 | if not to_insert: 12 | return html 13 | if isinstance(html, str_type) and isinstance(to_insert, unicode_type): 14 | to_insert = to_bytes(to_insert) 15 | if isinstance(to_insert, string_types): 16 | # 会放在之前 17 | if re.search(r'', html, flags=re.I): 18 | html = re.sub(r'\s*\n','%s\n'%to_insert, html, flags=re.I) 19 | return html 20 | 21 | 22 | 23 | def insert_into_header(to_insert, html, force=False): 24 | # 插入到 html 页面的头部 25 | if not force and get_no_html_inject_in_request(): 26 | return html 27 | if not to_insert: 28 | return html 29 | if isinstance(html, str_type) and isinstance(to_insert, unicode_type): 30 | to_insert = to_bytes(to_insert) 31 | if isinstance(to_insert, string_types): 32 | html = re.sub(r'\s*\n', methods=['POST', 'GET']) 14 | def install_ssl(domain=''): 15 | set_not_cache_current_request() 16 | domain = domain or request.values.get('domain', '').lower().strip() 17 | if ':' in domain: 18 | domain = domain.split(':')[0] 19 | if not request.host.startswith('127.0.0.1'): 20 | abort(404, 'should be localhost') 21 | if request.remote_addr and request.remote_addr != '127.0.0.1': 22 | abort(404, 'outside error') 23 | 24 | bucket = get_bucket_from_domain(domain) 25 | if not bucket: 26 | abort(404, 'bucket is not found') 27 | 28 | cert_doc = get_ssl_cert_for_domain(domain) 29 | 30 | ssl_key = cert_doc.get('ssl_key') 31 | ssl_cert = cert_doc.get('ssl_cert') 32 | if ssl_key and ssl_cert: 33 | return send_plain_text('%s,%s' % (ssl_key, ssl_cert)) 34 | else: 35 | return ',' 36 | 37 | 38 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Cais/archive.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | 3 | block content 4 | .content: .list_with_title 5 | entries = d.get_data(type='post',limit=300, sort='desc', status='public').group('-date:year') 6 | for year, posts in entries 7 | div.listing_title= year 8 | .listing: for post in posts: .listing-item 9 | .listing_post 10 | a(href=post.url, title=post.title)= post.title 11 | div.post_date: span.date= post.date("%m-%d") 12 | +h.paginator(pre_label='Prev', next_label='Next') 13 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Cais/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | +h.i18n('Home', '首页', 'zh_cn') 3 | +h.i18n('Categories', '分类', 'zh_cn') 4 | +h.i18n('Archive', '归档', 'zh_cn') 5 | +h.i18n('Tags', '标签', 'zh_cn') 6 | +h.i18n('About', '关于', 'zh_cn') 7 | +h.i18n('Links', '友链', 'zh_cn') 8 | 9 | head 10 | +h.headers 11 | 12 | block title 13 | title= posts.post.title or site.title 14 | 15 | +h.load('/template/css/markdown.scss') 16 | +h.load('/template/css/style.scss') 17 | 18 | body 19 | 20 | .sidebar: .sidebar_body 21 | nav_data = (['首页', '/'], ['归档', '/archive'],['订阅', '/feed']) 22 | //+h.get_nav(nav_data, load_front_sources=False) 23 | +site.just_nav 24 | 25 | .header 26 | .logo_title 27 | .title.animated.fadeInDown 28 | h1(title="") 29 | a(href=h.url("/")) {{ site.title }} 30 | if site.raw_content 31 | .description.animated.fadeInDown 32 | p= '♥ %s ♥' % site.raw_content[:100] 33 | 34 | +site.socials 35 | 36 | 37 | block content 38 | 39 | 40 | .footer 41 | a(target="_blank", href="{{request.url_root}}") 42 | span All content copyright {{site.title}} © 2015 • All rights reserved. 43 | span Designed by 44 | a(href="https://www.caicai.me") CaiCai 45 | .by 46 | a(href="https://farBox.com/?s=f", target="_blank") Proudly published with FarBox! 47 | 48 | +h.auto_sidebar('right') 49 | +h.load('font') 50 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Cais/feed.jade: -------------------------------------------------------------------------------- 1 | doctype xml 2 | +response.set_content_type('application/xml') 3 | feed(xmlns="http://www.w3.org/2005/Atom") 4 | title= site.title 5 | description= site.description.escaped 6 | link(href="http://{{ request.host }}/") 7 | link(ref="self", href="http://{{ request.host }}/feed") 8 | id= site._id 9 | feed_posts = d.get_data(type='post', limit=10) 10 | if feed_posts 11 | updated= feed_posts[0]['date'].strftime('%Y-%m-%dT%H:%M:%SZ') 12 | for post in feed_posts 13 | entry 14 | post_url = 'http://' + request.host + post.url.escaped 15 | title= post.title.escaped 16 | link(href=post_url, rel="alternate") 17 | updated= post.date.strftime('%Y-%m-%dT%H:%M:%SZ') 18 | id= post.url_path.escaped 19 | author 20 | name= site.author or site.admin_name or site.title 21 | summary(type="html")= post.content.escaped -------------------------------------------------------------------------------- /farbox_bucket/themes/Cais/index+tag.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | 3 | mixin make_post(post) 4 | .post 5 | .post_title: h2 6 | a(href=post.url)= post.title 7 | 8 | .post_content.markdown= post.content.opening or post.content.limit(200, keep_images=True) 9 | 10 | .post_footer 11 | if post.tags: .tags: for tag in post.tags 12 | a.btn(role="tags", href=posts.get_tag_url(tag))= tag 13 | 14 | .info 15 | i.fa.fa-clock-o 16 | span.date= post.date("%Y-%m-%d %H:%M") 17 | 18 | i.fa.fa-comment-o 19 | a(href="{{post.url}}#comments")= '%s Comments'%(post.comments_count or 0) 20 | 21 | 22 | block content 23 | .content: .post_list 24 | for post in posts 25 | +make_post(post) 26 | +h.paginator(pre_label='Newer Posts', next_label='Older Posts') 27 | 28 | 29 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Cais/post.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | 3 | 4 | block content 5 | .content 6 | .post_page: .post 7 | .post_title: h2 8 | a= post.title 9 | .post_content.markdown= post.content 10 | .post_footer 11 | if post.tags: .tags: for tag in post.tags 12 | a.btn(role="tags", href=posts.get_tag_url(tag))= tag 13 | .info 14 | i.fa.fa-clock-o 15 | span.date= post.date("%Y-%m-%d %H:%M") 16 | 17 | i.fa.fa-comment-o 18 | a(href="#comments")= '%s Comments'%(post.comments_count or 0) 19 | 20 | +post.comments_as_html() 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Cais/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | [CaiCai (target=_blank)](http://blog.caicai.me/) 是我们的老朋友,作为一名出道不算久的设计师,但是涉猎的知识很广,写代码、策划、测试。 3 | 4 | 5 | CaiCai的进步速度非常快,最开始的时候,他想重新设计FarBox的后台管理,除了UI稿,然后被我们“友好”地驳回了;如果现在他还有时间重新设计的话,恐怕我们就没有这么容易驳回了…… 6 | 7 | 8 | 除了网页界面外,CaiCai还参与了FarBox Editor的界面自定义模块的工作,这是他的一个[repo (target=_blank)](https://github.com/hi-caicai/Farbox-Editor-)。 9 | 10 | 11 | 最近,他开始创作一款新的FarBox模板。对,我们看了一个初始的模样,很值得期待。 12 | 13 | 14 | 有时,也会怀疑,CaiCai的时间怎么如此充裕,虽然有几次大半夜时分联系,他说`正在外面Happy呢`。 15 | 16 | 17 | 对,就是这样一个人的一个作品。希望你会喜欢。 18 | 19 | 20 | 2017-4-16 update: Cais 的模板来自原来的 FarBox 系统,上文这段介绍已经是许多年前的了,如今的 CaiCai 同学也更加的优秀,保留这个介绍,好像还是蛮有趣的事情。 :) 21 | 22 | 23 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Classify/archive.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | 3 | block content 4 | group_type = 'tags' if request.path.strip('/')=='tags' else '-date:year' 5 | entries = d.get_data(type='post',limit=50, sort='desc', status='public').group(group_type) 6 | for sub_title, posts in entries: .archive 7 | h1= sub_title 8 | ul: for post in posts: li 9 | a(href=post.url, title=post.title)= post.title 10 | span.date= post.date.format("%Y-%m-%d") 11 | 12 | if paginator.has_pre or paginator.has_next: .pager 13 | if paginator.has_pre 14 | a.round.pre(href=paginator.pre_url) ← Newer 15 | if paginator.has_next 16 | a.round.next(href=paginator.next_url) Older → -------------------------------------------------------------------------------- /farbox_bucket/themes/Classify/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | +h.headers 4 | 5 | block title 6 | title= request.args.s or site.title 7 | +h.load('markdown') 8 | +h.load('pure') 9 | +h.load('/template/css/style.scss') 10 | body 11 | #header 12 | nav_data = (['首页', '/'], ['归档', '/archive'],['订阅', '/feed']) 13 | //+h.get_nav(nav_data, toggle_menu=True) 14 | +site.nav 15 | 16 | #layout 17 | block content 18 | 19 | #footer= site.footer -------------------------------------------------------------------------------- /farbox_bucket/themes/Classify/category.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | 3 | category = d.get_doc(path=request.offset_path1, type='folder') 4 | 5 | block title 6 | if category 7 | title = category.title 8 | else 9 | +response.raise_404() 10 | 11 | title= title 12 | 13 | block content 14 | category_posts = d.get_data(type='post', status='public', limit=10, path=request.offset_path1) 15 | 16 | .posts_in_list 17 | h1= category.title 18 | ul: for post in category_posts: li 19 | a(href=post.url)= post.title 20 | span.date= post.date('%d %b %Y') 21 | 22 | if paginator.has_pre or paginator.has_next: .pager 23 | if paginator.has_pre 24 | a.round.pre(href=paginator.pre_url) ← Newer 25 | if paginator.has_next 26 | a.round.next(href=paginator.next_url) Older → -------------------------------------------------------------------------------- /farbox_bucket/themes/Classify/feed.jade: -------------------------------------------------------------------------------- 1 | doctype xml 2 | +response.set_content_type('application/xml') 3 | feed(xmlns="http://www.w3.org/2005/Atom") 4 | title= site.title 5 | description= site.description.escaped 6 | link(href="http://{{ request.host }}/") 7 | link(ref="self", href="http://{{ request.host }}/feed") 8 | id= site._id 9 | feed_posts = d.get_data(type='post', limit=10) 10 | if feed_posts 11 | updated= feed_posts[0]['date'].strftime('%Y-%m-%dT%H:%M:%SZ') 12 | for post in feed_posts 13 | entry 14 | post_url = 'http://' + request.host + post.url.escaped 15 | title= post.title.escaped 16 | link(href=post_url, rel="alternate") 17 | updated= post.date.strftime('%Y-%m-%dT%H:%M:%SZ') 18 | id= post.url_path.escaped 19 | author 20 | name= site.author or site.admin_name or site.title 21 | summary(type="html")= post.content.escaped -------------------------------------------------------------------------------- /farbox_bucket/themes/Classify/index.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | 3 | block content 4 | newest_posts = d.get_data(type='post', limit=10, with_page=False, status='public') 5 | if newest_posts 6 | newest_post = newest_posts[0] 7 | if newest_post: .newest_post 8 | h2 9 | a(href=newest_post.url)= newest_post.title 10 | a.summary(href=newest_post.url) 11 | .content.markdown 12 | if newest_post.metadata.refer 13 | refer_doc = d.get_doc(newest_post.metadata.refer) 14 | if refer_doc and refer_doc.type == 'post' 15 | blockquote.refer= refer_doc.content.limit(180) 16 | div= newest_post.content.opening or newest_post.content.limit(180) 17 | 18 | .pure-g#categories 19 | categories = d.get_data(type='folder', limit=100, level=1, sort='position') 20 | for category in categories 21 | .pure-u-1.pure-u-sm-1-2.pure-u-lg-1-3.pure-u-lg-1-4: .category 22 | a(href=h.url('/category/%s'%category.path)) 23 | h3= category.title 24 | posts_count = site.count_folder(category.path, 'posts') 25 | span {{ posts_count }} posts -------------------------------------------------------------------------------- /farbox_bucket/themes/Classify/post.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | 3 | block title 4 | title= post.title 5 | 6 | block content 7 | .post.post_page 8 | h1.title= post.title 9 | .content 10 | .post_content.markdown 11 | if post.metadata.refer 12 | refer_doc = d.get_doc(post.metadata.refer) 13 | if refer_doc and refer_doc.type == 'post' 14 | blockquote.refer= refer_doc.content.plain_html 15 | div= post.content 16 | .info 17 | if post.tags: .tags: for tag in post.tags 18 | a(href=posts.get_tag_url(tag))= tag 19 | .date @{{post.date}} 20 | 21 | +post.comments_as_html() 22 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Esta/archive.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | block content 3 | .autopagerize_page_element:.content 4 | .archive: ul.list_with_title 5 | entries = d.get_data(type='post',limit=300, sort='desc').group('-date:year') 6 | for year, posts in entries 7 | div.listing_title= year 8 | ul.listing 9 | for post in posts: .listing_item: .listing_post 10 | a(href=post.url, title=post.title)= post.title 11 | div.post_time 12 | span.date= post.date("%m-%d") 13 | +h.paginator() -------------------------------------------------------------------------------- /farbox_bucket/themes/Esta/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | +h.i18n('Home', '首页', 'zh_cn') 3 | +h.i18n('Categories', '分类', 'zh_cn') 4 | +h.i18n('Archive', '归档', 'zh_cn') 5 | +h.i18n('Tags', '标签', 'zh_cn') 6 | +h.i18n('About', '关于', 'zh_cn') 7 | +h.i18n('Links', '友链', 'zh_cn') 8 | head 9 | block title 10 | title= post.title or site.title 11 | +h.headers 12 | +h.load('markdown') 13 | +h.load('/template/style.scss') 14 | 15 | body 16 | .sidebar 17 | .logo_title 18 | .title 19 | a(href=h.url('/')) 20 | img(src=site.site_avatar,style="width:127px;") 21 | h3(title="") 22 | a(href=h.url("/"))= site.title 23 | if site.sub_title 24 | .sub_title: p=site.sub_title 25 | 26 | +site.socials 27 | 28 | .footer 29 | a(target="_blank", href="http://{{request.domain}}") 30 | span Designed by 31 | a(href="https://www.caicai.me") CaiCai 32 | .by_farbox 33 | a(href="https://www.farBox.com", target="_blank") Proudly published with FarBox! 34 | 35 | .main 36 | .page_top 37 | +site.get_nav(align="right", toggle_menu=True) 38 | 39 | block content 40 | 41 | +h.load('font') 42 | +h.load('/fb_static/lib/fonts/open_sans.css') 43 | 44 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Esta/index+tag.jade: -------------------------------------------------------------------------------- 1 | from mixins import make_post 2 | extends base.jade 3 | block content 4 | .autopagerize_page_element:.content 5 | for post in posts 6 | +make_post(post, is_detail=False) 7 | +h.paginator(pre_label="Newer Posts", next_label="Older Posts") 8 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Esta/mixins.jade: -------------------------------------------------------------------------------- 1 | mixin make_post(post, is_detail=False) 2 | .post 3 | .post_title: h2 4 | if is_detail 5 | a.detail_title= post.title 6 | else 7 | a(href=post.url)= post.title 8 | if is_detail 9 | .post_content.markdown= post.content 10 | else 11 | .post_content.markdown= post.content.opening or post.content.limit(200, keep_images=False) 12 | .post_footer: .meta: .info 13 | i.fa.fa-calendar 14 | span.date= post.date("%Y-%m-%d") 15 | span.comments_count 16 | i.fa.fa-comment-o 17 | a(href="{{post.url}}#comments")= '%s Comments' % (post.comments_count or 0) 18 | 19 | if post.tags 20 | i.fa.fa-tag 21 | for tag in post.tags 22 | a.tag(href=posts.get_tag_url(tag))= tag 23 | 24 | if is_detail 25 | +post.comments_as_html() -------------------------------------------------------------------------------- /farbox_bucket/themes/Esta/post.jade: -------------------------------------------------------------------------------- 1 | from mixins import make_post 2 | 3 | extends base.jade 4 | block content 5 | .content: .post-page 6 | +make_post(post, is_detail=True) -------------------------------------------------------------------------------- /farbox_bucket/themes/Fexo/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | +h.headers 4 | block title 5 | title= post.title or site.title 6 | +h.load('/fb_static/lib/pure.css') 7 | +h.load('/fb_static/lib/markdown/basic.css') 8 | +h.load("/template/static/style.scss") 9 | body 10 | block header 11 | #nav_header 12 | +site.get_nav(align="right", toggle_menu=True) 13 | 14 | block content 15 | 16 | 17 | 18 | +h.back_to_top() 19 | 20 | +h.load("/fb_static/lib/jquery.js") 21 | +h.load('font') 22 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Fexo/category+categories.jade: -------------------------------------------------------------------------------- 1 | from mixins import make_timeline 2 | extends base.jade 3 | block content 4 | .content 5 | .page-header 6 | .box-blog-info 7 | a.avatar(href='/') 8 | img(src=site.site_avatar) 9 | .info 10 | h3.name= site.title 11 | .slogan= site.sub_title 12 | 13 | if posts.category 14 | all_posts = posts.list_obj 15 | else 16 | all_posts = get_data(type='post',limit=300, sort='desc') 17 | entries = all_posts.group('path:parent_path') 18 | +make_timeline(entries) 19 | 20 | +h.paginator() -------------------------------------------------------------------------------- /farbox_bucket/themes/Fexo/index.jade: -------------------------------------------------------------------------------- 1 | from mixins import make_timeline 2 | extends base.jade 3 | 4 | block content 5 | .content 6 | .page-header: .box-blog-info 7 | a.avatar(href='/') 8 | img(src=site.site_avatar) 9 | .info 10 | h3.name= site.title 11 | .slogan= site.sub_title 12 | 13 | entries = d.get_data(type='post',limit=300, sort='desc').group('-date:year') 14 | +make_timeline(entries) 15 | 16 | +h.paginator() -------------------------------------------------------------------------------- /farbox_bucket/themes/Fexo/license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 forsigner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /farbox_bucket/themes/Fexo/mixins.jade: -------------------------------------------------------------------------------- 1 | mixin make_timeline(cated_posts) 2 | ul.post_list: for category, posts in cated_posts 3 | li.item_title 4 | dot_class = 'dot dot_%s'%loop.index 5 | span(style="background: {{h.get_a_color()}}", class=dot_class) 6 | category_title = category or 'UnCategory' 7 | span.category_title(id=h.get_dom_id(category_title))= category_title 8 | for post in posts 9 | li.item_post.item 10 | span.dot(style="background: {{h.get_a_color()}}") 11 | span.post_date= post.date.format('%m-%d') 12 | a.post_title(href=post.url)= post.title 13 | 14 | 15 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Fexo/post.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | 3 | +h.i18n("Post TOC", "文章目录", "zh_cn") 4 | 5 | block content 6 | use_toc = post.toc and post.metadata.get('toc', True) 7 | .post: div(class="post_with_toc" if use_toc else "post_without_toc") 8 | .header 9 | h1.title= post.title 10 | .info 11 | span.date= post.date('%Y-%m-%d') 12 | if post.category 13 | a.category(href=post.category.url)= post.category.filename or post.category.title 14 | 15 | if use_toc 16 | .post_container: .pure-g 17 | .pure-u-1.pure-u-md-2-3.pure-u-lg-4-5 18 | .post_content.markdown= post.content 19 | .pure-u-1.pure-u-md-1-3.pure-u-lg-1-5: .toc_container 20 | toc_header = '
%s
'%_("Post TOC") 21 | +h.auto_toc(post, toc_header=toc_header) 22 | else 23 | .post_container 24 | .post_content.markdown= post.content 25 | 26 | +post.comments_as_html() -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/License.txt: -------------------------------------------------------------------------------- 1 | 此模板运行于 FarBox 之上, 如果需要应用到其它平台上, 请先联系原作者获得许可。 2 | 3 | 另外, 此主题为付费主题, 付费之后可以获取、修改源码, 但不可以任何方式公开、开源此源码, 以及其衍生版本的源码。 -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/archive.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | block content 3 | .autopagerize_page_element:.content 4 | ul.list_with_title 5 | entries = d.get_data(type='post',limit=300, sort='desc').group('-date:year') 6 | for year, posts in entries 7 | div.listing_title= year 8 | ul.listing 9 | for post in posts 10 | .listing_item 11 | .listing_post 12 | a(href=post.url, title=post.title)= post.title 13 | div.post_time 14 | span.date= post.date("%m-%d") 15 | +h.paginator(pre_label=_('Prev'), next_label=_('Next')) -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | 3 | head 4 | +h.headers 5 | block title 6 | title= posts.post.title or site.title 7 | +h.load('font') 8 | +h.load('/template/css/markdown.scss') 9 | +h.load('/template/css/animate.3.5.2.min.css') 10 | +h.load('/template/css/style.scss') 11 | body 12 | .main.animated 13 | .header.animated.fadeInDown 14 | .site_title_container 15 | .site_title 16 | if site.title.length > 6 17 | a_class = 'long_long' 18 | elif site.title.length > 4 19 | a_class = 'long' 20 | else 21 | a_class = '' 22 | h1 23 | a(class=a_class, href=h.url('/'))= site.title 24 | 25 | .description 26 | p.sub_title= site.sub_title 27 | //+h.nav(['首页', '/'], ['订阅', '/feed']) 28 | +site.nav 29 | block content 30 | 31 | .footer 32 | 33 | .powered_by 34 | a(href="https://www.caicai.me") Designed by CaiCai, 35 | a(href="https://www.farBox.com", target="_blank") Proudly published with FarBox! 36 | 37 | .footer_slogan 38 | span= site.configs.footer_slogan or '重拾写作的乐趣' 39 | 40 | -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/feed.jade: -------------------------------------------------------------------------------- 1 | doctype xml 2 | +response.set_content_type('application/xml') 3 | feed(xmlns="http://www.w3.org/2005/Atom") 4 | title= site.title 5 | description= site.description.escaped 6 | link(href="http://{{ request.host }}/") 7 | link(ref="self", href="http://{{ request.host }}/feed") 8 | id= site._id 9 | feed_posts = d.get_data(type='post', limit=10) 10 | if feed_posts 11 | updated= feed_posts[0]['date'].strftime('%Y-%m-%dT%H:%M:%SZ') 12 | for post in feed_posts 13 | entry 14 | post_url = 'http://' + request.host + post.url.escaped 15 | title= post.title.escaped 16 | link(href=post_url, rel="alternate") 17 | updated= post.date.strftime('%Y-%m-%dT%H:%M:%SZ') 18 | id= post.url_path.escaped 19 | author 20 | name= site.author or site.admin_name or site.title 21 | summary(type="html")= post.content.escaped -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/index+tag.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | block content 3 | .autopagerize_page_element:.content 4 | for post in posts: .post.animated.fadeInDown 5 | .post_title 6 | h2: a(href=post.url)= post.title 7 | .list 8 | .post_content 9 | p= post.content.limit(words=90).no_pic.plain 10 | 11 | .post_footer: .meta: .info 12 | span.field 13 | i.fa.fa-sun-o 14 | span.date= post.date.format("%Y.%m.%d") 15 | span.field 16 | i.fa.fa-comment-o 17 | span= "%s Comments" % (post.comments_count or 0) 18 | if post.tags: span.field.tags 19 | i.fa.fa-flask 20 | for tag in post.tags 21 | a.tag(href=posts.get_tag_url(tag))= tag 22 | 23 | +h.paginator(pre_label='返回上一页', next_label='阅读更多文章') -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/post.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | 3 | block content 4 | .autopagerize_page_element: .content: .post_page 5 | .post.animated.fadeInDown 6 | .post_title.post_detail_title 7 | h2: a= post.title 8 | span.date= post.date("%Y.%m.%d %H:%M") 9 | 10 | .post_content.markdown= post.content 11 | 12 | .post_footer:.meta: .info 13 | if post.tags: span.field.tags 14 | i.fa.fa-flask 15 | for tag in post.tags 16 | a.tag(href=posts.get_tag_url(tag))= tag 17 | +post.comments_as_html() 18 | +h.back_to_top() -------------------------------------------------------------------------------- /farbox_bucket/themes/Puti/readme.txt: -------------------------------------------------------------------------------- 1 | price: 10 2 | 3 | 这个模板简洁而极具设计感,在模板应用后,网站对应的设置 (Dashboard) 内会多出几个字体相关的设置。 4 | 5 | **但请注意:** 6 | 1,Puti 这个主题更适用于中文的内容。 7 | 2,网站的标题应该是中文的、没有标点符号、不超过8字,最好是偶数,比如 4 字 或 6 字。 8 | 3,网站的副标题也不宜过长。 -------------------------------------------------------------------------------- /farbox_bucket/themes/Sollrei/archive.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | 3 | block content 4 | h2.archive-title 归档 5 | entries = data.get_data(type='post', limit=300, sort='-date', pager_name='archive').group('date:year', reverse=True) 6 | .mod-archive:.archive 7 | for year, posts in entries 8 | .archive-year= year 9 | ul.archive-list 10 | for post in posts: li.post-item 11 | span.date= post.date('%Y-%m-%d') 12 | a(href=post.url, title=post.title)= post.title 13 | 14 | +h.paginator() -------------------------------------------------------------------------------- /farbox_bucket/themes/Sollrei/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | +h.headers 4 | block title 5 | title= site.title 6 | +h.load('/template/css/reset.scss') 7 | +h.load('font', 'pure', 'markdown') 8 | +h.load('/template/css/style.scss') 9 | +h.load("/template/css/fonts.css") 10 | body.page-home 11 | #header.mod-header 12 | h1.site-title:a(href=h.url('/?status=loaded'))= site.title 13 | nav_items = [('Home', '/')] 14 | for category in posts.categories 15 | +nav_items.append([category.title, category.url]) 16 | +nav_items.append(['Archive', '/archive']) 17 | .mod-nav 18 | +h.get_nav(nav_items, toggle_menu=True) 19 | 20 | block banner 21 | 22 | #main.main 23 | .pure-g 24 | .pure-u-23-24.pure-u-md-17-24.layout 25 | block content 26 | footer#footer.mod-footer 27 | .contact= site.socials 28 | .copyright 29 | span Powered by 30 | a(href="https://www.farBox.com", target="_blank") FarBox   31 | span Theme by 32 | a(href="https://www.sollrei.me") Sollrei 33 | +h.load('jquery') 34 | +h.load('/template/js/particles.js') 35 | +h.load('/template/js/index.js') -------------------------------------------------------------------------------- /farbox_bucket/themes/Sollrei/index+category+tag.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | 3 | is_not_homepage = request.path.startswith('/tag/') or request.path.startswith('/category/') 4 | 5 | block title 6 | title= posts.category.title or posts.tags.join("+") or site.title 7 | 8 | block banner 9 | .mod-banner 10 | bg = site.real_background_image or '/fb_static/unsplash/5.jpg' 11 | .banner(style='background-image: url(%s)'%bg, id='' if is_not_homepage else 'particles-js') 12 | if is_not_homepage: .banner-title 13 | h3= posts.category.title or posts.tags.join('+') 14 | posts_count = posts.category.posts_count or posts.length 15 | p= posts_count 16 | span= 'posts' if posts_count !=1 else 'post' 17 | 18 | 19 | block content 20 | 21 | .post-list:for post in posts: .mod-post 22 | .post-meta 23 | time.post-date(datetime=post.date('%B %d, %Y'), pubdate="pubdate") 24 | span.post-m= post.date('%b') 25 | span.post-d= post.date('%d') 26 | span.post-y= post.date('%Y') 27 | .post-main 28 | h3.post-title 29 | a(href=post.url)= post.title 30 | .post-content.markdown= post.content.opening or post.content.limit(150) 31 | .post-info 32 | if post.tags 33 | for tag in post.tags 34 | a.tag(href=posts.get_tag_url(tag))= tag 35 | span.count 36 | i.fa.fa-book 37 | span= post.visits or 0 38 | +h.paginator() -------------------------------------------------------------------------------- /farbox_bucket/themes/Sollrei/post.jade: -------------------------------------------------------------------------------- 1 | extends base.jade 2 | block title 3 | title= post.title 4 | block content 5 | .detail-page 6 | .mod-article.mod-post 7 | .post-meta 8 | time.post-date(datetime=post.date('%B %d, %Y'), pubdate="pubdate") 9 | span.post-m= post.date('%b') 10 | span.post-d= post.date('%d') 11 | span.post-y= post.date('%Y') 12 | .post-main 13 | h2.article-title= post.title 14 | .post-metas 15 | if post.tags: span 16 | i.fa.fa-tags 17 | for tag in post.tags 18 | a.tag(href="/tag/{{tag}}")  {{tag}} 19 | span.count 20 | i.fa.fa-book 21 | span= post.visits or 0 22 | .entry.markdown= post.content 23 | 24 | 25 | +post.comments_as_html() -------------------------------------------------------------------------------- /farbox_bucket/themes/build_themes.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import os 3 | from farbox_bucket.client.dump_template import get_template_info 4 | from farbox_bucket.utils import to_bytes 5 | 6 | root = os.path.abspath(os.path.dirname(__file__)) 7 | 8 | themes_py_file = os.path.join(root, '__init__.py') 9 | 10 | 11 | templates = {} 12 | 13 | for name in os.listdir(root): 14 | folder_path = os.path.join(root, name) 15 | if not os.path.isdir(folder_path): 16 | continue 17 | template_key = name.lower().strip() 18 | template_info = get_template_info(folder_path) 19 | template_info['_theme_key'] = template_key 20 | templates[template_key] = template_info 21 | 22 | 23 | py_file_content = '#coding: utf8\nthemes = %s' % templates 24 | with open(themes_py_file, 'wb') as f: 25 | f.write(to_bytes(py_file_content)) 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /farbox_bucket/utils/async_block.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | from flask import request 4 | from gevent.event import Event 5 | import gevent 6 | 7 | 8 | async_website_blocks = {} 9 | 10 | 11 | def run_in_website_with_block(func): 12 | def _func(*args, **kwargs): 13 | host = request.host 14 | block_event = async_website_blocks.setdefault(host, Event()) 15 | while block_event.is_set(): 16 | gevent.sleep(0.01) 17 | block_event.set() 18 | try: 19 | result = func(*args, **kwargs) 20 | block_event.clear() 21 | return result 22 | except Exception as e: 23 | block_event.clear() 24 | raise e 25 | return _func 26 | -------------------------------------------------------------------------------- /farbox_bucket/utils/cli_color.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import, print_function 3 | 4 | #CSI="\x1B[" 5 | #RED = CSI+"31;40m" 6 | #GREEN = CSI+'32;40m' 7 | #RESET =CSI+"m" 8 | 9 | 10 | FLAGS = dict( 11 | RESET = "\x1B[0m", 12 | BOLD = "\x1B[1m", 13 | DIM = "\x1B[2m", 14 | UNDER = "\x1B[4m", 15 | REVERSE = "\x1B[7m", 16 | HIDE = "\x1B[8m", 17 | CLEARSCREEN = "\x1B[2J", 18 | CLEARLINE = "\x1B[2K", 19 | BLACK = "\x1B[30m", 20 | RED = "\x1B[31m", 21 | GREEN = "\x1B[32m", 22 | YELLOW = "\x1B[33m", 23 | BLUE = "\x1B[34m", 24 | MAGENTA = "\x1B[35m", 25 | CYAN = "\x1B[36m", 26 | WHITE = "\x1B[37m", 27 | BBLACK = "\x1B[40m", 28 | BRED = "\x1B[41m", 29 | BGREEN = "\x1B[42m", 30 | BYELLOW = "\x1B[43m", 31 | BBLUE = "\x1B[44m", 32 | BMAGENTA = "\x1B[45m", 33 | BCYAN = "\x1B[46m", 34 | BWHITE = "\x1B[47m", 35 | NEWLINE = "\r\n\x1B[0m", 36 | ) 37 | 38 | def print_with_color(strings, color='red', end='\r\n'): 39 | color = FLAGS.get(color.upper()) 40 | if color: 41 | print(color + strings + FLAGS['RESET'], end=end) 42 | else: 43 | print(strings) 44 | 45 | 46 | def print_colorful_parts(string_parts, end=''): 47 | for strings, color in string_parts: 48 | print_with_color(strings, color, end) 49 | print(FLAGS['NEWLINE'], end='') 50 | 51 | 52 | if __name__ == '__main__': 53 | print_with_color('hello', 'green', end=' ') 54 | print_with_color('hello', 'blue') 55 | 56 | print_colorful_parts( 57 | [('hello', 'magenta'), 58 | ('world', 'yellow'), 59 | ('hello', 'red'), 60 | ('world', 'cyan')], 61 | end=' ' 62 | ) 63 | -------------------------------------------------------------------------------- /farbox_bucket/utils/client_sync/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/utils/client_sync/log.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import, print_function 3 | import datetime 4 | from farbox_bucket.utils import smart_str, string_types 5 | from farbox_bucket.utils.path import write_file 6 | from .sync_utils import make_sure_sync_log_path 7 | 8 | 9 | 10 | def write_logs(logs, app_name, filepath=None, root=None,): 11 | # filepath: logs for which file, root: under which root 12 | if isinstance(logs, string_types): 13 | logs = [logs] 14 | if not logs: 15 | return # ignore 16 | if not root: 17 | return # ignore 18 | now = datetime.datetime.now() 19 | now_str = now.strftime('%Y-%m-%d %H:%M:%S') 20 | log_path = make_sure_sync_log_path(root, app_name) 21 | with open(log_path, 'a') as f: 22 | for log in logs: 23 | log = raw_log = smart_str(log) 24 | if filepath: 25 | filepath = smart_str(filepath) 26 | log = '%s: %s %s\n' %(now_str, filepath, log) 27 | else: 28 | log = '%s %s\n' %(now_str, smart_str(log)) 29 | write_file(f, log) 30 | 31 | -------------------------------------------------------------------------------- /farbox_bucket/utils/console_utils.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | import sys, getopt 4 | 5 | 6 | 7 | # getopt.getopt(['--file=error.txt', '-h', '-v', '6', '--version', '9'], 'hv:', ['file=', 'version']) 8 | #getopt.getopt([ 'farbox', '--file=error.txt', '--version','-h', '-v', '6'], 'hv:', ['file=', 'version']) 9 | # getopt.getopt(['--file=error.txt', '--version','-h', '-v', '6', '9'], 'hv:', ['file=', 'version']) 10 | # ([('--file', 'error.txt'), ('-h', ''), ('-v', '6'), ('--version', '')], ['9']) 11 | 12 | def get_args_from_console(raw_args=None, short_opts='', long_opts=None): 13 | if raw_args is None: 14 | raw_args = sys.argv[1:] 15 | long_opts = long_opts or [] 16 | opts, args = getopt.getopt(raw_args, short_opts, long_opts) 17 | kwargs = {} 18 | for k, v in opts: 19 | k = k.strip('-') 20 | kwargs[k] = v 21 | return kwargs, args 22 | 23 | 24 | def get_first_arg_from_console(): 25 | raw_args = sys.argv[1:] 26 | if raw_args: 27 | return raw_args[0] 28 | else: 29 | return None -------------------------------------------------------------------------------- /farbox_bucket/utils/convert/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/utils/convert/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/utils/convert/coffee2js.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | import os, subprocess, tempfile 3 | try: 4 | import gevent 5 | except: 6 | gevent = None 7 | 8 | def get_bin_script(): 9 | scripts = [ 10 | '/usr/local/bin/coffee', 11 | '/usr/bin/coffee', 12 | '/usr/local/share/npm/bin/coffee' 13 | ] 14 | for bin_script in scripts: 15 | if os.path.isfile(bin_script): 16 | return bin_script 17 | 18 | COFFEE_SCRIPT = get_bin_script() 19 | 20 | 21 | def compile_coffee(raw_content): 22 | if isinstance(raw_content, unicode): 23 | raw_content = raw_content.encode('utf8') 24 | temp = tempfile.NamedTemporaryFile() 25 | temp.file.write(raw_content) 26 | temp.file.close() 27 | 28 | command = 'cat %s | %s -sc' % (temp.name, COFFEE_SCRIPT) 29 | try: 30 | js_content = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) 31 | except: 32 | js_content = raw_content 33 | temp.close() 34 | return js_content 35 | 36 | def is_coffee_valid(): 37 | if 'call(this)' in compile_coffee('number = -42 if opposite'): 38 | return True 39 | else: 40 | return False 41 | 42 | 43 | def compile_coffee_with_timeout(raw_content, timeout=2): 44 | if not gevent: 45 | return 46 | gevent_job = gevent.spawn(compile_coffee, raw_content) 47 | try: 48 | content = gevent_job.get(block=True, timeout=timeout) 49 | except: 50 | content = '' 51 | gevent_job.kill(block=False) 52 | return content 53 | 54 | 55 | -------------------------------------------------------------------------------- /farbox_bucket/utils/convert/utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from farbox_bucket.utils.convert.coffee2js import compile_coffee 3 | from farbox_bucket.utils.convert.css import compile_css 4 | from farbox_bucket.utils.convert.jade2jinja import convert_jade_to_html 5 | 6 | 7 | def compile_frontend_resource(ext, raw_content): 8 | ext = ext.lower().strip('.') 9 | if ext in ['less', 'scss', 'sass']: 10 | func = compile_css 11 | compiled_type = 'text/css' 12 | elif ext in ['coffee']: 13 | func = compile_coffee 14 | compiled_type = 'text/javascript' 15 | elif ext in ['jade']: 16 | func = convert_jade_to_html 17 | compiled_type = 'text/html' 18 | else: 19 | func = '' 20 | compiled_type = '' 21 | if func: 22 | try: 23 | compiled_content = func(raw_content) 24 | except: 25 | compiled_content = '' 26 | else: 27 | compiled_content = '' 28 | return compiled_type, compiled_content -------------------------------------------------------------------------------- /farbox_bucket/utils/encrypt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/utils/encrypt/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/utils/error.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import traceback, sys 3 | 4 | 5 | 6 | 7 | 8 | def print_error(exc_info=None): # 本身不要再引发错误 9 | f = None 10 | try: 11 | error_info = exc_info or sys.exc_info() 12 | if error_info: 13 | e_type, value, tb = error_info[:3] 14 | try: 15 | traceback.print_exception(e_type, value, tb) 16 | except: 17 | pass 18 | except: 19 | pass -------------------------------------------------------------------------------- /farbox_bucket/utils/functional.py: -------------------------------------------------------------------------------- 1 | 2 | # You can't trivially replace this `functools.partial` because this binds to 3 | # classes and returns bound instances, whereas functools.partial (on CPython) 4 | # is a type and its instances don't bind. 5 | def curry(_curried_func, *args, **kwargs): 6 | def _curried(*moreargs, **morekwargs): 7 | return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) 8 | _curried.__name__ = _curried_func.__name__ 9 | _curried.func_name = _curried_func.func_name 10 | _curried.original_func = _curried_func 11 | return _curried 12 | 13 | 14 | 15 | class cached_property(object): 16 | """ 17 | Decorator that creates converts a method with a single 18 | self argument into a property cached on the instance. 19 | """ 20 | def __init__(self, func): 21 | self.func = func 22 | 23 | def __get__(self, instance, type): 24 | res = instance.__dict__[self.func.__name__] = self.func(instance) 25 | return res 26 | 27 | -------------------------------------------------------------------------------- /farbox_bucket/utils/gzip_content.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | from io import BytesIO 4 | import zlib 5 | #from gzip import GzipFile 6 | from base64 import b64decode, b64encode 7 | 8 | 9 | def gzip_content(content, base64=False): 10 | if isinstance(content, unicode): 11 | content = content.encode('utf8') 12 | zipped_content = zlib.compress(content) 13 | if base64: 14 | zipped_content = b64encode(zipped_content) 15 | return zipped_content 16 | 17 | 18 | def ungzip_content(raw_content, base64=False): 19 | if base64: 20 | raw_content = b64decode(raw_content) 21 | if isinstance(raw_content, unicode): 22 | raw_content = raw_content.encode('utf8') 23 | original_size = len(raw_content) 24 | f = BytesIO(raw_content) 25 | #z = zlib.decompressobj(15 + 16) # zlib.MAX_WBITS == 15 26 | z = zlib.decompressobj() 27 | total_size = 0 28 | content = '' 29 | while True: 30 | buf = z.unconsumed_tail 31 | if buf == "": 32 | buf = f.read(1024) 33 | if buf == "": 34 | break 35 | got = z.decompress(buf, 4096) 36 | if got == "": 37 | break 38 | content += got 39 | total_size += len(got) 40 | if total_size > 50*original_size: 41 | # this is boom 42 | return '' 43 | return content -------------------------------------------------------------------------------- /farbox_bucket/utils/image/__init__.py: -------------------------------------------------------------------------------- 1 | #coding:utf8 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /farbox_bucket/utils/ip/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/utils/ip/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/utils/logger.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | import logging 4 | 5 | 6 | cached_file_loggers = {} 7 | 8 | def get_file_logger(name): 9 | cached_logger = cached_file_loggers.get(name) 10 | if cached_logger: 11 | return cached_logger 12 | logger = logging.getLogger(name) 13 | logger.setLevel(logging.INFO) 14 | # create a file handler 15 | try: 16 | handler = logging.FileHandler('/var/log/%s.log'%name) 17 | except: 18 | handler = logging.FileHandler('/tmp/log_%s.log' % name) 19 | handler.setLevel(logging.INFO) 20 | # create a logging format 21 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 22 | handler.setFormatter(formatter) 23 | # add the handlers to the logger 24 | logger.addHandler(handler) 25 | cached_file_loggers[name] = logger 26 | return logger -------------------------------------------------------------------------------- /farbox_bucket/utils/mail/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import -------------------------------------------------------------------------------- /farbox_bucket/utils/mail/basic_utils.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | from farbox_bucket.utils import email_re, string_types 4 | 5 | def pure_email_address(address, check=False): 6 | #name -> xxxx & 小写处理 7 | if not address: 8 | return 9 | if isinstance(address, (list, tuple)) and len(address) == 1: 10 | address = address[0] 11 | if isinstance(address, string_types): 12 | address = address.split('<', 1)[-1].strip('<>').strip().lower() 13 | address = address.replace('%40', '@') # for URL 14 | if check: 15 | if not is_email_address(address): 16 | return 17 | return address 18 | 19 | 20 | def is_email_address(email): 21 | if isinstance(email, string_types): 22 | email = email.strip() 23 | if email: 24 | return bool(email_re.match(email)) 25 | return False 26 | 27 | 28 | def get_valid_addresses(addresses, max_size=None): 29 | if isinstance(addresses, string_types): # 单一的一个邮箱地址 30 | return pure_email_address(addresses) 31 | if not isinstance(addresses, (tuple, list)): # 不支持的类型 32 | return [] 33 | result = [] 34 | for address in addresses: 35 | if is_email_address(address): 36 | address = pure_email_address(address) 37 | if address not in result: 38 | result.append(address) 39 | if max_size and isinstance(max_size, int): 40 | result = result[:max_size] 41 | return result 42 | 43 | 44 | -------------------------------------------------------------------------------- /farbox_bucket/utils/mail/system.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import datetime 3 | from farbox_bucket.settings import SES_ID, SES_KEY, SES_SENDER 4 | from farbox_bucket.utils.ssdb_utils import qpush_back 5 | from .utils import send_email_by_amazon 6 | 7 | 8 | 9 | 10 | def send_mail_by_system(to_address, content, subject=None, raw=False): 11 | if not SES_KEY or not SES_ID: 12 | return False 13 | if len(content) > 1024*1024: # 内容过大了 14 | return False 15 | ses_id, ses_key = SES_ID, SES_KEY 16 | message_id = send_email_by_amazon(to_address=to_address, content=content, subject=subject, raw=raw, 17 | from_address=SES_SENDER, ses_id = ses_id, ses_key=ses_key) 18 | if not message_id: 19 | return False 20 | else: 21 | log_doc = dict( 22 | message_id = message_id, 23 | to_address = to_address, 24 | sent_at = datetime.datetime.utcnow(), 25 | subject = subject, 26 | raw = raw, 27 | content = content, 28 | ) 29 | qpush_back('_system_mail_sender', log_doc) # 记录日志 30 | return True 31 | 32 | -------------------------------------------------------------------------------- /farbox_bucket/utils/md_related/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/utils/md_related/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/utils/memcache_block.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from farbox_bucket.utils.memcache import cache_client 3 | 4 | 5 | 6 | def is_blocked(block_id, ttl=60): 7 | cache_key = "mblock_%s" % block_id 8 | if cache_client.get(cache_key): 9 | return True 10 | else: 11 | cache_client.set(cache_key, "y", expiration=ttl) 12 | -------------------------------------------------------------------------------- /farbox_bucket/utils/monkey/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/utils/monkey/http.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from httplib import HTTPConnection 3 | from urllib3 import PoolManager 4 | 5 | _get_response = HTTPConnection.getresponse 6 | _pool_manager_init = PoolManager.__init__ 7 | 8 | def http_connection_get_response(self, buffering=False): 9 | response = _get_response(self, buffering) 10 | if response.status == 206: # 不是200, 像Dropbox的API就会报错 11 | response.status = 200 12 | return response 13 | 14 | 15 | def pool_manger_init(self, num_pools=10, headers=None, **connection_pool_kw): 16 | connection_pool_kw['block'] = True 17 | connection_pool_kw['maxsize'] = 500 18 | _pool_manager_init(self, num_pools, headers, **connection_pool_kw) 19 | 20 | 21 | PoolManager.__init__ = pool_manger_init 22 | HTTPConnection.getresponse = http_connection_get_response 23 | -------------------------------------------------------------------------------- /farbox_bucket/utils/pay/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hepochen/FarBox/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/utils/pay/__init__.py -------------------------------------------------------------------------------- /farbox_bucket/utils/simple_encrypt.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | import base64 4 | from Crypto.Cipher import DES 5 | from farbox_bucket.utils import to_bytes 6 | 7 | 8 | def simple_encrypt(key, text): 9 | key = key[:8] 10 | text = to_bytes(text) 11 | des = DES.new(key, DES.MODE_ECB) 12 | reminder = len(text)%8 13 | if reminder == 0: # pad 8 bytes 14 | text += '\x08'*8 15 | else: 16 | text += chr(8-reminder) * (8-reminder) 17 | return base64.b64encode(des.encrypt(text)) 18 | 19 | 20 | def simple_decrypt(key,text): 21 | key = key[:8] 22 | text = base64.b64decode(text) 23 | des = DES.new(key, DES.MODE_ECB) 24 | text = des.decrypt(text) 25 | pad = ord(text[-1]) 26 | if pad == '\x08': 27 | return text[:-8] 28 | return text[:-pad] 29 | -------------------------------------------------------------------------------- /farbox_bucket/utils/system_status_recorder.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | from __future__ import absolute_import 3 | from farbox_bucket.utils.ssdb_utils import hset, hget, hscan 4 | import datetime 5 | import time 6 | 7 | 8 | 9 | 10 | class record_system_timed_status(object): 11 | """ 12 | @record_system_timed_status(field='??') 13 | """ 14 | def __init__(self, field): 15 | self.field = field 16 | 17 | def __call__(self, func): 18 | def _func(*args, **kwargs): 19 | t1 = time.time() 20 | result = func(*args, **kwargs) 21 | try: 22 | seconds_cost = str(time.time() - t1) 23 | key = '%s_%s' % (int(t1*1000), self.field) 24 | hset('_system_recorder', key=key, value=seconds_cost) 25 | except: 26 | pass 27 | return result 28 | return _func 29 | 30 | 31 | 32 | def get_system_timed_records(): 33 | raw_records = hscan('_system_recorder', limit=1000, reverse_scan=True) 34 | records = [] 35 | for record_id, seconds_cost in raw_records: 36 | if '_' not in record_id: 37 | continue 38 | timestamp, action_name = record_id.split('_', 1) 39 | try: 40 | timestamp = int(timestamp)/1000. 41 | except: 42 | continue 43 | date = datetime.datetime.utcfromtimestamp(timestamp) 44 | date = date.strftime('%Y-%m-%d %H:%M:%S') 45 | record = '%s UTC %s costs %s' % (date, action_name, seconds_cost) 46 | records.append(record) 47 | return records 48 | -------------------------------------------------------------------------------- /farbox_bucket/utils/web_utils/__init__.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | -------------------------------------------------------------------------------- /farbox_bucket/utils/web_utils/request.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | 4 | def to_per_page(per_page, new_per_page=None, min_per_page=1, max_per_page=1000): 5 | if new_per_page: 6 | # 如果有设定 new_per_page, 会尝试以其为高优先级 7 | try: per_page = int(new_per_page) 8 | except: pass 9 | if per_page < min_per_page: 10 | per_page = min_per_page 11 | if per_page > max_per_page: 12 | per_page = max_per_page 13 | return per_page 14 | -------------------------------------------------------------------------------- /farbox_bucket/utils/web_utils/response.py: -------------------------------------------------------------------------------- 1 | #coding: utf8 2 | from __future__ import absolute_import 3 | from flask import make_response, Response 4 | from farbox_bucket.utils.data import json_dumps 5 | 6 | STATUS_MESSAGES = { 7 | 200: 'ok', 8 | 500: 'Server Error', 9 | 503: 'Wait for a Moment, and Try Again.', 10 | 400: 'Bad Request', 11 | 401: 'Auth error.', 12 | 404: 'Page not Found or this Request is not Allowed', 13 | } 14 | 15 | 16 | def jsonify(data): 17 | try: 18 | data = json_dumps(data, indent=4) 19 | except: 20 | data = json_dumps(dict(error='json_error')) 21 | response = Response(data, mimetype='application/json') 22 | return response 23 | 24 | 25 | 26 | def json_with_status_code(code, message=''): 27 | message = message or STATUS_MESSAGES.get(code, '') 28 | result = dict(code=code, message=message) 29 | response = make_response(jsonify(result)) 30 | response.status_code = code 31 | return response 32 | --------------------------------------------------------------------------------