# Lsshu
_admin@lsshu.cn_
## 安装
```shell
pip install lsshu-cms
```
## 使用
_1、在项目根目录新建文件 **`main.py`**_
```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from lsshu.oauth.main import router as router_oauth
app = FastAPI(
title='Base API Docs',
description='Base API接口文档',
version='1.0.0'
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(router_oauth, prefix="/api")
if __name__ == '__main__':
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
```
_2、在项目根目录新建python包 **`app`** 在包app下打开文件 **`__init__.py`**_
```python
if __name__ == '__main__':
from lsshu.oauth.model import Model, Engine
Model.metadata.create_all(Engine) # 创建表结构
APP_PERMISSIONS = []
from config import OAUTH_ADMIN_USERS
from lsshu.internal.helpers import store_permissions, init_user_and_password
store_permissions(APP_PERMISSIONS) # 初始化权限
init_user_and_password(OAUTH_ADMIN_USERS) # 初始化授权用户
```
_3、在包app下新建python包 **`demo`** 在包demp下新建文件 **`model.py`**_
```python
from sqlalchemy import Column, String
from lsshu.internal.db import Model
from lsshu.internal.method import plural
name = plural(__name__.capitalize())
table_name = name.replace('.', '_')
permission = {"name": "Demo", "scope": name, "action": [{"name": "de", "scope": "de"}]}
class Models(Model):
""" 模型 """
__tablename__ = table_name
name = Column(String(15), nullable=False, unique=True, comment="名称")
```
_4、在包demo下新建文件 **`crud.py`**_
```python
from lsshu.demo.model import Models
from lsshu.internal.crud import BaseCRUD
class CRUD(BaseCRUD):
"""表操作"""
params_model = Models
```
_5、在包demo下新建文件 **`schema.py`**_
```python
from datetime import datetime
from typing import Optional, List
from pydantic import BaseModel
from lsshu.internal.schema import SchemasPaginate
class SchemasResponse(BaseModel):
"""模型 返回"""
id: int
name: Optional[str] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
orm_mode = True
class SchemasStoreUpdate(BaseModel):
"""模型 提交"""
name: Optional[str] = None
class SchemasPaginateItem(SchemasPaginate):
items: List[SchemasResponse]
class SchemasParams(BaseModel):
pass
```
_6、在包demo下新建文件 **`main.py`**_
```python
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status, Security
from sqlalchemy.orm import Session
from lsshu.internal.db import dbs
from lsshu.internal.depends import model_screen_params, model_post_screen_params, auth_user
from lsshu.internal.schema import ModelScreenParams, Schemas
from lsshu.oauth.user.schema import SchemasOAuthScopes
from .crud import CRUD
from .model import name as name
from .schema import SchemasResponse, SchemasParams, SchemasPaginateItem, SchemasStoreUpdate
router = APIRouter(tags=["Demo"])
scopes = [name, ]
@router.get('/{}'.format(name), name="get {}".format(name))
async def get_models(db: Session = Depends(dbs), params: ModelScreenParams = Depends(model_screen_params),
auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.list" % name])):
"""
:param db:
:param params:
:param auth:
:return:
"""
db_model_list = CRUD.paginate(db=db, screen_params=params)
return Schemas(data=SchemasPaginateItem(**db_model_list))
@router.post('/{}.post'.format(name), name="post {}".format(name))
async def post_models(db: Session = Depends(dbs), params: ModelScreenParams = Depends(model_post_screen_params),
auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.list" % name])):
"""
:param db:
:param params:
:param auth:
:return:
"""
db_model_list = CRUD.paginate(db=db, screen_params=params)
return Schemas(data=SchemasPaginateItem(**db_model_list))
@router.get('/{}.params'.format(name), name="get {}".format(name))
async def params_models(db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.list" % name])):
"""
:param db:
:param auth:
:return:
"""
data = {}
return Schemas(data=SchemasParams(**data))
@router.get('/{}/{{pk}}'.format(name), name="get {}".format(name))
async def get_model(pk: int, db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.get" % name])):
"""
:param pk:
:param db:
:param auth:
:return:
"""
db_model = CRUD.first(db=db, pk=pk)
if db_model is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="{} not found".format(name.capitalize()))
return Schemas(data=SchemasResponse(**db_model))
@router.post('/{}'.format(name), name="get {}".format(name))
async def store_model(item: SchemasStoreUpdate, db: Session = Depends(dbs),
auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.store" % name])):
"""
:param item:
:param db:
:param auth:
:return:
"""
db_model = CRUD.first(db=db, where=("name", item.name))
if db_model is not None:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
detail="{} already registered".format(name.capitalize()))
bool_model = CRUD.store(db=db, item=item)
return Schemas(data=SchemasResponse(**bool_model.to_dict()))
@router.put("/{}/{{pk}}".format(name), name="update {}".format(name))
async def update_put_model(pk: int, item: SchemasStoreUpdate, db: Session = Depends(dbs),
auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.update" % name])):
"""
:param pk:
:param item:
:param db:
:param auth:
:return:
"""
db_model = CRUD.first(db=db, pk=pk)
if db_model is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="{} not found".format(name.capitalize()))
bool_model = CRUD.update(db=db, pk=pk, item=item)
return Schemas(data=SchemasResponse(**bool_model.to_dict()))
@router.patch("/{}/{{pk}}".format(name), name="update {}".format(name))
async def update_patch_model(pk: int, item: SchemasStoreUpdate, db: Session = Depends(dbs),
auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.update" % name])):
"""
:param pk:
:param item:
:param db:
:param auth:
:return:
"""
db_model = CRUD.first(db=db, pk=pk)
if db_model is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="{} not found".format(name.capitalize()))
bool_model = CRUD.update(db=db, pk=pk, item=item, exclude_unset=True)
return Schemas(data=SchemasResponse(**bool_model.to_dict()))
@router.delete("/{}/{{pk}}".format(name), name="delete {}".format(name))
async def delete_model(pk: int, db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.delete" % name])):
"""
:param pk:
:param db:
:param auth:
:return:
"""
bool_model = CRUD.delete(db=db, pk=pk)
return Schemas(data=bool_model)
@router.delete("/{}".format(name), name="deletes {}".format(name))
async def delete_role_models(pks: List[int], db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + ["%s.delete" % name])):
"""
:param pks:
:param db:
:param auth:
:return:
"""
bool_model = CRUD.delete(db=db, pks=pks)
return Schemas(data=bool_model)
```
_7、在根目录下新建文件 **`config.py`**_
```python
import os
ROOT_PATH = os.path.dirname(__file__)
# API 接口返回数据
SCHEMAS_SUCCESS_CODE: int = 0
SCHEMAS_SUCCESS_STATUS: str = 'success'
SCHEMAS_SUCCESS_MESSAGE: str = '数据请求成功!'
SCHEMAS_ERROR_CODE: int = 1
SCHEMAS_ERROR_STATUS: str = 'error'
SCHEMAS_ERROR_MESSAGE: str = '数据请求失败!'
# 站点
HOST_URL: str = ""
# 上传目录
UPLOAD_NAME: str = "static"
UPLOAD_DIR: str = "static"
UPLOAD_URI: str = "/static"
# OAuth 授权相关
OAUTH_DEFAULT_TAGS: list = ['OAuth']
OAUTH_LOGIN_SCOPES: str = "login"
OAUTH_TOKEN_URI: str = "/token"
OAUTH_TOKEN_URL: str = "/api%s" % OAUTH_TOKEN_URI
OAUTH_SCOPES_URI: str = "/scopes"
OAUTH_TOKEN_SCOPES: dict = {
OAUTH_LOGIN_SCOPES: OAUTH_LOGIN_SCOPES.capitalize()
}
OAUTH_SECRET_KEY: str = "4a876f7766d1a0e9d97231089be927e38d6dea09233ad212f056b7f1a75cd41d"
OAUTH_ALGORITHM: str = "HS256"
OAUTH_ACCESS_TOKEN_EXPIRE_MINUTES: int = 300
OAUTH_OAUTH_ROUTER: dict = {}
# 超级管理员 账号:密码
OAUTH_ADMIN_USERS: dict = {
"admin": "admin"
}
# 数据库相关
DB_SQLALCHEMY_DATABASE_URL: str = "sqlite:///{}".format(os.path.join(ROOT_PATH, 'db.sqlite3'))
# DB_SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:root@192.168.1.3:3306/ic" # ic 数据库
# echo=True表示引擎将用repr()函数记录所有语句及其参数列表到日志
# 由于SQLAlchemy是多线程,指定check_same_thread=False来让建立的对象任意线程都可使用。这个参数只在用SQLite数据库时设置
DB_ENGINE_KWARGS: dict = {
# "echo": True,
# "encoding": 'utf-8',
# "pool_pre_ping": True,
# "pool_size": 100,
# "pool_recycle": 3600,
# "max_overflow": 100,
"connect_args": {
'check_same_thread': False,
# "charset": "utf8mb4"
}
}
# 在SQLAlchemy中,CRUD都是通过会话(session)进行的,所以我们必须要先创建会话,每一个SessionLocal实例就是一个数据库session
# flush()是指发送数据库语句到数据库,但数据库不一定执行写入磁盘;commit()是指提交事务,将变更保存到数据库文件
DB_SESSION_MAKER_KWARGS: dict = {
"autoflush": False,
"autocommit": False,
"expire_on_commit": True
}
```
_8、在根目录下新建文件 **`.gitignore`**_
```gitignore
.idea
Desktop.ini
db.sqlite3
.DS_Store
*__pycache__*
```
### docker 部署
_1、在根目录下新建文件 **`Dockerfile`**_
```Dockerfile
FROM python:3.8
WORKDIR /app
EXPOSE 80
RUN mkdir -p /app && pip install lsshu-cms
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--reload"]
```
_2、创建镜像并运行_
```shell
docker build -t project_name . && docker run -d --name project_name -v /projects/project_path:/app -p 49000:80 project_name
```
_@project_name 项目名称; /projects/project_path 项目所在的目录; 49000 宿主机端口; 80 容器端口;_
_3、删除容器和镜像_
```shell
docker stop project_name && docker rm project_name && docker rmi project_name
```
### nginx 部署
```nginx
http {
upstream project_server {
server 0.0.0.0:49000 weight=1;
}
server{
listen 80;
server_name project.com;
index index.html index.htm;
root /projects/project_path/dist;
try_files $uri $uri/ /index.html;
location /static/ {
alias /projects/project_path/static/; #静态资源路径
}
location ~/api|/docs|/openapi.json
{
proxy_pass http://project_server;
# 配置websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 其它代理
proxy_set_header Http_Referer $http_referer;
proxy_set_header Host $host:$server_port;
proxy_set_header X-real-ip $remote_addr;
}
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md|app|main\.py|config\.py)
{
return 404;
}
}
}
```
_@root /projects/project_path/dist 其中`dist` 可为前端打包的目录;其它根据自身增减_
Raw data
{
"_id": null,
"home_page": "https://github.com/lsshu/fastapi-cms",
"name": "lsshu-cms",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "",
"keywords": "",
"author": "Lsshu",
"author_email": "admin@lsshu.cn",
"download_url": "https://files.pythonhosted.org/packages/55/a5/83278f95eaa0f66f628f8763754404812aebf929722352b639403ac4cf84/lsshu-cms-2.1.0.tar.gz",
"platform": null,
"description": "# Lsshu \n_admin@lsshu.cn_\n\n## \u5b89\u88c5\n```shell\npip install lsshu-cms\n```\n\n## \u4f7f\u7528\n_1\u3001\u5728\u9879\u76ee\u6839\u76ee\u5f55\u65b0\u5efa\u6587\u4ef6 **`main.py`**_ \n```python\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom lsshu.oauth.main import router as router_oauth\n\napp = FastAPI(\n title='Base API Docs',\n description='Base API\u63a5\u53e3\u6587\u6863',\n version='1.0.0'\n)\napp.add_middleware(\n CORSMiddleware,\n allow_origins=[\"*\"],\n allow_credentials=True,\n allow_methods=[\"*\"],\n allow_headers=[\"*\"],\n)\napp.include_router(router_oauth, prefix=\"/api\")\nif __name__ == '__main__':\n import uvicorn\n\n uvicorn.run(\"main:app\", host=\"0.0.0.0\", port=8000, reload=True)\n```\n\n_2\u3001\u5728\u9879\u76ee\u6839\u76ee\u5f55\u65b0\u5efapython\u5305 **`app`** \u5728\u5305app\u4e0b\u6253\u5f00\u6587\u4ef6 **`__init__.py`**_ \n```python\nif __name__ == '__main__':\n from lsshu.oauth.model import Model, Engine\n\n Model.metadata.create_all(Engine) # \u521b\u5efa\u8868\u7ed3\u6784\n\n APP_PERMISSIONS = []\n\n from config import OAUTH_ADMIN_USERS\n from lsshu.internal.helpers import store_permissions, init_user_and_password\n\n store_permissions(APP_PERMISSIONS) # \u521d\u59cb\u5316\u6743\u9650\n init_user_and_password(OAUTH_ADMIN_USERS) # \u521d\u59cb\u5316\u6388\u6743\u7528\u6237\n```\n\n_3\u3001\u5728\u5305app\u4e0b\u65b0\u5efapython\u5305 **`demo`** \u5728\u5305demp\u4e0b\u65b0\u5efa\u6587\u4ef6 **`model.py`**_ \n```python\nfrom sqlalchemy import Column, String\n\nfrom lsshu.internal.db import Model\nfrom lsshu.internal.method import plural\n\nname = plural(__name__.capitalize())\ntable_name = name.replace('.', '_')\npermission = {\"name\": \"Demo\", \"scope\": name, \"action\": [{\"name\": \"de\", \"scope\": \"de\"}]}\n\n\nclass Models(Model):\n \"\"\" \u6a21\u578b \"\"\"\n __tablename__ = table_name\n name = Column(String(15), nullable=False, unique=True, comment=\"\u540d\u79f0\")\n```\n\n_4\u3001\u5728\u5305demo\u4e0b\u65b0\u5efa\u6587\u4ef6 **`crud.py`**_ \n```python\nfrom lsshu.demo.model import Models\nfrom lsshu.internal.crud import BaseCRUD\n\n\nclass CRUD(BaseCRUD):\n \"\"\"\u8868\u64cd\u4f5c\"\"\"\n params_model = Models\n```\n\n_5\u3001\u5728\u5305demo\u4e0b\u65b0\u5efa\u6587\u4ef6 **`schema.py`**_ \n```python\nfrom datetime import datetime\nfrom typing import Optional, List\n\nfrom pydantic import BaseModel\n\nfrom lsshu.internal.schema import SchemasPaginate\n\n\nclass SchemasResponse(BaseModel):\n \"\"\"\u6a21\u578b \u8fd4\u56de\"\"\"\n id: int\n name: Optional[str] = None\n created_at: Optional[datetime] = None\n updated_at: Optional[datetime] = None\n\n class Config:\n orm_mode = True\n\n\nclass SchemasStoreUpdate(BaseModel):\n \"\"\"\u6a21\u578b \u63d0\u4ea4\"\"\"\n name: Optional[str] = None\n\n\nclass SchemasPaginateItem(SchemasPaginate):\n items: List[SchemasResponse]\n\n\nclass SchemasParams(BaseModel):\n pass\n```\n_6\u3001\u5728\u5305demo\u4e0b\u65b0\u5efa\u6587\u4ef6 **`main.py`**_ \n```python\nfrom typing import List\n\nfrom fastapi import APIRouter, Depends, HTTPException, status, Security\nfrom sqlalchemy.orm import Session\n\nfrom lsshu.internal.db import dbs\nfrom lsshu.internal.depends import model_screen_params, model_post_screen_params, auth_user\nfrom lsshu.internal.schema import ModelScreenParams, Schemas\nfrom lsshu.oauth.user.schema import SchemasOAuthScopes\n\nfrom .crud import CRUD\nfrom .model import name as name\nfrom .schema import SchemasResponse, SchemasParams, SchemasPaginateItem, SchemasStoreUpdate\n\nrouter = APIRouter(tags=[\"Demo\"])\nscopes = [name, ]\n\n\n@router.get('/{}'.format(name), name=\"get {}\".format(name))\nasync def get_models(db: Session = Depends(dbs), params: ModelScreenParams = Depends(model_screen_params),\n auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.list\" % name])):\n \"\"\"\n :param db:\n :param params:\n :param auth:\n :return:\n \"\"\"\n db_model_list = CRUD.paginate(db=db, screen_params=params)\n return Schemas(data=SchemasPaginateItem(**db_model_list))\n\n\n@router.post('/{}.post'.format(name), name=\"post {}\".format(name))\nasync def post_models(db: Session = Depends(dbs), params: ModelScreenParams = Depends(model_post_screen_params),\n auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.list\" % name])):\n \"\"\"\n :param db:\n :param params:\n :param auth:\n :return:\n \"\"\"\n db_model_list = CRUD.paginate(db=db, screen_params=params)\n return Schemas(data=SchemasPaginateItem(**db_model_list))\n\n\n@router.get('/{}.params'.format(name), name=\"get {}\".format(name))\nasync def params_models(db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.list\" % name])):\n \"\"\"\n :param db:\n :param auth:\n :return:\n \"\"\"\n\n data = {}\n return Schemas(data=SchemasParams(**data))\n\n\n@router.get('/{}/{{pk}}'.format(name), name=\"get {}\".format(name))\nasync def get_model(pk: int, db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.get\" % name])):\n \"\"\"\n :param pk:\n :param db:\n :param auth:\n :return:\n \"\"\"\n db_model = CRUD.first(db=db, pk=pk)\n if db_model is None:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"{} not found\".format(name.capitalize()))\n return Schemas(data=SchemasResponse(**db_model))\n\n\n@router.post('/{}'.format(name), name=\"get {}\".format(name))\nasync def store_model(item: SchemasStoreUpdate, db: Session = Depends(dbs),\n auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.store\" % name])):\n \"\"\"\n :param item:\n :param db:\n :param auth:\n :return:\n \"\"\"\n db_model = CRUD.first(db=db, where=(\"name\", item.name))\n if db_model is not None:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,\n detail=\"{} already registered\".format(name.capitalize()))\n bool_model = CRUD.store(db=db, item=item)\n return Schemas(data=SchemasResponse(**bool_model.to_dict()))\n\n\n@router.put(\"/{}/{{pk}}\".format(name), name=\"update {}\".format(name))\nasync def update_put_model(pk: int, item: SchemasStoreUpdate, db: Session = Depends(dbs),\n auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.update\" % name])):\n \"\"\"\n :param pk:\n :param item:\n :param db:\n :param auth:\n :return:\n \"\"\"\n db_model = CRUD.first(db=db, pk=pk)\n if db_model is None:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"{} not found\".format(name.capitalize()))\n bool_model = CRUD.update(db=db, pk=pk, item=item)\n return Schemas(data=SchemasResponse(**bool_model.to_dict()))\n\n\n@router.patch(\"/{}/{{pk}}\".format(name), name=\"update {}\".format(name))\nasync def update_patch_model(pk: int, item: SchemasStoreUpdate, db: Session = Depends(dbs),\n auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.update\" % name])):\n \"\"\"\n :param pk:\n :param item:\n :param db:\n :param auth:\n :return:\n \"\"\"\n db_model = CRUD.first(db=db, pk=pk)\n if db_model is None:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"{} not found\".format(name.capitalize()))\n bool_model = CRUD.update(db=db, pk=pk, item=item, exclude_unset=True)\n return Schemas(data=SchemasResponse(**bool_model.to_dict()))\n\n\n@router.delete(\"/{}/{{pk}}\".format(name), name=\"delete {}\".format(name))\nasync def delete_model(pk: int, db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.delete\" % name])):\n \"\"\"\n :param pk:\n :param db:\n :param auth:\n :return:\n \"\"\"\n bool_model = CRUD.delete(db=db, pk=pk)\n return Schemas(data=bool_model)\n\n\n@router.delete(\"/{}\".format(name), name=\"deletes {}\".format(name))\nasync def delete_role_models(pks: List[int], db: Session = Depends(dbs), auth: SchemasOAuthScopes = Security(auth_user, scopes=scopes + [\"%s.delete\" % name])):\n \"\"\"\n :param pks:\n :param db:\n :param auth:\n :return:\n \"\"\"\n bool_model = CRUD.delete(db=db, pks=pks)\n return Schemas(data=bool_model)\n```\n_7\u3001\u5728\u6839\u76ee\u5f55\u4e0b\u65b0\u5efa\u6587\u4ef6 **`config.py`**_ \n```python\nimport os\n\nROOT_PATH = os.path.dirname(__file__)\n# API \u63a5\u53e3\u8fd4\u56de\u6570\u636e\nSCHEMAS_SUCCESS_CODE: int = 0\nSCHEMAS_SUCCESS_STATUS: str = 'success'\nSCHEMAS_SUCCESS_MESSAGE: str = '\u6570\u636e\u8bf7\u6c42\u6210\u529f\uff01'\nSCHEMAS_ERROR_CODE: int = 1\nSCHEMAS_ERROR_STATUS: str = 'error'\nSCHEMAS_ERROR_MESSAGE: str = '\u6570\u636e\u8bf7\u6c42\u5931\u8d25\uff01'\n\n# \u7ad9\u70b9\nHOST_URL: str = \"\"\n\n# \u4e0a\u4f20\u76ee\u5f55\nUPLOAD_NAME: str = \"static\"\nUPLOAD_DIR: str = \"static\"\nUPLOAD_URI: str = \"/static\"\n\n# OAuth \u6388\u6743\u76f8\u5173\nOAUTH_DEFAULT_TAGS: list = ['OAuth']\nOAUTH_LOGIN_SCOPES: str = \"login\"\n\nOAUTH_TOKEN_URI: str = \"/token\"\nOAUTH_TOKEN_URL: str = \"/api%s\" % OAUTH_TOKEN_URI\nOAUTH_SCOPES_URI: str = \"/scopes\"\nOAUTH_TOKEN_SCOPES: dict = {\n OAUTH_LOGIN_SCOPES: OAUTH_LOGIN_SCOPES.capitalize()\n}\nOAUTH_SECRET_KEY: str = \"4a876f7766d1a0e9d97231089be927e38d6dea09233ad212f056b7f1a75cd41d\"\nOAUTH_ALGORITHM: str = \"HS256\"\nOAUTH_ACCESS_TOKEN_EXPIRE_MINUTES: int = 300\nOAUTH_OAUTH_ROUTER: dict = {}\n\n# \u8d85\u7ea7\u7ba1\u7406\u5458 \u8d26\u53f7:\u5bc6\u7801\nOAUTH_ADMIN_USERS: dict = {\n \"admin\": \"admin\"\n}\n\n# \u6570\u636e\u5e93\u76f8\u5173\nDB_SQLALCHEMY_DATABASE_URL: str = \"sqlite:///{}\".format(os.path.join(ROOT_PATH, 'db.sqlite3'))\n# DB_SQLALCHEMY_DATABASE_URL = \"mysql+pymysql://root:root@192.168.1.3:3306/ic\" # ic \u6570\u636e\u5e93\n\n# echo=True\u8868\u793a\u5f15\u64ce\u5c06\u7528repr()\u51fd\u6570\u8bb0\u5f55\u6240\u6709\u8bed\u53e5\u53ca\u5176\u53c2\u6570\u5217\u8868\u5230\u65e5\u5fd7\n# \u7531\u4e8eSQLAlchemy\u662f\u591a\u7ebf\u7a0b\uff0c\u6307\u5b9acheck_same_thread=False\u6765\u8ba9\u5efa\u7acb\u7684\u5bf9\u8c61\u4efb\u610f\u7ebf\u7a0b\u90fd\u53ef\u4f7f\u7528\u3002\u8fd9\u4e2a\u53c2\u6570\u53ea\u5728\u7528SQLite\u6570\u636e\u5e93\u65f6\u8bbe\u7f6e\nDB_ENGINE_KWARGS: dict = {\n # \"echo\": True,\n # \"encoding\": 'utf-8',\n # \"pool_pre_ping\": True,\n # \"pool_size\": 100,\n # \"pool_recycle\": 3600,\n # \"max_overflow\": 100,\n \"connect_args\": {\n 'check_same_thread': False,\n # \"charset\": \"utf8mb4\"\n }\n}\n# \u5728SQLAlchemy\u4e2d\uff0cCRUD\u90fd\u662f\u901a\u8fc7\u4f1a\u8bdd(session)\u8fdb\u884c\u7684\uff0c\u6240\u4ee5\u6211\u4eec\u5fc5\u987b\u8981\u5148\u521b\u5efa\u4f1a\u8bdd\uff0c\u6bcf\u4e00\u4e2aSessionLocal\u5b9e\u4f8b\u5c31\u662f\u4e00\u4e2a\u6570\u636e\u5e93session\n# flush()\u662f\u6307\u53d1\u9001\u6570\u636e\u5e93\u8bed\u53e5\u5230\u6570\u636e\u5e93\uff0c\u4f46\u6570\u636e\u5e93\u4e0d\u4e00\u5b9a\u6267\u884c\u5199\u5165\u78c1\u76d8\uff1bcommit()\u662f\u6307\u63d0\u4ea4\u4e8b\u52a1\uff0c\u5c06\u53d8\u66f4\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u6587\u4ef6\nDB_SESSION_MAKER_KWARGS: dict = {\n \"autoflush\": False,\n \"autocommit\": False,\n \"expire_on_commit\": True\n}\n```\n\n_8\u3001\u5728\u6839\u76ee\u5f55\u4e0b\u65b0\u5efa\u6587\u4ef6 **`.gitignore`**_ \n```gitignore\n.idea\nDesktop.ini\ndb.sqlite3\n.DS_Store\n*__pycache__*\n```\n\n\n### docker \u90e8\u7f72\n_1\u3001\u5728\u6839\u76ee\u5f55\u4e0b\u65b0\u5efa\u6587\u4ef6 **`Dockerfile`**_ \n```Dockerfile\nFROM python:3.8\nWORKDIR /app\nEXPOSE 80\nRUN mkdir -p /app && pip install lsshu-cms\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"80\", \"--reload\"]\n```\n_2\u3001\u521b\u5efa\u955c\u50cf\u5e76\u8fd0\u884c_ \n```shell\ndocker build -t project_name . && docker run -d --name project_name -v /projects/project_path:/app -p 49000:80 project_name\n```\n_@project_name \u9879\u76ee\u540d\u79f0; /projects/project_path \u9879\u76ee\u6240\u5728\u7684\u76ee\u5f55; 49000 \u5bbf\u4e3b\u673a\u7aef\u53e3; 80 \u5bb9\u5668\u7aef\u53e3;_\n\n_3\u3001\u5220\u9664\u5bb9\u5668\u548c\u955c\u50cf_ \n```shell\ndocker stop project_name && docker rm project_name && docker rmi project_name\n```\n\n### nginx \u90e8\u7f72\n```nginx\nhttp {\n upstream project_server { \n server 0.0.0.0:49000 weight=1;\n }\n server{\n listen 80;\n server_name project.com;\n index index.html index.htm;\n root /projects/project_path/dist;\n \n try_files $uri $uri/ /index.html;\n \n location /static/ {\n alias /projects/project_path/static/; #\u9759\u6001\u8d44\u6e90\u8def\u5f84\n }\n \n location ~/api|/docs|/openapi.json\n {\n proxy_pass http://project_server;\n # \u914d\u7f6ewebsocket\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";\n # \u5176\u5b83\u4ee3\u7406\n proxy_set_header Http_Referer $http_referer;\n proxy_set_header Host $host:$server_port; \n proxy_set_header X-real-ip $remote_addr;\n }\n location ~ ^/(\\.user.ini|\\.htaccess|\\.git|\\.svn|\\.project|LICENSE|README.md|app|main\\.py|config\\.py)\n {\n return 404;\n }\n }\n}\n```\n_@root /projects/project_path/dist \u5176\u4e2d`dist` \u53ef\u4e3a\u524d\u7aef\u6253\u5305\u7684\u76ee\u5f55\uff1b\u5176\u5b83\u6839\u636e\u81ea\u8eab\u589e\u51cf_\n",
"bugtrack_url": null,
"license": "GPLv3",
"summary": "FastAPI \u5f00\u53d1\u7684CMS",
"version": "2.1.0",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4943df85e1796036fb3ba1a4f3478ac7287ca513ce6ac8bf5265fdcc5f51b72e",
"md5": "90677d684692951d87e08d772231beb5",
"sha256": "939d6d563627bffbe0db21c945eccf06147bef8f188fa091cd6e50384aece68d"
},
"downloads": -1,
"filename": "lsshu_cms-2.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "90677d684692951d87e08d772231beb5",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 37447,
"upload_time": "2023-04-06T10:00:56",
"upload_time_iso_8601": "2023-04-06T10:00:56.410959Z",
"url": "https://files.pythonhosted.org/packages/49/43/df85e1796036fb3ba1a4f3478ac7287ca513ce6ac8bf5265fdcc5f51b72e/lsshu_cms-2.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "55a583278f95eaa0f66f628f8763754404812aebf929722352b639403ac4cf84",
"md5": "53ecfbb0f9781ff8e3716489f590eafd",
"sha256": "19929efafe1c102316f15059a7aebe52a6fbb3c4d9b4cd01b7b8d09a74b3cac2"
},
"downloads": -1,
"filename": "lsshu-cms-2.1.0.tar.gz",
"has_sig": false,
"md5_digest": "53ecfbb0f9781ff8e3716489f590eafd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 29067,
"upload_time": "2023-04-06T10:00:59",
"upload_time_iso_8601": "2023-04-06T10:00:59.007508Z",
"url": "https://files.pythonhosted.org/packages/55/a5/83278f95eaa0f66f628f8763754404812aebf929722352b639403ac4cf84/lsshu-cms-2.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-04-06 10:00:59",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "lsshu",
"github_project": "fastapi-cms",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "lsshu-cms"
}