Compare commits
No commits in common. "da5ac866d9ac1a6543a28286b7138a4e643a0efd" and "0fb97181535f0de90215cb079a5d0c8ede24978e" have entirely different histories.
da5ac866d9
...
0fb9718153
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 zy7y
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
163
README.md
163
README.md
@ -1,134 +1,67 @@
|
|||||||
# Mini RBAC 系统
|
# Mini RBAC
|
||||||
`Python(FastAPI)`、`VUE3`实现,仅保留核心权限控制的极简后台管理。
|
`Python(FastAPI)`、`VUE3`实现,仅保留核心权限控制的极简后台管理。
|
||||||
![index](./imgs/index.png)
|
![index](./imgs/index.png)
|
||||||
|
|
||||||
相关视频:[https://www.bilibili.com/video/BV1mP411H7PL/](https://www.bilibili.com/video/BV1mP411H7PL/)
|
[演示地址-前端: http://49.232.203.244:1856](http://49.232.203.244:1856/login)
|
||||||
|
|
||||||
## 项目说明文档
|
[演示地址-接口地址: http://49.232.203.244:1855/docs](http://49.232.203.244:1855/docs)
|
||||||
|
|
||||||
## 1. 数据库模型
|
## 功能
|
||||||
|
### 权限控制
|
||||||
### 1.1 用户表 (sys_user)
|
- [x] 前端菜单权限控制
|
||||||
- username: 账号 (唯一)
|
- [x] 前端路由权限控制
|
||||||
- nickname: 姓名
|
- [x] 前端按钮权限控制
|
||||||
- password: 密码
|
- [x] 后端接口权限控制
|
||||||
|
### 业务功能
|
||||||
### 1.2 角色表 (sys_role)
|
- [x] 登录、退出、切换角色
|
||||||
- name: 角色名称
|
- [x] 用户管理 -> 新增、编辑、删除、查询
|
||||||
- remark: 角色描述
|
- [x] 角色管理 -> 新增、编辑、删除、查询
|
||||||
|
- [x] 菜单管理 -> 新增、编辑、删除、查询
|
||||||
### 1.3 菜单表 (sys_menu)
|
|
||||||
- name: 名称
|
|
||||||
- icon: 菜单图标
|
|
||||||
- path: 菜单url
|
|
||||||
- type: 菜单类型 (0目录 1组件 2按钮 3数据)
|
|
||||||
- component: 组件地址
|
|
||||||
- pid: 父id
|
|
||||||
- identifier: 权限标识 (如user:add)
|
|
||||||
- api: 接口地址
|
|
||||||
- method: 接口请求方式
|
|
||||||
|
|
||||||
### 1.4 关系表
|
|
||||||
- 用户角色关系表 (sys_user_role)
|
|
||||||
- uid: 用户id
|
|
||||||
- rid: 角色id
|
|
||||||
|
|
||||||
- 角色菜单关系表 (sys_role_menu)
|
|
||||||
- rid: 角色id
|
|
||||||
- mid: 菜单id
|
|
||||||
|
|
||||||
## 2. API接口
|
|
||||||
|
|
||||||
### 2.1 认证相关
|
|
||||||
- POST /login 用户登录
|
|
||||||
- GET /ws WebSocket系统信息
|
|
||||||
|
|
||||||
### 2.2 用户管理
|
|
||||||
- GET /user 用户列表
|
|
||||||
- POST /user/query 用户查询
|
|
||||||
- POST /user 用户新增
|
|
||||||
- DELETE /user/{pk} 用户删除
|
|
||||||
- GET /user/{pk} 用户信息
|
|
||||||
- PUT /user/{pk} 用户更新
|
|
||||||
- PUT /user/role/{rid} 用户切换角色
|
|
||||||
|
|
||||||
### 2.3 角色管理
|
|
||||||
- GET /role 角色列表
|
|
||||||
- POST /role/query 角色查询
|
|
||||||
- POST /role 角色新增
|
|
||||||
- GET /role/{rid}/menu 查询角色权限
|
|
||||||
- DELETE /role/{pk} 角色删除
|
|
||||||
- PUT /role/{pk} 角色更新
|
|
||||||
|
|
||||||
### 2.4 菜单管理
|
|
||||||
- POST /menu 菜单新增
|
|
||||||
- GET /menu 菜单列表
|
|
||||||
- DELETE /menu/{pk} 菜单删除
|
|
||||||
- PUT /menu/{pk} 菜单更新
|
|
||||||
|
|
||||||
## 3. 技术栈
|
|
||||||
|
|
||||||
### 3.1 后端
|
|
||||||
- Python 3.x
|
|
||||||
- FastAPI (Web框架)
|
|
||||||
- Tortoise ORM (ORM框架)
|
|
||||||
- Uvicorn (ASGI服务器)
|
|
||||||
- Gunicorn (WSGI服务器)
|
|
||||||
- bcrypt (密码加密)
|
|
||||||
- python-jose (JWT认证)
|
|
||||||
|
|
||||||
### 3.2 前端
|
|
||||||
- Vue 3 (前端框架)
|
|
||||||
- Pinia (状态管理)
|
|
||||||
- Vue Router (路由管理)
|
|
||||||
- Ant Design Vue (UI组件库)
|
|
||||||
- ECharts (图表库)
|
|
||||||
- Vite (构建工具)
|
|
||||||
- Axios (HTTP客户端)
|
|
||||||
|
|
||||||
## 4. 网页页面
|
|
||||||
|
|
||||||
### 4.1 主要页面结构
|
|
||||||
- 登录页 (login)
|
|
||||||
- 主页面 (main)
|
|
||||||
- 仪表盘 (dashboard)
|
|
||||||
- 系统管理
|
|
||||||
- 用户管理 (user)
|
|
||||||
- 角色管理 (role)
|
|
||||||
- 菜单管理 (menu)
|
|
||||||
- 测试页面 (test)
|
|
||||||
|
|
||||||
### 4.2 页面功能
|
|
||||||
- 登录认证
|
|
||||||
- 用户管理:增删改查、角色分配
|
|
||||||
- 角色管理:增删改查、权限分配
|
|
||||||
- 菜单管理:增删改查
|
|
||||||
- 仪表盘:系统信息展示
|
|
||||||
- WebSocket实时数据更新
|
|
||||||
|
|
||||||
### 4.3权限控制
|
|
||||||
- 前端菜单权限控制
|
|
||||||
- 前端路由权限控制
|
|
||||||
- 前端按钮权限控制
|
|
||||||
- 后端接口权限控制
|
|
||||||
|
|
||||||
|
|
||||||
## 备注
|
# 使用框架&工具
|
||||||
原项目地址:[https://github.com/zy7y/mini-rbac](https://github.com/zy7y/mini-rbac)
|
## 前端 `node 16.15 LTS`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@kangc/v-md-editor": "^2.3.15",
|
||||||
|
"ant-design-vue": "^3.2.12",
|
||||||
|
"axios": "^0.27.2",
|
||||||
|
"echarts": "^5.3.3",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"normalize.css": "^8.0.1",
|
||||||
|
"pinia": "^2.0.21",
|
||||||
|
"pinia-plugin-persistedstate": "^2.2.0",
|
||||||
|
"vue": "^3.2.38",
|
||||||
|
"vue-router": "^4.1.5",
|
||||||
|
"vite": "^3.0.9"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 后端 `Python 3.9.7`
|
||||||
|
```
|
||||||
|
bcrypt==4.0.0
|
||||||
|
fastapi==0.82.0
|
||||||
|
passlib==1.7.4
|
||||||
|
pytest==7.1.3
|
||||||
|
python-jose==3.3.0
|
||||||
|
requests==2.28.1
|
||||||
|
uvicorn==0.18.3
|
||||||
|
tortoise-orm==0.19.2
|
||||||
|
websockets==10.3
|
||||||
|
```
|
||||||
|
|
||||||
本人学习全栈项目时clone了这个项目作为参考,同时优化了readme文件,未违反mit开源协议
|
|
||||||
|
|
||||||
## 图片展示
|
|
||||||
|
<details>
|
||||||
|
<summary>更多图片</summary>
|
||||||
|
|
||||||
### 页面
|
### 页面
|
||||||
|
|
||||||
![login](./imgs/login.png)
|
![login](./imgs/login.png)
|
||||||
![change](./imgs//select.png)
|
![change](./imgs//select.png)
|
||||||
![user](./imgs/useradd.png)
|
![user](./imgs/useradd.png)
|
||||||
![role](./imgs/menuadd.png)
|
![role](./imgs/menuadd.png)
|
||||||
![menu](./imgs/roleadd.png)
|
![menu](./imgs/roleadd.png)
|
||||||
|
|
||||||
### 接口
|
### 接口
|
||||||
|
|
||||||
![user-api](./imgs/user-api.png)
|
![user-api](./imgs/user-api.png)
|
||||||
![role-menu](./imgs/role-menu-api.png)
|
![role-menu](./imgs/role-menu-api.png)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
@ -1,139 +0,0 @@
|
|||||||
# Created by .ignore support plugin (hsz.mobi)
|
|
||||||
### Python template
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@ -11,6 +11,7 @@ __pycache__/
|
|||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
|
dist/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
# 第一阶段:构建镜像
|
|
||||||
FROM python:3.9-slim-buster AS build
|
|
||||||
WORKDIR /app
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
|
||||||
echo 'Asia/Shanghai' >/etc/timezone && \
|
|
||||||
pip install --no-cache-dir --user -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
|
|
||||||
COPY . .
|
|
||||||
# 第二阶段:运行镜像
|
|
||||||
FROM python:3.9-slim-buster
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=build /root/.local /root/.local
|
|
||||||
COPY --from=build /app .
|
|
||||||
ENV PATH=/root/.local/bin:$PATH
|
|
||||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|
@ -144,5 +144,5 @@ async def has_permissions(rid, is_menu=False):
|
|||||||
sql = f"""
|
sql = f"""
|
||||||
select {filters}
|
select {filters}
|
||||||
FROM sys_menu as m, sys_role_menu as srm WHERE m.id = srm.mid
|
FROM sys_menu as m, sys_role_menu as srm WHERE m.id = srm.mid
|
||||||
AND srm.rid = (?) and m.status != 9 and srm.status !=9 order by m.id asc"""
|
AND srm.rid = (?) and m.status != 9 order by m.id asc"""
|
||||||
return await RoleMenuDao.raw_sql(sql, [rid])
|
return await RoleMenuDao.raw_sql(sql, [rid])
|
||||||
|
@ -2,10 +2,9 @@ from fastapi.exceptions import HTTPException
|
|||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
|
|
||||||
from core.log import logger
|
|
||||||
|
|
||||||
|
|
||||||
class TokenAuthFailure(HTTPException):
|
class TokenAuthFailure(HTTPException):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -21,18 +20,4 @@ async def http_exception(request: Request, exc: HTTPException):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def global_exception(request: Request, exc):
|
exception_handlers = {HTTPException: http_exception}
|
||||||
if hasattr(request.state, "request_id"):
|
|
||||||
request_id = request.state.request_id
|
|
||||||
else:
|
|
||||||
request_id = None
|
|
||||||
logger.info("request_id 获取失败 请确认对应APIRouter使用了route_class=LogRoute ")
|
|
||||||
logger.exception(f"{request_id} Exception Log: {exc}")
|
|
||||||
return JSONResponse({
|
|
||||||
"msg": str(exc),
|
|
||||||
"code": 500,
|
|
||||||
"data": None
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
exception_handlers = {Exception: global_exception, HTTPException: http_exception}
|
|
||||||
|
@ -1,15 +1 @@
|
|||||||
import logging
|
from loguru import logger
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
# 创建一个 FileHandler,用于写入日志文件
|
|
||||||
handler = logging.FileHandler('log_file.log')
|
|
||||||
handler.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
# 设置日志格式
|
|
||||||
formatter = logging.Formatter("[%(asctime)s] %(levelname)s %(message)s")
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
|
|
||||||
# 添加 FileHandler 到日志处理器中
|
|
||||||
logger.addHandler(handler)
|
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
import time
|
|
||||||
import uuid
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from fastapi.middleware import Middleware
|
from fastapi.middleware import Middleware
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.routing import APIRoute
|
|
||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
from core.log import logger
|
from core.log import logger
|
||||||
|
|
||||||
|
|
||||||
# fix: 中间件没法获取到request 请求体数据 响应体
|
|
||||||
class CustomRequestLogMiddleware(BaseHTTPMiddleware):
|
class CustomRequestLogMiddleware(BaseHTTPMiddleware):
|
||||||
async def dispatch(self, request, call_next):
|
async def dispatch(self, request, call_next):
|
||||||
logger.info(
|
logger.info(
|
||||||
@ -24,28 +16,8 @@ class CustomRequestLogMiddleware(BaseHTTPMiddleware):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class LogRoute(APIRoute):
|
|
||||||
def get_route_handler(self) -> Callable:
|
|
||||||
original_route_handler = super().get_route_handler()
|
|
||||||
|
|
||||||
async def custom_route_handler(request: Request) -> Response:
|
|
||||||
request_id = str(uuid.uuid4())
|
|
||||||
request.state.request_id = request_id
|
|
||||||
logger.info(f"{request_id} Request Log {request.client} {request.method}"
|
|
||||||
f" {request.url} {request.headers}\n {await request.body()}")
|
|
||||||
before = time.time()
|
|
||||||
response: Response = await original_route_handler(request)
|
|
||||||
duration = time.time() - before
|
|
||||||
response.headers["X-Response-Time"] = str(duration)
|
|
||||||
logger.info(f"{request_id} Response Log {duration}s {response.headers}\n"
|
|
||||||
f" {response.body.decode('utf-8')}")
|
|
||||||
return response
|
|
||||||
|
|
||||||
return custom_route_handler
|
|
||||||
|
|
||||||
|
|
||||||
middlewares = [
|
middlewares = [
|
||||||
# Middleware(CustomRequestLogMiddleware),
|
Middleware(CustomRequestLogMiddleware),
|
||||||
Middleware(
|
Middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=["*"],
|
||||||
|
@ -2,6 +2,7 @@ from core.dbhelper import DbHelper
|
|||||||
|
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
|
|
||||||
filter_del = {"status__not": 9}
|
filter_del = {"status__not": 9}
|
||||||
|
|
||||||
def __init__(self, dao: DbHelper):
|
def __init__(self, dao: DbHelper):
|
||||||
|
@ -20,6 +20,7 @@ def list_to_tree(
|
|||||||
menu_map = {menu["id"]: menu for menu in menus}
|
menu_map = {menu["id"]: menu for menu in menus}
|
||||||
arr = []
|
arr = []
|
||||||
for menu in menus:
|
for menu in menus:
|
||||||
|
|
||||||
# 有父级
|
# 有父级
|
||||||
if mid := menu.get(parent_flag):
|
if mid := menu.get(parent_flag):
|
||||||
# 有 子项的情况
|
# 有 子项的情况
|
||||||
@ -78,7 +79,7 @@ def load_routers(
|
|||||||
kwargs = dict(router=router_obj, dependencies=depends)
|
kwargs = dict(router=router_obj, dependencies=depends)
|
||||||
app.include_router(**kwargs)
|
app.include_router(**kwargs)
|
||||||
|
|
||||||
logger.info("♻️开始扫描路由。")
|
logger.info("开始扫描路由。")
|
||||||
if depends is None:
|
if depends is None:
|
||||||
depends = []
|
depends = []
|
||||||
if is_init:
|
if is_init:
|
||||||
@ -98,9 +99,9 @@ def load_routers(
|
|||||||
|
|
||||||
for route in app.routes:
|
for route in app.routes:
|
||||||
try:
|
try:
|
||||||
logger.info(
|
logger.debug(
|
||||||
f"🦌{route.path}, {route.methods}, {route.__dict__.get('summary')}"
|
f"{route.path}, {route.methods}, {route.__dict__.get('summary')}"
|
||||||
)
|
)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
logger.info("®️路由注册完成✅。")
|
logger.info("👌路由注册完成✅。")
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
bcrypt==4.0.1
|
bcrypt==4.0.0
|
||||||
fastapi==0.94.0
|
fastapi==0.85.0
|
||||||
gunicorn==20.1.0
|
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
pytest==7.2.2
|
pytest==7.1.3
|
||||||
python-jose==3.3.0
|
python-jose==3.3.0
|
||||||
requests==2.28.2
|
requests==2.28.1
|
||||||
tortoise-orm==0.19.3
|
tortoise-orm==0.19.2
|
||||||
uvicorn==0.21.0
|
uvicorn==0.18.3
|
||||||
websockets==10.4
|
websockets==10.3
|
||||||
|
loguru==0.6.0
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
from fastapi import APIRouter, WebSocket
|
from fastapi import APIRouter, WebSocket
|
||||||
|
|
||||||
from core.middleware import LogRoute
|
|
||||||
from schemas import common as BaseSchema
|
from schemas import common as BaseSchema
|
||||||
from service import auth as AuthService
|
from service import auth as AuthService
|
||||||
|
|
||||||
router = APIRouter(tags=["公共"], route_class=LogRoute)
|
router = APIRouter(tags=["公共"])
|
||||||
|
|
||||||
|
|
||||||
LoginResult = BaseSchema.Response[BaseSchema.LoginResult]
|
LoginResult = BaseSchema.Response[BaseSchema.LoginResult]
|
||||||
|
|
||||||
|
|
||||||
@router.post("/login", summary="登录")
|
@router.post("/login", summary="登录", response_model=LoginResult)
|
||||||
async def login(data: BaseSchema.LoginForm) -> LoginResult:
|
async def login(data: BaseSchema.LoginForm):
|
||||||
return await AuthService.user_login(data)
|
return await AuthService.user_login(data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from core.middleware import LogRoute
|
|
||||||
from schemas import common as BaseSchema
|
from schemas import common as BaseSchema
|
||||||
from schemas import menu as MenuSchema
|
from schemas import menu as MenuSchema
|
||||||
from service.menu import service as MenuService
|
from service.menu import service as MenuService
|
||||||
|
|
||||||
router = APIRouter(prefix="/menu", tags=["菜单管理"], route_class=LogRoute)
|
router = APIRouter(prefix="/menu", tags=["菜单管理"])
|
||||||
|
|
||||||
Response = BaseSchema.Response
|
Response = BaseSchema.Response
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="菜单新增")
|
@router.post("", summary="菜单新增", response_model=Response[MenuSchema.MenuRead])
|
||||||
async def menu_add(data: MenuSchema.MenuIn) -> Response[MenuSchema.MenuRead]:
|
async def menu_add(data: MenuSchema.MenuIn):
|
||||||
return await MenuService.create_item(data)
|
return await MenuService.create_item(data)
|
||||||
|
|
||||||
|
|
||||||
@router.get("", summary="菜单列表")
|
@router.get("", summary="菜单列表", response_model=Response)
|
||||||
async def menu_arr() -> Response:
|
async def menu_arr():
|
||||||
return await MenuService.get_items()
|
return await MenuService.get_items()
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{pk}", summary="菜单删除")
|
@router.delete("/{pk}", summary="菜单删除", response_model=Response)
|
||||||
async def menu_del(pk: int) -> Response:
|
async def menu_del(pk: int):
|
||||||
return await MenuService.delete_item(pk)
|
return await MenuService.delete_item(pk)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{pk}", summary="菜单更新")
|
@router.put("/{pk}", summary="菜单更新", response_model=Response)
|
||||||
async def menu_put(pk: int, data: MenuSchema.MenuIn) -> Response:
|
async def menu_put(pk: int, data: MenuSchema.MenuIn):
|
||||||
"""更新菜单"""
|
"""更新菜单"""
|
||||||
return await MenuService.update_item(pk, data)
|
return await MenuService.update_item(pk, data)
|
||||||
|
@ -1,46 +1,46 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Query
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from core.middleware import LogRoute
|
|
||||||
from schemas import common as BaseSchema
|
from schemas import common as BaseSchema
|
||||||
from schemas import role as RoleSchema
|
from schemas import role as RoleSchema
|
||||||
from schemas.common import QueryData
|
|
||||||
from service.role import service as RoleService
|
from service.role import service as RoleService
|
||||||
|
|
||||||
router = APIRouter(prefix="/role", tags=["角色管理"], route_class=LogRoute)
|
router = APIRouter(prefix="/role", tags=["角色管理"])
|
||||||
|
|
||||||
Response = BaseSchema.Response
|
Response = BaseSchema.Response
|
||||||
ListAll = BaseSchema.ListAll
|
ListAll = BaseSchema.ListAll
|
||||||
|
|
||||||
role_list_schema = ListAll[List[RoleSchema.RoleRead]]
|
role_list_schema = ListAll[list[RoleSchema.RoleRead]]
|
||||||
|
|
||||||
|
|
||||||
@router.get("", summary="角色列表")
|
@router.get("", summary="角色列表", response_model=Response[role_list_schema])
|
||||||
async def role_list(query: QueryData = Depends()) -> Response[role_list_schema]:
|
async def role_list(
|
||||||
return await RoleService.get_items(query.offset, query.limit)
|
offset: int = Query(default=1, description="偏移量-页码"),
|
||||||
|
limit: int = Query(default=10, description="数据量"),
|
||||||
|
):
|
||||||
|
return await RoleService.get_items(offset, limit)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/query", summary="角色查询")
|
@router.post("/query", summary="角色查询", response_model=Response[role_list_schema])
|
||||||
async def role_query(query: RoleSchema.RoleQuery) -> Response[role_list_schema]:
|
async def role_query(query: RoleSchema.RoleQuery):
|
||||||
return await RoleService.query_items(query)
|
return await RoleService.query_items(query)
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="角色新增")
|
@router.post("", summary="角色新增", response_model=Response[RoleSchema.RoleInfo])
|
||||||
async def role_create(data: RoleSchema.RoleIn) -> Response[RoleSchema.RoleInfo]:
|
async def role_create(data: RoleSchema.RoleIn):
|
||||||
return await RoleService.create_item(data)
|
return await RoleService.create_item(data)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{rid}/menu", summary="查询角色拥有权限")
|
@router.get("/{rid}/menu", summary="查询角色拥有权限", response_model=Response)
|
||||||
async def role_has_menu(rid: int) -> Response:
|
async def role_has_menu(rid: int):
|
||||||
return await RoleService.has_tree_menus(rid)
|
return await RoleService.has_tree_menus(rid)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{pk}", summary="角色删除")
|
@router.delete("/{pk}", summary="角色删除", response_model=Response)
|
||||||
async def role_del(pk: int) -> Response:
|
async def role_del(pk: int):
|
||||||
return await RoleService.delete_item(pk)
|
return await RoleService.delete_item(pk)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{pk}", summary="角色更新")
|
@router.put("/{pk}", summary="角色更新", response_model=Response)
|
||||||
async def role_put(pk: int, data: RoleSchema.RoleIn) -> Response:
|
async def role_put(pk: int, data: RoleSchema.RoleIn):
|
||||||
"""更新角色"""
|
"""更新角色"""
|
||||||
return await RoleService.update_item(pk, data)
|
return await RoleService.update_item(pk, data)
|
||||||
|
@ -1,53 +1,53 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, Query
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from core.middleware import LogRoute
|
|
||||||
from core.security import check_permissions
|
from core.security import check_permissions
|
||||||
from schemas import common as BaseSchema
|
from schemas import common as BaseSchema
|
||||||
from schemas import user as UserSchema
|
from schemas import user as UserSchema
|
||||||
from schemas.common import QueryData
|
|
||||||
from service.user import service as UserService
|
from service.user import service as UserService
|
||||||
|
|
||||||
router = APIRouter(prefix="/user", tags=["用户管理"], route_class=LogRoute)
|
router = APIRouter(prefix="/user", tags=["用户管理"])
|
||||||
|
|
||||||
Response = BaseSchema.Response
|
Response = BaseSchema.Response
|
||||||
ListAll = BaseSchema.ListAll
|
ListAll = BaseSchema.ListAll
|
||||||
|
|
||||||
user_list_schema = ListAll[List[UserSchema.UserRead]]
|
user_list_schema = ListAll[list[UserSchema.UserRead]]
|
||||||
|
|
||||||
|
|
||||||
@router.get("", summary="用户列表")
|
@router.get("", summary="用户列表", response_model=Response[user_list_schema])
|
||||||
async def user_list(query: QueryData = Depends()) -> Response[user_list_schema]:
|
async def user_list(
|
||||||
return await UserService.get_items(query.offset, query.limit)
|
offset: int = Query(default=1, description="偏移量-页码"),
|
||||||
|
limit: int = Query(default=10, description="数据量"),
|
||||||
|
):
|
||||||
|
return await UserService.get_items(offset, limit)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/query", summary="用户查询")
|
@router.post("/query", summary="用户查询", response_model=Response[user_list_schema])
|
||||||
async def user_query(query: UserSchema.UserQuery) -> Response[user_list_schema]:
|
async def user_query(query: UserSchema.UserQuery):
|
||||||
return await UserService.query_items(query)
|
return await UserService.query_items(query)
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="用户新增")
|
@router.post("", summary="用户新增", response_model=Response[UserSchema.UserRead])
|
||||||
async def user_create(data: UserSchema.UserAdd) -> Response[UserSchema.UserRead]:
|
async def user_create(data: UserSchema.UserAdd):
|
||||||
return await UserService.create_item(data)
|
return await UserService.create_item(data)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{pk}", summary="用户删除")
|
@router.delete("/{pk}", summary="用户删除", response_model=Response)
|
||||||
async def user_delete(pk: int) -> Response:
|
async def user_delete(pk: int):
|
||||||
return await UserService.delete_item(pk)
|
return await UserService.delete_item(pk)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{pk}", summary="用户信息")
|
@router.get("/{pk}", summary="用户信息", response_model=Response[UserSchema.UserInfo])
|
||||||
async def user_info(pk: int) -> Response[UserSchema.UserInfo]:
|
async def user_info(pk: int):
|
||||||
return await UserService.get_item(pk)
|
return await UserService.get_item(pk)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{pk}", summary="用户更新")
|
@router.put("/{pk}", summary="用户更新", response_model=Response)
|
||||||
async def user_update(pk: int, data: UserSchema.UserPut) -> Response:
|
async def user_update(pk: int, data: UserSchema.UserPut):
|
||||||
return await UserService.update_item(pk, data)
|
return await UserService.update_item(pk, data)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/role/{rid}", summary="用户切换角色")
|
@router.put("/role/{rid}", summary="用户切换角色", response_model=Response)
|
||||||
async def user_change_role(
|
async def user_change_role(
|
||||||
rid: int, user: UserSchema.UserRead = Depends(check_permissions)
|
rid: int, user: UserSchema.UserRead = Depends(check_permissions)
|
||||||
) -> Response:
|
):
|
||||||
return await UserService.change_current_role(user.id, rid)
|
return await UserService.change_current_role(user.id, rid)
|
||||||
|
@ -26,8 +26,8 @@ class ReadBase(BaseModel):
|
|||||||
class LoginForm(BaseModel):
|
class LoginForm(BaseModel):
|
||||||
"""用户登录参数"""
|
"""用户登录参数"""
|
||||||
|
|
||||||
username: str = Field("admin", description="账号", max_length=12, min_length=3)
|
username: str = Field(..., description="账号", max_length=12, min_length=3)
|
||||||
password: str = Field("123456", description="密码", min_length=6, max_length=16)
|
password: str = Field(..., description="密码", min_length=6, max_length=16)
|
||||||
|
|
||||||
|
|
||||||
class LoginResult(BaseModel):
|
class LoginResult(BaseModel):
|
||||||
@ -41,8 +41,8 @@ class LoginResult(BaseModel):
|
|||||||
class QueryData(BaseModel):
|
class QueryData(BaseModel):
|
||||||
"""分页查询基础数据"""
|
"""分页查询基础数据"""
|
||||||
|
|
||||||
offset: int = Field(default=1, description="页码", ge=1)
|
offset: int = 1
|
||||||
limit: int = Field(default=10, description="数量", ge=1)
|
limit: int = 10
|
||||||
|
|
||||||
|
|
||||||
class ListAll(GenericModel, Generic[T]):
|
class ListAll(GenericModel, Generic[T]):
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from schemas.common import QueryData, ReadBase
|
from schemas.common import QueryData, ReadBase
|
||||||
|
|
||||||
@ -8,7 +7,7 @@ class RoleMenuIn(BaseModel):
|
|||||||
"""角色 -分配菜单id"""
|
"""角色 -分配菜单id"""
|
||||||
|
|
||||||
rid: int = Field(description="角色ID")
|
rid: int = Field(description="角色ID")
|
||||||
menus: List[int] = Field(description="菜单ID 列表")
|
menus: list[int] = Field(description="菜单ID 列表")
|
||||||
|
|
||||||
|
|
||||||
class RoleMenuRead(RoleMenuIn, ReadBase):
|
class RoleMenuRead(RoleMenuIn, ReadBase):
|
||||||
@ -21,7 +20,7 @@ class RoleBasic(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class RoleIn(RoleBasic):
|
class RoleIn(RoleBasic):
|
||||||
menus: List[int] = Field(..., description="菜单id列表")
|
menus: list[int] = Field(..., description="菜单id列表")
|
||||||
|
|
||||||
|
|
||||||
class RoleRead(RoleBasic, ReadBase):
|
class RoleRead(RoleBasic, ReadBase):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import Optional, List
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ class UserHasRole(BaseModel):
|
|||||||
class UserInfo(UserRead):
|
class UserInfo(UserRead):
|
||||||
"""用户信息模型"""
|
"""用户信息模型"""
|
||||||
|
|
||||||
roles: List[UserHasRole] = Field(..., description="用户拥有角色")
|
roles: list[UserHasRole] = Field(..., description="用户拥有角色")
|
||||||
|
|
||||||
|
|
||||||
class RoleActive(BaseModel):
|
class RoleActive(BaseModel):
|
||||||
@ -51,7 +51,7 @@ class RoleActive(BaseModel):
|
|||||||
class UserAdd(UserIn):
|
class UserAdd(UserIn):
|
||||||
"""新增用户模型"""
|
"""新增用户模型"""
|
||||||
|
|
||||||
roles: List[RoleActive] = Field(..., description="选择角色列表")
|
roles: list[RoleActive] = Field(..., description="选择角色列表")
|
||||||
|
|
||||||
|
|
||||||
class UserQuery(QueryData):
|
class UserQuery(QueryData):
|
||||||
@ -66,4 +66,4 @@ class UserPut(BaseModel):
|
|||||||
|
|
||||||
nickname: str = Field(..., description="用户昵称")
|
nickname: str = Field(..., description="用户昵称")
|
||||||
password: str = Field(..., description="密码")
|
password: str = Field(..., description="密码")
|
||||||
roles: List[RoleActive] = Field(..., description="选择角色列表")
|
roles: list[RoleActive] = Field(..., description="选择角色列表")
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
version: "3"
|
|
||||||
services:
|
|
||||||
backend:
|
|
||||||
build:
|
|
||||||
context: ./backend
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: mini-rbac
|
|
||||||
command: gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:80
|
|
||||||
ports:
|
|
||||||
- "1855:80"
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
build:
|
|
||||||
# 上下文管理, 执行frontend 下的 Dockerfile
|
|
||||||
context: ./frontend
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
# 容器名称
|
|
||||||
container_name: mini-web
|
|
||||||
# 宿主机2152 端口 绑定 容器中的80 端口
|
|
||||||
ports:
|
|
||||||
- "1856:80"
|
|
||||||
depends_on: # 依赖于backend容器,被依赖容器启动后此web容器才可启动
|
|
||||||
- backend
|
|
@ -1,129 +0,0 @@
|
|||||||
# Created by .ignore support plugin (hsz.mobi)
|
|
||||||
### Vue template
|
|
||||||
# gitignore template for Vue.js projects
|
|
||||||
#
|
|
||||||
# Recommended template: Node.gitignore
|
|
||||||
|
|
||||||
# TODO: where does this rule come from?
|
|
||||||
docs/_book
|
|
||||||
|
|
||||||
# TODO: where does this rule come from?
|
|
||||||
test/
|
|
||||||
|
|
||||||
### Node template
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverag
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v2
|
|
||||||
.yarn/cache
|
|
||||||
.yarn/unplugged
|
|
||||||
.yarn/build-state.yml
|
|
||||||
.yarn/install-state.gz
|
|
||||||
.pnp.*
|
|
@ -1,2 +1,2 @@
|
|||||||
VITE_BASE_URL = http://localhost:1855
|
VITE_BASE_URL = http://127.0.0.1:8000/api
|
||||||
VITE_WS = ws://localhost:1855/ws
|
VITE_WEBSOCKET = ws://localhost:8000/ws
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
FROM node:16.15-alpine
|
|
||||||
COPY ./ /app
|
|
||||||
WORKDIR /app
|
|
||||||
RUN npm config set registry https://registry.npmjs.org
|
|
||||||
RUN npm install
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM nginx:1.15.2-alpine
|
|
||||||
RUN mkdir /app
|
|
||||||
COPY --from=0 /app/dist /app
|
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
|
@ -1,30 +0,0 @@
|
|||||||
user nginx;
|
|
||||||
worker_processes 1;
|
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
pid /var/run/nginx.pid;
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
http {
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
access_log /var/log/nginx/access.log main;
|
|
||||||
sendfile on;
|
|
||||||
keepalive_timeout 65;
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
location / {
|
|
||||||
root /app;
|
|
||||||
index index.html;
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
frontend/package-lock.json
generated
12
frontend/package-lock.json
generated
@ -2160,9 +2160,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/decode-uri-component": {
|
"node_modules/decode-uri-component": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||||
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@ -7591,9 +7591,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decode-uri-component": {
|
"decode-uri-component": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||||
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
|
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
|
||||||
},
|
},
|
||||||
"deep-is": {
|
"deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
|
Loading…
Reference in New Issue
Block a user