# cameo
**Repository Path**: croonyy/cameo
## Basic Information
- **Project Name**: cameo
- **Description**: cameo是一个开源项目(fastapi-admin/fastapiadmin/fastapi_admin),用于帮助开发者快速搭建fastapi项目,并且自带了一个admin应用(前后端分离),参考django-admin设计,使用tortoise-orm作为数据库操作框架,支持mysql,sqlite,postgresql,等多种数据库,实现RBAC权限管理模式。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 5
- **Forks**: 2
- **Created**: 2025-06-25
- **Last Updated**: 2026-03-30
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Cameo
## 介绍
cameo是一个开源项目(fastapi-admin/fastapiadmin/fastapi_admin),用于帮助开发者快速搭建fastapi项目,并且自带了一个admin应用(前后端分离,前端使用vue3的开源框架naive-ui-admin),参考django-admin设计,使用tortoise-orm作为数据库操作框架,支持mysql,sqlite,postgresql等多种数据库,实现RBAC权限管理模式,增删改查api自动生成。
## 项目地址
- [__github__ https://github.com/croonyy/cameo](https://github.com/croonyy/cameo)
- [__gitee__ https://gitee.com/croonyy/cameo](https://gitee.com/croonyy/cameo)
## 版本依赖
- __python__ --version:3.13.0
- __vue3__
- __Node__ -v:v22.14.0
- __npm__ -version:10.9.2
## 安装教程
前后端依赖安装
```bash
# 事先安装python3.13
# 克隆源码
git clone https://gitee.com/croonyy/cameo.git
# 进入到项目目录 cd cameo
# 安装python环境依赖,如果依赖下载慢,请自行更换pip镜像源
pip install -r requirements.txt
# 初始化数据库
aerich upgrade
#执行脚本,创建admin用户(密码可后续自行更改),和模型权限
python init_data.py
# 前端安装
# 进入到/front目录
cd front
pnpm install
```
## 运行
```bash
# 1、运行后端服务
# 如果是python虚拟环境,请先激活虚拟环境 conda activate [python_env_name]
# 进入到项目目录下,执行
python run.py
# 2、运行前端服务
# 进入到前端目录下
cd front
pnpm run dev
# 3、访问
# 访问后端接口文档
http://localhost:3014/udadmin/docs
# 访问前端页面
http://localhost:1992/
# 登录账号/密码,预置两个用户,一个管理员和一个普通用户,可自行编辑用户修改密码
admin/admin
test_user/123456
```
## 项目图片展示
###### 接口文档

###### 控制台

###### 用户列表

###### 编辑用户

###### 权限实例

###### 编辑权限

###### 创建角色

## 项目结构
```shell
├── apps # app目录,推荐一个app创建一个文件夹,可参考源码样例,也可自行组织app代码结构
├── app1 # 样例app
├── ...
├── udadmin # admin应用
├── ...
├── config
├── settings.py # 项目配置文件
├── db
├── db.sqlite3 # 默认数据库为sqlite3,此为数据库文件
├── init
├── front # 前端根目录
├── ...
├── migrations # 数据库迁移文件,如若更换数据库,需要重新生成
├── app1 # 一个app会生成一个文件夹
├── udadmin
├── notes # 笔记
├── ...
├── static # 静态文件
├── docs-ui # 为了swagger文档从本地加载,文件本地化
├── images # 项目展示图片
├── favicon.ico
├── test # 测试代码
├── ...
├── tools # 工具代码
├── locate_print.py
├── objdoc.py
├── timer.py
├── .gitignore
├── cert.pem
├── init_data.py # 初始化数据库的用户和权限的脚本
├── key.pem
├── main.py
├── pyproject.toml
├── README.en.md
├── README.md
├── requirements.txt # python环境依赖
├── run.py # 主入口,python run.py 启动项目
├── vscode_extensions.txt # vscode 插件
```
## 使用说明
#### 请按步骤参考样例app1,熟悉步骤之后可删除app1目录,并创建自己的app
1. apps目录下创建app1(可随意命名,但请勿使用udadmin,udadmin是admin应用目录)目录
2. 创建app1/app.py,定义app应用
```python
app = FastAPI(
# root_path="/test",
title="app1",
version="1.0.0",
swagger_ui_oauth2_redirect_url="/docs/oauth2-redirect",
servers=[{"url": "http://localhost:1002", "description": "test"}],
description='
',
# docs_url="/docs",
debug=True,
openapi_tags=openapi_tags,
)
```
在主app上挂载子app也就是app1应用,参考main.py代码
```python
# 上下文管理,startup执行yield之前的代码,shutdown执行yield以下的代码
@asynccontextmanager
async def lifespan(app: FastAPI):
# 传递 TORTOISE_ORM 配置给 Tortoise.init()
await Tortoise.init(config=TORTOISE_ORM)
# 确保所有的模型都被创建(如果需要),取消注释项目启动会自己创建,
# 也可以用aerich init-db生成迁移文件并创建,或者用迁移文件aerich upgrade创建。
# await Tortoise.generate_schemas()
# 初始化数据库配置后,再注册模型,不然会找不到模型的app属性(因为app属性是在数据库init里面设置的)
# 挂载app,也要在数据库配置初始化之后,才能有模型的app名称
# 先从数据库获取信息配置,再挂载app(app里面会注册模型,才能拿到配置信息)
from apps.udadmin.app import app as admin_app
from apps.app1.app import app as app1
app.mount("/udadmin", admin_app, name="admin")
app.mount("/app1", app1, name="app1")
from apps.udadmin.utils.model_register import mr
print(f"registered models:\n{list(mr.models_info.keys())}")
yield
try:
# 销毁fastapi实例后断开数据库连接
await Tortoise.close_connections()
except Exception as e:
print("="*80)
print(e)
```
3. 创建app1/models.py,在app1/models.py里面定义模型
```python
class TestModel(Model):
id = fields.IntField(
pk=True, auto_increment=True, description="主键id", ud_name="序号"
)
big_int_field = fields.BigIntField()
binary_field = fields.BinaryField()
boolean_field = fields.BooleanField(ud_name="布尔类型")
char_enum_field = fields.CharEnumField(enums.TestEnum, description="测试枚举")
char_field = fields.CharField(max_length=255, description="字符串类型")
date_field = fields.DateField(description="日期类型")
date_time_field = fields.DatetimeField(description="日期时间类型")
decimal_field = fields.DecimalField(max_digits=10, decimal_places=2, description="小数类型")
float_field = fields.FloatField()
int_enum_field = fields.IntEnumField(
enum_type=IntEnum("IntTestEnum", {"选项1": 1, "选项2": 2, "选项3": 3})
)
int_field = fields.IntField()
json_field = fields.JSONField()
small_integer_field = fields.SmallIntField()
text_field = fields.TextField()
time_delta_field = fields.TimeDeltaField()
uuid_field = fields.UUIDField()
def __str__(self) -> str:
return f"<{self.__class__.__name__}: {self.id}>"
class Meta:
menu_name = "test模型"
table_description = "测试模型"
```
- 定义字段的时候可以加入ud_name属性,ud_name字段为前端显示字段名称,默认为字段名称,如果定义了ud_name,则前端显示ud_name字段名称
- 定义字段的时候可以加入ud_order属性,ud_order属性为前端显示字段排序,默认为999,数字越小越靠前
- 模型的 __\_\_str\_\___ 方法为模型显示名称(一般是关系名称,多对多,一对多时候显示的名称),可定义,不定义则默认为模型名称
- class Meta里面定义menu_name为前端菜单名称,table_description为前端模型描述,都可选,不填则默认为模型名称
```sh
# 迁移到数据库
# 命令行执行
aerich migrate # 生成模型修改的迁移文件
aerich upgrade # 应用迁移文件到数据库
```
4. 创建app1/ui.py,定义前端模型显示配置
```python
from apps.udadmin.utils.ui_tools import UiInfo
from . import models as md
TestModelUi = UiInfo(
model=md.TestModel,
list_display=[
"id",
"big_int_field",
"binary_field",
"boolean_field",
],
list_filter=[
"id",
"big_int_field",
"binary_field",
"boolean_field",
"char_enum_field",
"char_field",
"date_field",
"date_time_field",
"decimal_field",
"float_field",
"int_enum_field",
"int_field",
"json_field",
"small_integer_field",
"text_field",
"time_delta_field",
"uuid_field",
],
search_fields=[
"char_field",
],
)
```
- model为模型类
- list_display为列表显示字段,默认为所有字段,可以不定义
- list_filter为列表过滤字段,默认为空
- search_fields为搜索字段,默认为空
- 其他配置请参考udadmin.ui.UiInfo 定义
5. 在app1/app.py注册模型和ui配置
```python
from apps.udadmin.utils.model_register import mr
from apps.app1 import models as md
from apps.app1 import ui
mr.register(app, md.TestModel, ui_info=ui.TestModelUi)
```
- app为app1/app.py定义的app实例
- md.TestModel为app1/models.py定义的模型类
- ui_info为app1/ui.py定义的ui配置
6. 如果需要开发其他api,可在app1目录下创建view文件夹(推荐),或者自行组织文件结构书写代码定义接口
###### 上述步骤结束后,前端会自动生成模型的增删改查页面,管理员可直接看到,其他用户需要定义权限(前端页面【认证与授权/权限实例模型】里面定义)并赋权才看得到,权限格式参考admin应用已有的模型权限
## 生产部署
#### 1.前端
打包前端,进入到/front 目录下执行:
```bash
pnpm run build
```
命令执行完后会在/front目录生成一个 __dist__ 文件夹,里面就是打包好的所有前端代码文件
静态文件部署可使用本项目的后端服务,或者更好的使用 nginx 静态文件服务
##### (方式一)使用本项目后端服务:
因为后端已经启用了静态文件服务,/main.py里面的静态文件服务配置如下
```python
...
# 挂载静态文件服务
app.mount(
"/static",
StaticFiles(directory=os.path.join(BASE_DIR, "static"), html=True),
name="static",
)
...
# 重定向
@app.get("/")
async def index():
return RedirectResponse(url="/admin")
# # 前端入口
# 所有admin应用的路由都要定位到前端主页
# 捕获所有其他路径并返回 index.html
@app.get("/admin/{full_path:path}", response_class=HTMLResponse)
async def catch_all(request: Request, full_path: str):
return templates.TemplateResponse("/dist/index.html", {"request": request})
```
所以生产环境只需要将dist文件夹放到/static下,并修改 __/static/dist/app.config.js__ 文件里面的 __VITE_GLOB_API_URL_PREFIX__ 的值为你的后端服务的根路径(此步骤也可在打包前修改/front/.env.production 里面的VITE_GLOB_API_URL_PREFIX值来实现)
```js
// 文件 /static/dist/app.config.js
window.__PRODUCTION__CAMEO__CONF__={
"VITE_GLOB_APP_TITLE":"Cameo",
"VITE_GLOB_APP_SHORT_NAME":"Cameo",
"VITE_GLOB_API_URL":"",
"VITE_GLOB_API_URL_PREFIX":"http://localhost:3014",
"VITE_GLOB_UPLOAD_URL":"",
"VITE_GLOB_IMG_URL":""};
Object.freeze(window.__PRODUCTION__CAMEO__CONF__);
Object.defineProperty(window,"__PRODUCTION__CAMEO__CONF__",{configurable:false,writable:false,});
```
然后正常访问后端服务地址,即可访问前端页面
##### (方式二)使用nginx:
步骤类似上述方式,只是用nginx做静态资源服务,配置好以下两个路由(nginx语法来实现下述表述):
- nginx的 __/admin/...full_path...__ 路由到 /static/dist/index.html文件
- nginx的 __/static/...full_path...__ 路由到 /static/dist/...full_path...文件
```nginx
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/webroot; # 替换为你的实际路径
# 处理 /static/ 下的静态文件请求
location /static/ {
alias /path/to/your/dist/; # 指向 dist 目录
try_files $uri $uri/ =404;
# 可选:设置缓存头
expires 7d;
access_log off;
}
# 处理 /admin/ 下的所有路由(前端路由)
location /admin/ {
alias /path/to/your/dist/; # 同样指向 dist 目录
try_files $uri $uri/ /index.html;
# 如果你的前端入口文件是 index.html(在 dist 目录下)
# 可以这样写:
# try_files $uri $uri/ /dist/index.html;
}
# 可选:处理根路径或其他路由
location / {
try_files $uri $uri/ /index.html;
}
# 其他配置(如 gzip、SSL 等)...
}
```
#### 2.后端
后端启动
```shell
python run.py
```
可以使用nohup命令后台运行
修改生产配置/run.py文件里面的run_server方法中的uvicorn.run函数的参数
```python
...
def run_server():
uvicorn.run("main:app", host="localhost", port=3014, reload=True)
...
# 还可以增加启动参数,请参考uvicorn.run的参数说明
```
## 关于RBAC权限管理系统
__udadmin__ 应用的模型权限 __权限实例__ 已经由 init_data.py 脚本生成,自定义应用创建的模型需要手动创建权限实例,参考 udadmin已有的权限实例在ui界面上来创建,定义的权限实例可以由 __角色__ (可在角色管理模型里面新建)带给用户,也可以直接赋予用户,具体请参考项目里面的 __认证与授权__ 模块的模型
# 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request