{"id":13815313,"url":"https://github.com/fy0/fpage","last_synced_at":"2026-01-22T18:27:26.790Z","repository":{"id":31871280,"uuid":"35439436","full_name":"fy0/fpage","owner":"fy0","description":"Tornado project generator. Start a project with tornado, mako/jinjia2 and sqlalchemy/peewee in a minute.","archived":false,"fork":false,"pushed_at":"2018-07-12T16:03:31.000Z","size":73,"stargazers_count":234,"open_issues_count":1,"forks_count":43,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-05-15T07:41:54.109Z","etag":null,"topics":["fpage","mako","peewee","python","sqlalchemy","tornado","xsrf"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fy0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-05-11T17:38:48.000Z","updated_at":"2024-11-28T02:21:39.000Z","dependencies_parsed_at":"2022-08-30T14:31:22.627Z","dependency_job_id":null,"html_url":"https://github.com/fy0/fpage","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/fy0/fpage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fy0%2Ffpage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fy0%2Ffpage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fy0%2Ffpage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fy0%2Ffpage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fy0","download_url":"https://codeload.github.com/fy0/fpage/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fy0%2Ffpage/sbom","scorecard":{"id":415491,"data":{"date":"2025-08-11","repo":{"name":"github.com/fy0/fpage","commit":"b45e7962166372bcb795cedc69180ce0c6b63126"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Do What The F*ck You Want To Public License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T23:43:14.544Z","repository_id":31871280,"created_at":"2025-08-18T23:43:14.544Z","updated_at":"2025-08-18T23:43:14.544Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28668011,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T17:07:18.858Z","status":"ssl_error","status_checked_at":"2026-01-22T17:05:02.040Z","response_time":144,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fpage","mako","peewee","python","sqlalchemy","tornado","xsrf"],"created_at":"2024-08-04T04:03:19.034Z","updated_at":"2026-01-22T18:27:26.743Z","avatar_url":"https://github.com/fy0.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# fpage\n\n[![Travis](https://travis-ci.org/fy0/fpage.svg?branch=master)](https://travis-ci.org/fy0/fpage)\n[![Code Climate](https://codeclimate.com/github/fy0/fpage/badges/gpa.svg)](https://codeclimate.com/github/fy0/fpage)\n\nFPage 是一个传统的(即前后端分离之前)tornado项目生成器(CLI)。\n\n能够自动创建基于 tornado + mako/jinja2 + peewee/sqlalchemy 的项目。\n\n实例可参考 [StoryNote](https://github.com/fy0/storynote) [MyCTF](https://github.com/fy0/myctf) 等项目。\n\n\n[English](README_EN.md)\n\n## 使用\n\n通过 pip：\n\n```bash\npip install fpage\n\nfpage new [项目名]\n```\n\n或者\n\nclone后直接使用：\n\n```bash\npython fpage.py new [项目名]\n```\n\n接下来按照向导走，首先输入项目名。\n\n然后选择一个模板引擎（**M**ako/**J**inja2/**T**ornado）\n\n其次是ORM选择（**P**eewee/**S**QLChemy）\n\n最后输入 y 确认\n\n生成的目录就是你需要的，你可以试一下 python app.py 来运行他，然后访问 http://127.0.0.1:9000 来查看效果\n\n\n实例：\n```bash\n# fpage new test_project\n\nProject Name (test_project):\nTemplate Engine [M/J/T]:\nDatabase ORM [P/S]:\n\n   Project Name: test_project\nTemplate Engine: mako\n   Database ORM: peewee\n\nSure (Y/n)?\nComplete.\n\nTo get started:\n\n    cd test_project\n    python app.py\n\nServed at http://localhost:9000\n```\n\n\n\n## 特性\n\n* 基于 tornado \n\n* MVT 架构(Model, View, Template)\n\n* 兼容 python 3 \u0026 python 2\n\n* 合理的安全性支持 (secure cookie, xsrf)\n\n* 支持 flask 风格的 url 路由装饰器 @route\n\n* 简单 session 支持（基于 secure cookie）  \n\n* 可选择模板引擎 mako 或 jinjia2 或 tornado 默认，已做好配置  \n\n* 模板预定义模板变量：req static url_for csrf_token/xsrf_token config  \n\n* 集成 sqlalchemy/peewee 支持（二选一）  \n\n* 集成消息闪现功能（类似 django 中 messages 或 flask 中 flash）  \n\n* 集成简单的用户系统\n\n* 自动生成页面标题\n\n* 可选的 Peewee 序列化扩展组件\n\n* 内置分页工具\n\n\n## 目录结构\n\n* model 数据库交互\n\n* view 逻辑\n\n* templates 模板目录\n\n* lib 存放一些全局使用的工具类\n\n\n## 特性说明\n\n* **支持 flask 风格的 url 装饰器 @route**\n  ```python\n  from view import route, url_for, View\n  \n  @route('/')\n  class Index(View):\n      def get(self):\n          self.render()\n  \n      def post(self):\n          pass\n          \n  @route('/about', name='about')\n  class About(View):\n      def get(self):\n          self.render()\n\n  ```\n\n* **简单 session 支持（基于 secure cookie）**  \n  ```python\n  @route('/')\n  class Index(View):\n      def get(self):\n          self.session['test'] = 'session test 1'\n          del self.session['test']\n          self.session['test'] = 'session test 2'\n          self.render(s=self.session['test'])\n  ```\n  \n* **可选择模板引擎 mako 或 jinjia2 或 tornado 默认，已做好配置**  \n  ```mako\n  \u003cbody\u003e\n      ${self.body()}\n      \u003c%block name=\"script\"/\u003e\n  \u003c/body\u003e\n  ```\n  ```jinja\n  \u003cbody\u003e\n      {% block body %}{% endblock %}\n      {% block script %}{% endblock %}\n  \u003c/body\u003e\n  ```\n\n* **模板预定义模板变量：req static url_for csrf_token/xsrf_token**  \n  req -\u003e request object\n  ```mako\n    ${ req.current_user }\n  ```\n  static -\u003e static file\n  ```mako\n    \u003cscript src=\"${ static('js/main.js') }\"\u003e\u003c/script\u003e\n    \u003clink rel=\"stylesheet\" href=\"${ static('css/style.css') }\"\u003e\n  ```\n  url_for -\u003e url reverse\n  ```mako\n    \u003cp\u003e\u003ca href=\"${ url_for('jump') }\"\u003eJump Page\u003c/a\u003e\u003c/p\u003e\n    \u003cp\u003e\u003ca href=\"${ url_for('about') }\"\u003eAbout Page\u003c/a\u003e\u003c/p\u003e\n  ```\n  csrf_token -\u003e self.xsrf_form_html()\n  ```mako\n    \u003cform method=\"post\" class=\"am-form\"\u003e\n        ${csrf_token}\n    \u003c/form\u003e\n  ```\n\n* **集成 sqlalchemy/peewee 支持（二选一）**  \n  config\n  ```python\n  DATABASE_URI = \"sqlite:///database.db\"\n  ```\n  sqlalchemy\n  ```python\n  from model import BaseModel\n  from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean\n  \n  \n  class Test(BaseModel):\n      __tablename__ = 'test'\n      id = Column(Integer, primary_key=True, autoincrement=True)\n      test = Column(String)\n  ```\n  peewee\n  ```python\n  from peewee import *\n  from model import BaseModel\n  \n  \n  class Test(BaseModel):\n      test = TextField()\n  ```\n\n* **集成消息闪现功能（类似 django 中 messages 或 flask 中 flash）**  \n  view\n  ```python\n  @route('/jump_test', name='jump')\n  class Jump(View):\n      def get(self):\n          self.messages.error('Message Test: Error!!')\n          self.redirect(url_for('about'))\n  ```\n  template\n  ```mako\n  % for msg in get_messages():\n      % if msg.tag == 'success':\n          \u003cdiv class=\"ui-green\"\u003e\n              ${msg.txt}\n          \u003c/div\u003e\n      % elif msg.tag == 'error':\n          \u003cdiv class=\"ui-red\"\u003e\n              ${msg.txt}\n          \u003c/div\u003e\n      % endif\n  % endfor\n  ```\n\n* **自动生成页面标题**  \n\n  例如：config.TITLE = 'FPage'\n  \n  渲染模板时写入参数 page_title\n  ```python\n  self.render(page_title=page_title('测试板块', '社区')\n  ```\n  \n  于是此页面网页标题就是：测试板块 » 社区 » FPage\n\n* **内置分页工具**\n\n    model.pagination_peewee / model.pagination_sqlalchemy\n    \n    参数大致如此：\n    ```python\n    def pagination(count_all, query, page_size, cur_page=1, nearby=2):\n        pass\n    ```\n\n    输出大致如此：\n    ```python\n    {\n        'cur_page': cur_page,\n        'prev_page': prev_page,\n        'next_page': next_page,\n\n        'first_page': first_page,\n        'last_page': last_page,\n\n        'page_numbers': list(items),\n        'page_count': page_count,\n\n        'items': [...],\n        'info': {\n            'page_size': page_size,\n            'count_all': count_all,\n        }\n    }\n    ```\n\n\n## 更新\n\n### ver 1.2 update 2017.09.04\n\n* 加入了分页工具\n\n* 加入 pypi 软件源(早该如此……)\n\n\n### ver 1.2alpha update 2016\n\n* 现在 config 也作为模板中的一个预定义变量\n\n* 加入新的 View 基类： AjaxLoginView\n\n* 新的辅助函数：page_title，用来自定义标题\n\n* peewee 的 BaseModel 加入了几个工具函数：to_dict（转为字典）、get_by_pk（根据主键取项，无则返回None）、exists_by_pk（根据主键判断是否存在）\n\n* 修正了“记住密码”选项无效的问题\n\n* 用户注册增加了“再次输入密码”的校验\n\n* 登录和注册增加了参数 next，用来指定操作完成后跳转的url\n\n* 加入了自动测试\n\n\n### ver 1.1 update 2015.9.20\n\n* 加入了用户模块\n\n* 加入了与用户相关的两个View基类：LoginView（登陆后可访问） 和 NoLoginView（非登陆可访问）\n\n* 加入了一个不检查 xsrf 的基类 AjaxView\n\n* 默认 ORM 切换为 peewee\n\n* 一些小的修正\n\n\n## TODO-LIST\n\n* 不再增加新功能，一个时代已经落幕\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffy0%2Ffpage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffy0%2Ffpage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffy0%2Ffpage/lists"}