# brucelee
A mvc framework used FastApi
Simple and elegant use of FastApi in MVC mode
[Demo](https://brucelee.2rails.cn/)
### deploy one click on vercel:
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsmjkzsl%brucelee&project-name=brucelee&repository-name=brucelee)
## usage:
### install:
```sh
pip install brucelee
```
### generate a project named `myproject` in current directory
```bash
brucelee project -d myproject
```
```bash
cd myproject
```
now the project like this:
```
+---apps
+---configs
+---data
+---public
+---uploads
```
### generate app
```bash
brucelee app
[enter apps directory]
[enter app name like 'admin']
```
will generate an app in apps
### run project
```bash
brucelee run
Started server process
Waiting for application startup.
Application startup complete.
Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
```
open browser nav to http://127.0.0.1:8000 will see the web site
controller:
```python
from brucelee import api_router,api,Request,Response,BaseController,application,WebSocket,WebSocketDisconnect,UploadFile,File
from typing import Any, Dict ,List
from pydantic import conlist
application._public_auth_url = '/user/login'
application._user_auth_url = '/user/login'
@api_router(auth='public')
class TestController(BaseController):
@api.get("/user/login" )
def login(self):
""":title Login"""
redirect = self.get_param('redirect') if self.get_param('redirect') else '/'
return self.view()
@api.post("/test/verity_user",auth="none")
async def verity_user(self):
username = self['username']
password = self['password']
redirect = self['redirect']
if username and password:
#do veritied
if username in ['bruce','alice'] and password:
return self._verity_successed(username,redirect)
else:
return self._verity_error()
return self._verity_error()
@api.get("/user/logout")
def logout(self):
return self._user_logout()
@api.post("/test/upload")
async def upload_test(self, files: List[UploadFile] = File(...) ):
p = {}
for file in files:
path,url = await self._save_upload_file(file)
p[file.filename] = [path,url]
return {"files":p}
@api.get("/",auth='none' )
def home(self,request:Request):
'''
:title Home
'''
c = self.session.get('home',1)
c = c+1
self.cookies["a"] = c
if c>10:
del self.cookies["a"]
c = 0
self.session['home'] = c
text = "Hello World! I'm in FastapiMvcFramework"
routers_map = application.routers_map
routers = application.routes
return self.view()
@api.get("/xml",auth='user')
def get_legacy_data(self):
""":title XML(only bruce)"""
data = """<?xml version="1.0"?>
<shampoo>
<Header>
Apply shampoo here.
</Header>
<Body>
You'll have to use soap here.
</Body>
</shampoo>
"""
return self.view(content=data,media_type="application/xml")
@api.get("/chatgpt",auth="user")
def chatgpt(self):
"""
:title Chat(only alice)
"""
return self.view()
websockets:Dict[str,WebSocket] = {}
import pusher
pusher_client = pusher.Pusher(
app_id='1588311',
key='3eb7cc894586d11b97de',
secret='b1052d401c7d6542fc4f',
cluster='ap3',
ssl=True
)
@api_router(path="/{controller}")
class WSController(BaseController):
def __init__(self) -> None:
super().__init__()
@api.get("/" )
def ws_home(self):
""":title WebSocketDemo"""
return self.view()
@api.websocket("/chat/{client_id}")
async def websocket_endpoint(self, websocket: WebSocket,client_id: int):
await websocket.accept()
websockets[client_id]=(websocket)
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"You wrote: {data}" )
for clientid in websockets:
if client_id!=clientid:
await websockets[clientid].send_text(f"Client #{client_id} says: {data}")
except WebSocketDisconnect:
websockets.remove(websocket)
for connection in websockets:
await connection.send_text(f"Client #{client_id} left the chat")
```
home.html:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FastApi MVC Framework</title>
<link rel="stylesheet" href="home.css" />
</head>
<body>
<header style="text-align:left;display: flex;">
<h1>Python</h1>
<h4>on FastApi</h4>
</header>
<nav>
{% for item in routers_map %} {% if 'GET' in routers_map[item]['methods'] %} {% if routers_map[item]['auth']=='none' or request.session['user'] %}
<a href="{{routers_map[item]['path']}}">{{routers_map[item]['doc']
and routers_map[item]['doc']['title'] or item}}</a> {% endif %} {% endif %} {% endfor %}
<a href="#">About</a>
<a href="#">Contact</a> {% if request.session['user'] %}
<a href="/user/logout"><b>{{request.session['user']['username']}}</b>
Logout</a> {% endif %}
</nav>
<section>
<h2>Welcome to my website</h2>
<p>This is an example of a responsive design that works well on both desktop and mobile devices.</p>
<p>here is the `text` variable in class method:{{text}}</p>
<p style="color:red"><b>{{flash}}</b></p>
</section>
<footer>
<p>© 2023 My Website</p>
</footer>
</body>
</html>
```
Raw data
{
"_id": null,
"home_page": "https://github.com/smjkzsl/brucelee",
"name": "brucelee",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "web framework,mvc framework,fastapi framework",
"author": "Bruce chou",
"author_email": "smjkzsl@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/19/70/f5d6b6479a3c3f43ac380735a253de31eeffdf71be2c5015b10f9a1fe023/brucelee-1.1.0.tar.gz",
"platform": null,
"description": "# brucelee\r\nA mvc framework used FastApi\r\nSimple and elegant use of FastApi in MVC mode\r\n\r\n[Demo](https://brucelee.2rails.cn/) \r\n\r\n### deploy one click on vercel:\r\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsmjkzsl%brucelee&project-name=brucelee&repository-name=brucelee)\r\n\r\n## usage:\r\n### install:\r\n```sh\r\npip install brucelee\r\n```\r\n### generate a project named `myproject` in current directory\r\n```bash\r\nbrucelee project -d myproject\r\n```\r\n```bash\r\ncd myproject \r\n```\r\nnow the project like this:\r\n```\r\n+---apps\r\n+---configs\r\n+---data\r\n+---public\r\n+---uploads \r\n```\r\n\r\n### generate app\r\n```bash\r\nbrucelee app \r\n[enter apps directory]\r\n[enter app name like 'admin']\r\n```\r\nwill generate an app in apps\r\n### run project\r\n```bash\r\nbrucelee run\r\n\r\n Started server process \r\n Waiting for application startup.\r\n Application startup complete.\r\n Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)\r\n```\r\nopen browser nav to http://127.0.0.1:8000 will see the web site\r\n\r\ncontroller:\r\n```python\r\n\r\nfrom brucelee import api_router,api,Request,Response,BaseController,application,WebSocket,WebSocketDisconnect,UploadFile,File\r\n\r\nfrom typing import Any, Dict ,List\r\nfrom pydantic import conlist\r\n\r\napplication._public_auth_url = '/user/login'\r\napplication._user_auth_url = '/user/login'\r\n \r\n@api_router(auth='public')\r\nclass TestController(BaseController): \r\n @api.get(\"/user/login\" )\r\n def login(self):\r\n \"\"\":title Login\"\"\" \r\n redirect = self.get_param('redirect') if self.get_param('redirect') else '/' \r\n return self.view() \r\n @api.post(\"/test/verity_user\",auth=\"none\")\r\n async def verity_user(self): \r\n username = self['username']\r\n password = self['password']\r\n redirect = self['redirect']\r\n if username and password:\r\n #do veritied\r\n if username in ['bruce','alice'] and password:\r\n return self._verity_successed(username,redirect)\r\n else:\r\n return self._verity_error() \r\n return self._verity_error()\r\n \r\n @api.get(\"/user/logout\")\r\n def logout(self):\r\n return self._user_logout()\r\n \r\n @api.post(\"/test/upload\")\r\n async def upload_test(self, files: List[UploadFile] = File(...) ): \r\n p = {}\r\n for file in files:\r\n path,url = await self._save_upload_file(file)\r\n p[file.filename] = [path,url]\r\n return {\"files\":p} \r\n \r\n \r\n @api.get(\"/\",auth='none' )\r\n def home(self,request:Request): \r\n '''\r\n :title Home\r\n '''\r\n c = self.session.get('home',1)\r\n c = c+1 \r\n self.cookies[\"a\"] = c\r\n if c>10:\r\n del self.cookies[\"a\"]\r\n c = 0\r\n self.session['home'] = c\r\n text = \"Hello World! I'm in FastapiMvcFramework\"\r\n routers_map = application.routers_map\r\n routers = application.routes \r\n return self.view()\r\n \r\n @api.get(\"/xml\",auth='user')\r\n def get_legacy_data(self):\r\n \"\"\":title XML(only bruce)\"\"\"\r\n\r\n data = \"\"\"<?xml version=\"1.0\"?>\r\n <shampoo>\r\n <Header>\r\n Apply shampoo here.\r\n </Header>\r\n <Body>\r\n You'll have to use soap here.\r\n </Body>\r\n </shampoo>\r\n \"\"\"\r\n return self.view(content=data,media_type=\"application/xml\")\r\n \r\n @api.get(\"/chatgpt\",auth=\"user\")\r\n def chatgpt(self):\r\n \"\"\"\r\n :title Chat(only alice)\r\n \"\"\"\r\n return self.view()\r\n\r\n\r\n \r\n \r\nwebsockets:Dict[str,WebSocket] = {}\r\nimport pusher\r\n\r\npusher_client = pusher.Pusher(\r\n app_id='1588311',\r\n key='3eb7cc894586d11b97de',\r\n secret='b1052d401c7d6542fc4f',\r\n cluster='ap3',\r\n ssl=True\r\n)\r\n\r\n@api_router(path=\"/{controller}\")\r\nclass WSController(BaseController): \r\n def __init__(self) -> None:\r\n super().__init__()\r\n \r\n @api.get(\"/\" )\r\n def ws_home(self):\r\n \"\"\":title WebSocketDemo\"\"\"\r\n return self.view()\r\n\r\n @api.websocket(\"/chat/{client_id}\")\r\n async def websocket_endpoint(self, websocket: WebSocket,client_id: int):\r\n await websocket.accept()\r\n websockets[client_id]=(websocket)\r\n try:\r\n while True:\r\n data = await websocket.receive_text()\r\n await websocket.send_text(f\"You wrote: {data}\" )\r\n for clientid in websockets:\r\n if client_id!=clientid:\r\n await websockets[clientid].send_text(f\"Client #{client_id} says: {data}\")\r\n \r\n except WebSocketDisconnect:\r\n websockets.remove(websocket)\r\n for connection in websockets:\r\n await connection.send_text(f\"Client #{client_id} left the chat\")\r\n \r\n```\r\n\r\nhome.html:\r\n\r\n```html\r\n<!DOCTYPE html>\r\n<html>\r\n\r\n<head>\r\n <meta charset=\"utf-8\">\r\n <title>FastApi MVC Framework</title>\r\n <link rel=\"stylesheet\" href=\"home.css\" />\r\n\r\n</head>\r\n\r\n<body>\r\n <header style=\"text-align:left;display: flex;\">\r\n <h1>Python</h1>\r\n <h4>on FastApi</h4>\r\n\r\n </header>\r\n <nav>\r\n {% for item in routers_map %} {% if 'GET' in routers_map[item]['methods'] %} {% if routers_map[item]['auth']=='none' or request.session['user'] %}\r\n <a href=\"{{routers_map[item]['path']}}\">{{routers_map[item]['doc']\r\n and routers_map[item]['doc']['title'] or item}}</a> {% endif %} {% endif %} {% endfor %}\r\n\r\n <a href=\"#\">About</a>\r\n <a href=\"#\">Contact</a> {% if request.session['user'] %}\r\n <a href=\"/user/logout\"><b>{{request.session['user']['username']}}</b>\r\n Logout</a> {% endif %}\r\n </nav>\r\n <section>\r\n <h2>Welcome to my website</h2>\r\n <p>This is an example of a responsive design that works well on both desktop and mobile devices.</p>\r\n <p>here is the `text` variable in class method:{{text}}</p>\r\n <p style=\"color:red\"><b>{{flash}}</b></p>\r\n </section>\r\n <footer>\r\n <p>© 2023 My Website</p>\r\n </footer>\r\n</body>\r\n\r\n</html>\r\n```\r\n",
"bugtrack_url": null,
"license": "Apache License 2.0",
"summary": "Simple and elegant use of FastApi in MVC mode",
"version": "1.1.0",
"split_keywords": [
"web framework",
"mvc framework",
"fastapi framework"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "e133066c9ae3f1eeaa7738c4de9421bd79b8e497e239ea43042df13182cba104",
"md5": "3b00435089246e98084f0dddbab1b77f",
"sha256": "0eab911bc1585e947be57d68628a81c911493ef8f7a667f462225313afc544be"
},
"downloads": -1,
"filename": "brucelee-1.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3b00435089246e98084f0dddbab1b77f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 49392,
"upload_time": "2023-04-28T11:41:59",
"upload_time_iso_8601": "2023-04-28T11:41:59.034881Z",
"url": "https://files.pythonhosted.org/packages/e1/33/066c9ae3f1eeaa7738c4de9421bd79b8e497e239ea43042df13182cba104/brucelee-1.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "1970f5d6b6479a3c3f43ac380735a253de31eeffdf71be2c5015b10f9a1fe023",
"md5": "28fa3826c53d7db21fc21d6f3ca9dce9",
"sha256": "bbc0b0198b73637823e97636629a51163ca62138bb627783e3bfdc4db1a16c70"
},
"downloads": -1,
"filename": "brucelee-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "28fa3826c53d7db21fc21d6f3ca9dce9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 40869,
"upload_time": "2023-04-28T11:42:01",
"upload_time_iso_8601": "2023-04-28T11:42:01.101406Z",
"url": "https://files.pythonhosted.org/packages/19/70/f5d6b6479a3c3f43ac380735a253de31eeffdf71be2c5015b10f9a1fe023/brucelee-1.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-04-28 11:42:01",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "smjkzsl",
"github_project": "brucelee",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "brucelee"
}