├── .gitee ├── ISSUE_TEMPLATE.zh-CN.md └── PULL_REQUEST_TEMPLATE.zh-CN.md ├── .gitignore ├── LICENSE ├── README.en.md ├── README.md ├── activate.bat ├── activate.sh ├── app ├── __init__.py ├── api │ └── __init__.py ├── common │ ├── __init__.py │ ├── captcha.py │ ├── mail.py │ ├── mixin.py │ ├── ml.py │ ├── param.py │ ├── result.py │ ├── schema.py │ ├── status.py │ └── wrap.py └── modules │ ├── __init__.py │ ├── admin │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── adminroles │ ├── __init__.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── attachments │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── category │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── configs │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── iris │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── logs │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── notice_content │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── notice_content_admin │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── profiles │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── resources │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ ├── roles │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py │ └── rolesresources │ ├── __init__.py │ ├── dao.py │ ├── model.py │ ├── param.py │ ├── resource.py │ └── schema.py ├── assets └── img │ └── baoai │ ├── admin.png │ ├── ai.png │ ├── baoai_avatar_160.png │ ├── baoai_favicon_16.png │ ├── baoai_favicon_32.png │ ├── favicon.ico │ ├── know.png │ ├── logo.png │ ├── quant.png │ ├── soft.jpg │ ├── sys.png │ └── web.png ├── config.py ├── db ├── baoai.db └── baoai.sql ├── deploy ├── ReadME.md ├── baoai.ini ├── beat.ini ├── gunicorn_config.py ├── nginx.conf └── worker.ini ├── flask_restplus ├── __about__.py ├── __init__.py ├── _http.py ├── api.py ├── apidoc.py ├── cors.py ├── errors.py ├── fields.py ├── inputs.py ├── marshalling.py ├── mask.py ├── model.py ├── namespace.py ├── postman.py ├── representations.py ├── reqparse.py ├── resource.py ├── static │ ├── droid-sans.css │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── files │ │ ├── .npmignore │ │ ├── droid-sans-latin-400.woff │ │ ├── droid-sans-latin-400.woff2 │ │ ├── droid-sans-latin-700.woff │ │ └── droid-sans-latin-700.woff2 │ ├── swagger-ui-bundle.js │ ├── swagger-ui-bundle.js.map │ ├── swagger-ui-standalone-preset.js │ ├── swagger-ui-standalone-preset.js.map │ ├── swagger-ui.css │ ├── swagger-ui.css.map │ ├── swagger-ui.js │ └── swagger-ui.js.map ├── swagger.py ├── templates │ ├── swagger-ui-css.html │ ├── swagger-ui-libs.html │ └── swagger-ui.html └── utils.py ├── flask_restplus_patched ├── __init__.py ├── _http.py ├── api.py ├── http_exceptions.py ├── model.py ├── namespace.py ├── parameters.py ├── resource.py └── swagger.py ├── kill_python.sh ├── logging_config.py ├── manage.py ├── remote_debug.py ├── requirements.txt ├── requirements_talib.txt ├── run_baoai.bat ├── run_baoai.sh ├── run_www.bat ├── run_www.sh ├── static ├── ai │ └── iris │ │ ├── 3c099ad2-4ed1-11ea-a3e0-b42e9995d7c8.png │ │ └── c988de40-5330-11ea-aa9b-b42e9995d7c8.png └── uploads │ └── 2020 │ ├── 01 │ ├── 02 │ │ └── e4a77d02199947d4b0066aa9bd4e8b57.jpg │ └── 03 │ │ └── eff0930192824964b3dbc0a716c8c5a9.png │ └── 02 │ └── 14 │ └── 4fcc3b62d0f94421b56226a691eeb2fd.png ├── www ├── __init__.py ├── modules │ ├── __init__.py │ ├── about │ │ ├── __init__.py │ │ ├── task.py │ │ └── views.py │ └── main │ │ ├── __init__.py │ │ ├── task.py │ │ └── views.py └── templates │ ├── about │ └── index.html │ └── main │ └── index.html └── www_manage.py /.gitee/ISSUE_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 该问题是怎么引起的? 2 | 3 | 4 | 5 | ### 重现步骤 6 | 7 | 8 | 9 | ### 报错信息 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 相关的Issue 2 | 3 | 4 | ### 原因(目的、解决的问题等) 5 | 6 | 7 | ### 描述(做了什么,变更了什么) 8 | 9 | 10 | ### 测试用例 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | 3 | # Python 4 | *.py[cod] 5 | *.so 6 | *.egg 7 | *.egg-info 8 | dist/ 9 | build/ 10 | __pycache__/ 11 | 12 | # pyvenv 13 | venv*/ 14 | 15 | 16 | *.tar 17 | *.tar.gz 18 | 19 | *.sqlite 20 | migrations/ 21 | .vscode/ 22 | 23 | # Python 24 | *.py[cod] 25 | *.so 26 | *.egg 27 | *.egg-info 28 | dist/ 29 | build/ 30 | __pycache__/ 31 | 32 | # pyvenv 33 | venv/ 34 | 35 | *.tar 36 | *.tar.gz 37 | 38 | *.log 39 | migrations/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BaoAI 小宝人工智能和量化系统 2 | 人工智能和量化从这开始 3 | 4 |

5 | 6 | logo 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

18 | 19 | 小宝人工智能和量化平台是简洁、直观、强大的前端和后端SPA开发框架,支持国际化,以模块为基础,让WEB应用、人工智能和量化系统开发更迅速、更简单。平台包含多个模块,主要包括基于角色的权限管理基础平台(用户、角色、权限、日志、附件、配置参数、分类管理)、通知模块、自动代码产生模块、任务系统模块、内容管理系统模块、网站模块、电子手册模块、人工智能模块、图像识别模块,人脸识别模块,金融数据采集模块,大数据模块,量化交易模块等。 20 | 21 | ## 功能特点: 22 | 23 | + 超10万行代码 24 | + 平台模块化,易于开发扩展 25 | + 前端兼容多种浏览器 26 | + 兼容性好,跨平台,响应式设计 27 | + 平台二次开发学习曲线低,易上手 28 | + 国际化 29 | + 前后端代码分离 30 | + 基于H5的单页面应用(SPA) 31 | + 自动代码产生器 32 | + 自动产生API文档及测试界面 33 | + 支持多数据库和数据迁移 34 | + 强大的富文本编辑 35 | + 人工智能 36 | + 大数据网络爬虫 37 | + 金融数据采集模块 38 | + 量化分析 39 | + 完善的开发和部署工具和方案 40 | 41 | ## 下载源码 42 | 43 | BaoAI前后端分离框构,包含有前端项目和后端项目 44 | 45 | + 前端项目源码: [BaoAIFront](https://github.com/yuanbaonet/baoaifront) 46 | 47 | + 后端项目源码: [BaoAIBack](https://github.com/yuanbaonet/baoaiback) 48 | 49 | ## 文档 50 | 51 | + 手册 52 | + [BaoAI 开发手册](http://www.baoai.co/web/book?id=50) 53 | + [BaoAI 后端开发手册](http://www.baoai.co/web/book?id=48) 54 | 55 | + API 56 | + 以开发模式运行后端项目后,即可加载,如:http://localhost:8000/api, 使用Swagger UI加载。 57 | 58 | + 模块扩展 59 | + [模块](http://www.baoai.co/web/book?id=88) 60 | 61 | 62 | ## 前端和后端开发工具 63 | 64 | [Visual Studio Code](http://code.visualstudio.com) 65 | 66 | 安装插件: 67 | 68 | `Chinese (Simplified) Language Pack for Visual Studio Code` 69 | 70 | `jshint` 71 | 72 | `Python` 73 | 74 | `Git history` 75 | 76 | 77 | ## 项目后端 BaoAIBack 安装步骤 78 | 79 | 需要 [Python 3.6](http://www.python.org) 80 | 81 | ```shell 82 | # 1. 创建虚拟环境 83 | # windows, 假设项目根路径:d:/baoai/BaoaiBack/ 84 | cd d:/baoai/BaoaiBack 85 | mkdir venv 86 | cd venv 87 | python -m venv . 88 | 89 | # 运行虚拟环境 90 | d:/baoai/BaoaiBack/venv/Scripts/activate.bat 91 | cd d:/baoai/BaoaiBack 92 | 93 | # linux, 假设项目根路径:/baoai/BaoaiBack/ 94 | cd /baoai/BaoaiBack 95 | mkdir venv 96 | cd venv 97 | python -m venv . 98 | 99 | # 运行虚拟环境 100 | source /baoai/BaoaiBack/venv/bin/activate 101 | cd /baoai/BaoaiBack 102 | 103 | # 2. 安装依赖库(必须处于虚拟环境) 104 | # windows 安装依赖库 105 | python -m pip install --upgrade pip 106 | pip install -r requirements.txt 107 | # 如果下载速度慢可以采用国内镜像 108 | pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt 109 | 110 | # linux 安装依赖库 111 | python -m pip3 install --upgrade pip 112 | pip3 install -r requirements.txt 113 | # 如果下载速度慢可以采用国内镜像 114 | pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt 115 | 116 | # 3. 运行 Restful 服务 117 | # windows 118 | run_baoai.bat 119 | 120 | # linux 121 | # 默认使用gunicorn做为wsgi 122 | chmod +x run_baoai.sh 123 | ./run_baoai.sh 124 | 125 | # 4. 运行 www 服务(Jinja模块) 126 | # windows 127 | run_www.bat 128 | 129 | # linux 130 | chmod +x run_www.sh 131 | ./run_www.sh 132 | 133 | # 常用功能 134 | # 清空缓存 135 | python manage.py clean 136 | 137 | 138 | ``` 139 | ## 项目后端数据库 140 | 141 | 本项目支持绝大部门流行的关系数据库,包括:SQLite、MySQL、Postgres、Oracle、MS-SQL、SQLServer 和 Firebird。 142 | 143 | 已提供Sqlite数据库,和MySQL数据脚本文件。MySQL支持5.5及以上版本。 144 | 145 | 数据库转换无需修改代码,仅修改config.py中的SQLALCHEMY_DATABASE_URI即可。 146 | 147 | 默认使用sqlite数据库,优点是无需安装专门数据库软件,方便测试开发,生产部署请使用mysql或其它数据库软件。 148 | 149 | sqlite数据保存在 `db/baoai.db`,直接使用。 150 | 151 | mysql数据库脚本保存在 `db/baoai.mysql.sql`,需要新建数据库如baoai,然后导入脚本。 152 | 153 | 如果使用其他数据库,可以使用`Navicat Premium`工具菜单中的`数据传输`,进行不同数据库之前的数据迁移。 154 | 155 | 数据库相关操作: 156 | ``` 157 | # 数据迁移服务 158 | # 初始化 159 | python manage.py db init 160 | 161 | # 模型迁移 162 | python manage.py db migrate 163 | 164 | # 数据库脚本更新(操作数据) 165 | python manage.py db upgrade 166 | ``` 167 | 168 | ## 项目代码自动产生模块 169 | 170 | 使用自动代码产生模块,可以使字段、模型、生成数据库、前端代码、后端代码和权限配置一并可视化完成,一般项目可以零代码实现。 171 | 该部份主要包括三个扩展模块: 数据迁移模块、自动代码模型模块和自动代码产生模块 172 | 173 | 174 | ## BaoAI 小宝人工智能和量化平台系统架构 175 | logo 176 | 177 | 178 | ## BaoAI 小宝人工智能和量化平台知识体系 179 | 180 | 可用于各行业的前端和后端系统软件开发、CMS、人工智能、图像识别、人脸识别、大数据和量化投资领域等。前后端分离SPA架构,使用AngularJS/Bootstrap等前端框架实现响应式和SPA程序设计,后端主要使用Python语言,主要包括如下框架:flask提供web服务,Jinja2提供模板服务,Numpy、Pandas、Scikit-Learn、Tensorflow和Keras等实现人工智能服务,celery实现任务调度,scrapy提供网络爬虫,基于Backtrader的金融量化服务等。 181 | 182 | logo 183 | 184 | 基于BaoAI设计案例: 185 | 186 | 内容管理网站: 187 | 188 | logo 189 | 190 | 管理系统后台: 191 | 192 | logo 193 | 194 | 人工智能: 195 | 196 | logo 197 | 198 | 量化系统: 199 | 200 | logo 201 | 202 | ## 帮助 203 | 204 | + Email [703264459@qq.com](703264459@qq.com) 205 | 206 | 207 | ## 版权说明 208 | 209 | Apache2.0 210 | 211 | ## 版权说明 212 | 213 | logo 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /activate.bat: -------------------------------------------------------------------------------- 1 | venv\Scripts\activate.bat -------------------------------------------------------------------------------- /activate.sh: -------------------------------------------------------------------------------- 1 | source venv/bin/activate -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask, Blueprint 3 | from flask_sqlalchemy import SQLAlchemy 4 | from flask_cors import CORS 5 | from config import config 6 | from celery import Celery 7 | import logging_config 8 | 9 | db = SQLAlchemy() 10 | Config = config[os.getenv('FLASK_CONFIG') or 'default'] 11 | celery = None 12 | 13 | def create_app(): 14 | app = Flask(__name__, static_url_path=Config.APP_STATIC_URL_PATH, static_folder=Config.STATIC_FOLDER) 15 | app.config.from_object(Config) 16 | db.init_app(app) 17 | db.app = app 18 | 19 | # 解决禁止跨域请求的问题 20 | if app.config['CORS_ENABLED']: 21 | CORS(app, supports_credentials=True) 22 | 23 | # init Celery # 初始化Celery 24 | # global celery 25 | # celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'], backend=app.config['CELERY_RESULT_BACKEND']) 26 | # celery.conf.update(app.config) 27 | 28 | # init modules # 初始化模块,将模块对应的命名空间增加到蓝图 29 | from . import modules 30 | modules.init_app(app) 31 | 32 | # register blueprint # 注册蓝图到应用 33 | from . import api 34 | api.init_api(app) 35 | 36 | return app # , celery 37 | -------------------------------------------------------------------------------- /app/api/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, current_app 2 | from flask_restplus_patched import Api 3 | from app import Config 4 | from flask_sqlalchemy import get_debug_queries 5 | 6 | api_blueprint = Blueprint("open_api", __name__, url_prefix=Config.APP_BLUE_PRINT_URL_PREFIX) 7 | api = Api(api_blueprint, version="1.0", 8 | prefix=Config.APP_API_VERSION, title="BaoAI", description="BaoAI Open Api Service") 9 | 10 | # 查询花费时间较慢的阀值,高于该值将记录flask日志 11 | @api_blueprint.after_app_request 12 | def after_request(response): 13 | for query in get_debug_queries(): 14 | if query.duration >= Config.FLASK_SLOW_DB_QUERY_TIME : 15 | current_app.logger.warning('Slow query: %s\nParameters:%s\nDuration:%fs\nContext: %s\n' %(query.statement, query.parameters, query.duration, query.context)) 16 | return response 17 | 18 | def init_api(app): 19 | app.register_blueprint(api_blueprint) -------------------------------------------------------------------------------- /app/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/app/common/__init__.py -------------------------------------------------------------------------------- /app/common/captcha.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import io 3 | import random 4 | import string 5 | from PIL import Image, ImageFont, ImageDraw 6 | 7 | class CaptchaTool(object): 8 | """ 9 | 生成图片验证码 10 | """ 11 | 12 | def __init__(self, width=50, height=12): 13 | 14 | self.width = width 15 | self.height = height 16 | # 新图片对象 17 | self.im = Image.new('RGB', (width, height), 'white') 18 | # 字体 19 | self.font = ImageFont.load_default() 20 | # draw对象 21 | self.draw = ImageDraw.Draw(self.im) 22 | 23 | def draw_lines(self, num=3): 24 | """ 25 | 划线 26 | """ 27 | for num in range(num): 28 | x1 = random.randint(0, self.width / 2) 29 | y1 = random.randint(0, self.height / 2) 30 | x2 = random.randint(0, self.width) 31 | y2 = random.randint(self.height / 2, self.height) 32 | self.draw.line(((x1, y1), (x2, y2)), fill='black', width=1) 33 | 34 | def get_verify_code(self): 35 | """ 36 | 生成验证码图形 37 | """ 38 | # 设置随机4位数字验证码 39 | code = ''.join(random.sample(string.digits, 4)) 40 | # 绘制字符串 41 | for item in range(4): 42 | self.draw.text((6 + random.randint(-3, 3) + 10 * item, 2 + random.randint(-2, 2)), 43 | text=code[item], 44 | fill=(random.randint(32, 127), 45 | random.randint(32, 127), 46 | random.randint(32, 127)) 47 | , font=self.font) 48 | # 划线 49 | # self.draw_lines() 50 | # 重新设置图片大小 51 | self.im = self.im.resize((100, 24)) 52 | # 图片转为base64字符串 53 | buffered = io.BytesIO() 54 | self.im.save(buffered, format="JPEG") 55 | img_str = "data:image/png;base64," + str(str(base64.b64encode(buffered.getvalue())).strip('b').strip('\'')) 56 | return img_str, code -------------------------------------------------------------------------------- /app/common/mail.py: -------------------------------------------------------------------------------- 1 | 2 | """mail 3 | 4 | mail module 5 | 6 | PROJECT: BaoAI Backend 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | 13 | import smtplib 14 | from email.mime.text import MIMEText 15 | from email.header import Header 16 | 17 | 18 | 19 | def mail(sender, password, receiver, from_, to_, subject, message): 20 | '''Send Mail # 发送邮件 21 | 22 | Usage: # 使用: 23 | mail('admin@baoai.co', 'xxxxxxxx', '703264459@qq.com', 'BaoAI', 'henry', 'email test title', 'email content...') 24 | 25 | Args: 26 | sender (str): Sender's mailbox , for example: 'admin@baoai.co' # 发件人邮箱 27 | password (str): 'xxxxxxxx' # Sender's mailbox password # 发件人邮箱密码 28 | receiver (str): '703264459@qq.com' # Recipient mailbox # 收件人邮箱 29 | from_ (str) (str): Sender's nickname # 发件人昵称 30 | to_ (str) (str): Sender's nickname # 收件人昵称 31 | subject (str) (str): email's subject # 邮件标题 32 | message (str) (str): email's content # 邮件内容 33 | 34 | Returns: 35 | dict: result, For example: {'status':True, 'message':'Success'} 36 | ''' 37 | try: 38 | msg = MIMEText(message,'plain','utf-8') 39 | msg['From'] = Header(from_,'utf-8') 40 | msg['To'] = Header(to_,'utf-8') 41 | msg['Subject'] = Header(subject,'utf-8') 42 | 43 | server = smtplib.SMTP_SSL("smtp.exmail.qq.com", 465) # SMTP server in sender's mailbox, general port is 25 # 发件人邮箱中的SMTP服务器,一般端口是25 44 | server.login(sender, password) # the sender's mailbox account and password. # 括号中对应的是发件人邮箱账号、邮箱密码 45 | server.sendmail(sender, receiver, msg.as_string()) # the sender's mailbox account, the recipient's mailbox account, and the sending of mail. # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件 46 | server.quit() # Close the connection # 关闭连接 47 | return {'status':True, 'message':'Success'} 48 | except Exception as e: 49 | return {'status':False, 'message':"Failed: " + str(e)} 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/common/mixin.py: -------------------------------------------------------------------------------- 1 | """mixin 2 | 3 | Model predefinition 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import Config, db 13 | from sqlalchemy.ext.declarative import declared_attr 14 | from datetime import datetime 15 | 16 | class TableMixin: 17 | '''Data table object, global settings set public properties and methods # 数据表对象, 全局设置设置公共属性与方法 18 | ''' 19 | id = db.Column(db.Integer, primary_key=True) 20 | pid = db.Column(db.Integer, nullable=False, default=0) # parent id # 父ID 21 | title = db.Column(db.String(255), nullable=True) 22 | created = db.Column( 23 | db.DateTime, nullable=False, default=datetime.now) # 记录增加时间 24 | #db.DateTime, nullable=False, default=datetime.utcnow) 25 | updated = db.Column( 26 | db.DateTime, onupdate=datetime.now) # 记录修改时间 27 | #db.DateTime, onupdate=datetime.utcnow) 28 | status = db.Column(db.Boolean, nullable=False, default=True, index=True) # Record status True/1=normal False/0=pause # 记录状态, 1 正常 0 暂停 29 | weight = db.Column(db.Integer, nullable=False, default=0, index=True) # Weight, the higher the value, the higher the order # 权重,值越大排序越前 30 | lang = db.Column(db.String(10), nullable=False, index=True, default='all') # lang: all zh-cn zh-tw en jp kr 31 | uid = db.Column(db.Integer, nullable=False, default=0, index=True) # admin or user id # 用户ID 32 | 33 | @declared_attr 34 | def __tablename__(cls): 35 | _table_prefix = Config.TABLE_PREFIX 36 | return _table_prefix + cls.__name__.lower() 37 | 38 | def __repr__(self): 39 | return ( 40 | "<{class_name}(" 41 | "id={self.id}, " 42 | "title=\"{self.title}\"" 43 | ")>".format( 44 | class_name=self.__class__.__name__, 45 | self=self 46 | ) 47 | ) 48 | 49 | class PureTableMixin: 50 | id = db.Column(db.Integer, primary_key=True) 51 | 52 | @declared_attr 53 | def __tablename__(cls): 54 | _table_prefix = Config.TABLE_PREFIX 55 | return _table_prefix + cls.__name__.lower() 56 | 57 | class StockTableMixin: 58 | id = db.Column(db.Integer, primary_key=True) 59 | title = db.Column(db.String(20), nullable=True) 60 | 61 | class StockPureTableMixin: 62 | id = db.Column(db.Integer, primary_key=True) 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/common/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 请求参数 5 | 6 | PROJECT: BaoAI Backend 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | 13 | from flask_marshmallow import base_fields 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | 16 | class BasicPagerParameters(Parameters): 17 | page = base_fields.Integer(required=True) 18 | limit = base_fields.Integer(required=True) 19 | 20 | class PagerParameters(Parameters): 21 | search = base_fields.String(required=False, default='') 22 | sort = base_fields.String(required=False, default='') 23 | order = base_fields.String(required=True, default='asc') 24 | offset = base_fields.Integer(required=True, default=0) 25 | limit = base_fields.Integer(required=True, default=10) 26 | lang = base_fields.String(required=False, default='') 27 | 28 | class PagerJSONParameters(JSONParameters): 29 | search = base_fields.String(required=False, default='') 30 | sort = base_fields.String(required=False, default='') 31 | order = base_fields.String(required=True, default='asc') 32 | offset = base_fields.Integer(required=True, default=0) 33 | limit = base_fields.Integer(required=True, default=10) 34 | lang = base_fields.String(required=False, default='') 35 | 36 | class IDSParameters(JSONParameters): 37 | ids = base_fields.List(base_fields.Integer()) 38 | 39 | class IDJSONParameters(JSONParameters): 40 | id = base_fields.Integer(required=True) 41 | 42 | class UIDParameters(Parameters): 43 | """ 44 | Uniform ID 45 | """ 46 | uid = base_fields.Integer(required=True) 47 | 48 | class IDParameters(Parameters): 49 | """ 50 | ID 51 | """ 52 | id = base_fields.Integer(required=True) 53 | 54 | class UIDAndIDSParameters(JSONParameters): 55 | """ 56 | UID And IDS 57 | """ 58 | uid = base_fields.Integer(required=True) 59 | ids = base_fields.List(base_fields.Integer()) 60 | 61 | class LangParameters(Parameters): 62 | lang = base_fields.String(required=False) -------------------------------------------------------------------------------- /app/common/result.py: -------------------------------------------------------------------------------- 1 | """result 2 | 3 | PROJECT: BaoAI Backend 4 | AUTHOR: henry <703264459@qq.com> 5 | WEBSITE: http://www.baoai.co 6 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 7 | LICENSE: Apache-2.0 8 | """ 9 | 10 | from flask import current_app,request 11 | from .status import Status 12 | class Result(object): 13 | @staticmethod 14 | def success(data={}, status=Status.SUCCESS.status, message=Status.SUCCESS.message): 15 | ret_json = { 16 | "status": status, 17 | "message": message, 18 | "data": data 19 | } 20 | return ret_json 21 | 22 | @staticmethod 23 | def error(data={}, status=Status.ERROR.status, message=Status.ERROR.message): 24 | ret_json = { 25 | "status": status, 26 | "message": message, 27 | "data": data 28 | } 29 | return ret_json 30 | -------------------------------------------------------------------------------- /app/common/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 响应模式 5 | 6 | PROJECT: BaoAI Backend 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | 13 | from .status import Status 14 | from flask_restplus_patched import ModelSchema 15 | from flask_marshmallow import base_fields 16 | 17 | class BasicSchema(ModelSchema): 18 | status = base_fields.Integer(default=Status.SUCCESS.status) 19 | message = base_fields.String(default=Status.SUCCESS.message) 20 | data = base_fields.Raw() 21 | class Meta: 22 | pass 23 | 24 | class BasicPagerSchema(ModelSchema): 25 | has_prev = base_fields.Boolean() 26 | has_next = base_fields.Boolean() 27 | pages = base_fields.Integer() 28 | page = base_fields.Integer() 29 | lst_size = base_fields.Integer() 30 | total = base_fields.Integer() 31 | items = base_fields.Raw() 32 | class Meta: 33 | pass 34 | 35 | class DeleteSchema(ModelSchema): 36 | delete = base_fields.Integer() 37 | class Meta: 38 | pass 39 | 40 | class ResSchema(ModelSchema): 41 | res = base_fields.Integer() 42 | class Meta: 43 | pass -------------------------------------------------------------------------------- /app/common/status.py: -------------------------------------------------------------------------------- 1 | """status 2 | 3 | Custom Response Status 4 | 定制响应状态 5 | 6 | PROJECT: BaoAI Backend 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | 13 | class Status(): 14 | class SUCCESS: 15 | status = 200000 16 | message = "Success" 17 | 18 | class ERROR: 19 | status = 200400 20 | message = "Error" 21 | 22 | class TOKEN_SUCCESS: 23 | status = 200001 24 | message = "Token Success" 25 | 26 | class PARAMETER_ERROR: 27 | status = 400422 28 | message = "Parameter Error" 29 | 30 | class FORBIDDEN: 31 | status = 400403 32 | message = "Forbidden!" 33 | 34 | class FILE_NOT_FOUND: 35 | status = 400404 36 | message = "Forbidden!" 37 | 38 | class INTERNAL_ERROR: 39 | status = 500000 40 | message = "Internal Error" 41 | 42 | class TOKEN_ERROR: 43 | status = 500001 44 | message = "Token Error" 45 | 46 | class TOKEN_SIGNATURE_EXPIRED: 47 | status = 500002 48 | message = "Token Signature Expired" 49 | 50 | class TOKEN_BADSIGNATURE: 51 | status = 500003 52 | message = "Token BadSignature" 53 | 54 | class TOKEN_TAMPERED: 55 | status = 500004 56 | message = "Token Tampered" 57 | 58 | class TOKEN_UNKNOWN_REASON: 59 | status = 500005 60 | message = "Error Token With Unknown Reason" 61 | 62 | class TOKEN_ILLEGAL: 63 | status = 500005 64 | message = "Illegal Payload Inside" 65 | -------------------------------------------------------------------------------- /app/modules/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import current_app 3 | import traceback 4 | 5 | def file_name(file_dir): 6 | '''All subdirectories under the directory #目录下所有的子目录 7 | 8 | Args: 9 | file_dir (str) : 目录绝对路径 10 | 11 | Returns: 12 | str : modules dirs # 模块目录 13 | 14 | ''' 15 | modules_dirs = [] 16 | for root, dirs, files in os.walk(file_dir): 17 | dirs.remove('__pycache__') 18 | modules_dirs = dirs 19 | break 20 | # print('root_dir:', root) # 当前目录路径 21 | # print('sub_dirs:', dirs) # 当前路径下所有子目录 22 | # print('files:', files) # 当前路径下所有非目录子文件 23 | return modules_dirs 24 | 25 | 26 | def init_app(app, **kwargs): 27 | '''init app # 初始化应用 28 | 29 | 1. import_module动态导入所有模块 30 | 2. 执行导入模块的init_app方法 31 | 32 | Args: 33 | app (obj) : flask app 34 | 35 | Returns: 36 | void : void 37 | 38 | ''' 39 | basedir = os.path.abspath(os.path.dirname(__file__)) 40 | modules_dirs = file_name(basedir) 41 | from importlib import import_module 42 | # for module_name in app.config['ENABLED_MODULES']: 43 | for module_name in modules_dirs: 44 | # import_module('.%s' % module_name, package=__name__).init_app(app, **kwargs) 45 | try: 46 | import_module('.%s' % module_name, package=__name__).init_app(app, **kwargs) 47 | except Exception as e: 48 | print('%s module exception, traceback:\n%s' % (module_name, traceback.format_exc())) 49 | 50 | -------------------------------------------------------------------------------- /app/modules/admin/__init__.py: -------------------------------------------------------------------------------- 1 | from app.api import api 2 | def init_app(app, **kwargs): 3 | """ 4 | Init admin module. 5 | """ 6 | from . import resource 7 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/admin/model.py: -------------------------------------------------------------------------------- 1 | """dao 2 | 3 | Data Access Object 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | import time 12 | from app import db 13 | from flask import current_app 14 | from werkzeug.security import generate_password_hash, check_password_hash 15 | # serializer for JWT 16 | from itsdangerous import TimedJSONWebSignatureSerializer as Serializer 17 | # exceptions for JWT 18 | from itsdangerous import SignatureExpired, BadSignature, BadData 19 | from app.common.mixin import * 20 | from app.common.result import Result 21 | from app.common.status import Status 22 | 23 | class Admin(TableMixin, db.Model): 24 | __tablename__ = 'admin' 25 | username = db.Column(db.String(32), unique=True, index=True, nullable=False) 26 | email = db.Column(db.String(255), unique=True, nullable=True) 27 | password_hash = db.Column(db.String(128)) 28 | nickname = db.Column(db.String(32)) 29 | last_login_time = db.Column(db.DateTime) # last login time 30 | last_login_ip = db.Column(db.String(32), nullable=True) # last login ip 31 | current_login_ip = db.Column(db.String(32), nullable=True) # current login IP 32 | current_login_time = db.Column(db.DateTime) # current login time 33 | login_failure = db.Column(db.Integer, nullable=False, default=0) # login failure count 34 | avatar = db.Column(db.String(255), nullable=True) # avatar url 35 | locked = db.Column(db.Boolean(), nullable=False, default=False) 36 | 37 | def __repr__(self): 38 | return '' % self.username 39 | 40 | @property 41 | def password(self): 42 | raise AttributeError("password is not a readable attribute!") 43 | 44 | @password.setter 45 | def password(self, password): 46 | self.password_hash = generate_password_hash(password) 47 | 48 | def verify_password(self, password): 49 | """Verify Password # 验证密码 50 | """ 51 | return check_password_hash(self.password_hash, password) 52 | 53 | def generate_token(self, expires=3600): 54 | """Generate Token # 产生令牌 55 | 56 | token contains id, username and timestamp 57 | 令牌包含:id, username, 时间截 58 | 59 | """ 60 | s = Serializer( 61 | secret_key=current_app.config['SECRET_KEY'], 62 | salt=current_app.config['AUTH_SALT'], 63 | expires_in=expires 64 | ) 65 | timestamp = time.time() 66 | # token contains id, username and timestamp 67 | token = s.dumps({ 68 | 'id': self.id, 69 | 'username': self.username, 70 | 'iat': timestamp 71 | }).decode() 72 | return token 73 | 74 | def generate_new_token(self, expires, username, rftoken): 75 | """Generate new token with reflesh token # 使用刷新令牌获取新令牌 76 | 77 | Args: 78 | expires (int): expires time (unit: second) 79 | username (str): username 80 | rftoken (str): reflesh token 81 | 82 | Returns: 83 | str: new token, default is None 84 | 85 | """ 86 | token = None 87 | res = Admin.confirm(rftoken) 88 | if res['status'] != Status.TOKEN_SUCCESS.status : 89 | return token 90 | if res['data']['username'] != username : 91 | return token 92 | s = Serializer( 93 | secret_key=current_app.config['SECRET_KEY'], 94 | salt=current_app.config['AUTH_SALT'], 95 | expires_in=expires 96 | ) 97 | timestamp = time.time() 98 | # token contains id, username and timestamp 99 | token = s.dumps({ 100 | 'id': self.id, 101 | 'username': self.username, 102 | 'iat': timestamp 103 | }).decode() 104 | return token 105 | 106 | @staticmethod 107 | def confirm(token): 108 | """Confirm token # 确认令牌,返回确认状态 109 | 110 | Args: 111 | token (str): To be verified token 112 | 113 | Returns: 114 | object: app.common.result.Result 115 | 116 | """ 117 | # token decoding 118 | s = Serializer( 119 | secret_key=current_app.config['SECRET_KEY'], 120 | salt=current_app.config['AUTH_SALT']) 121 | data = {} 122 | try: 123 | data = s.loads(token) 124 | # token decoding faild 125 | # if it happend a plenty of times, there might be someone 126 | # trying to attact your server, so it should be a warning. 127 | except SignatureExpired: 128 | msg = 'token expired' 129 | # current_app.logger.warning(msg) 130 | return Result.error(data,status=Status.TOKEN_SIGNATURE_EXPIRED.status, message=Status.TOKEN_SIGNATURE_EXPIRED.message) 131 | except BadSignature as e: 132 | encoded_payload = e.payload 133 | if encoded_payload is not None: 134 | try: 135 | s.load_payload(encoded_payload) 136 | except BadData: 137 | # the token is tampered. 138 | msg = 'token tampered' 139 | return Result.error(data,status=Status.TOKEN_TAMPERED.status, message=Status.TOKEN_TAMPERED.message) 140 | msg = 'badSignature of token' 141 | return Result.error(data,status=Status.TOKEN_BADSIGNATURE.status, message=Status.TOKEN_BADSIGNATURE.message) 142 | except: 143 | msg = 'wrong token with unknown reason' 144 | return Result.error(data,status=Status.TOKEN_UNKNOWN_REASON.status, message=Status.TOKEN_UNKNOWN_REASON.message) 145 | if ('id' not in data) : 146 | msg = 'illegal payload inside' 147 | return Result.error(data,status=Status.TOKEN_ILLEGAL.status, message=Status.TOKEN_ILLEGAL.message) 148 | return Result.success(data=data,status=Status.TOKEN_SUCCESS.status, message=Status.TOKEN_SUCCESS.message) 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /app/modules/admin/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from flask_marshmallow import base_fields 12 | from marshmallow import validate, ValidationError 13 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 14 | from .schema import * 15 | 16 | def validate_length(value): 17 | if len(value) < 3 or len(value) > 30: 18 | raise ValidationError('Length [3-30]') 19 | 20 | class AdminLoginParameters(JSONParameters): 21 | username = base_fields.String(required=True, validate=validate_length) 22 | password = base_fields.String(required=True) 23 | 24 | class AdminParameters(JSONParameters, AdminSchema): 25 | class Meta(AdminSchema.Meta): 26 | pass 27 | 28 | class FindPassParameters(JSONParameters): 29 | email = base_fields.String(required=True, validate=validate.Email(error='Format Error')) 30 | 31 | class RefleshTokenParameters(JSONParameters): 32 | rftoken = base_fields.String(required=True) 33 | username = base_fields.String(required=True) 34 | 35 | class UidsRidsParameters(JSONParameters): 36 | rids = base_fields.List(base_fields.Integer()) 37 | uids = base_fields.List(base_fields.Integer()) 38 | 39 | -------------------------------------------------------------------------------- /app/modules/admin/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from flask_restplus_patched import ModelSchema 12 | from flask_marshmallow import base_fields 13 | from app.common.schema import BasicSchema,BasicPagerSchema 14 | from .model import * 15 | 16 | class AdminSchema(ModelSchema): 17 | """ 18 | Base Admin schema exposes only the most general fields. 19 | """ 20 | class Meta: 21 | model = Admin 22 | 23 | class AdminListPagerSchema(ModelSchema): 24 | total = base_fields.Integer() 25 | rows = base_fields.Nested(AdminSchema(exclude=['password_hash']), many=True) 26 | class Meta: 27 | pass 28 | 29 | class RolesSchema(ModelSchema): 30 | rid = base_fields.Integer() 31 | title = base_fields.String() 32 | resources = base_fields.String() 33 | class Meta: 34 | pass 35 | 36 | class RolesDetailSchema(ModelSchema): 37 | uid = base_fields.Integer() 38 | rids = base_fields.List(base_fields.Integer()) 39 | rids_str = base_fields.String() 40 | resources_ids = base_fields.List(base_fields.Integer()) 41 | resources_ids_str = base_fields.String() 42 | titles = base_fields.String() 43 | roles = base_fields.Nested(RolesSchema, many=True) 44 | class Meta: 45 | pass 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/modules/adminroles/__init__.py: -------------------------------------------------------------------------------- 1 | """adminroles 2 | 3 | Init adminroles module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init adminroles module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/adminroles/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class AdminRoles(TableMixin, db.Model): 16 | __tablename__ = 'admin_roles' 17 | uid = db.Column(db.Integer, nullable=False) 18 | rid = db.Column(db.Integer, nullable=False) 19 | __table_args__ = ( 20 | db.UniqueConstraint('uid', 'rid', name='uix_admin_roles_uid_rid'), 21 | ) 22 | 23 | def __repr__(self): 24 | return '' % (self.id, self.uid, self.rid) 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/modules/adminroles/param.py: -------------------------------------------------------------------------------- 1 | from flask_marshmallow import base_fields 2 | from marshmallow import validate 3 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 4 | from app.common.param import BasicPagerParameters, PagerParameters, PagerJSONParameters 5 | from .schema import * 6 | 7 | -------------------------------------------------------------------------------- /app/modules/adminroles/resource.py: -------------------------------------------------------------------------------- 1 | from app import db 2 | from flask import current_app,request 3 | from app.common.status import Status 4 | from app.common.schema import * 5 | from app.common.param import * 6 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 7 | from app.common.result import Result 8 | from .model import * 9 | from .schema import * 10 | from .param import * 11 | import random 12 | import string 13 | from sqlalchemy import or_ 14 | 15 | ns = Namespace("adminroles", description="adminroles API Resource") 16 | -------------------------------------------------------------------------------- /app/modules/adminroles/schema.py: -------------------------------------------------------------------------------- 1 | from flask_restplus_patched import ModelSchema 2 | from flask_marshmallow import base_fields 3 | from app.common.schema import BasicSchema,BasicPagerSchema 4 | from .model import * 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/modules/attachments/__init__.py: -------------------------------------------------------------------------------- 1 | """attachments 2 | 3 | Init attachments module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init attachments module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/attachments/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class Attachments(TableMixin, db.Model): 16 | __tablename__ = 'attachments' 17 | admin_id = db.Column(db.Integer, nullable=False, default=0) 18 | user_id = db.Column(db.Integer, nullable=False, default=0) 19 | module_name = db.Column(db.String(100), nullable=False, default='attachments') # Module Name 20 | module_obj_id = db.Column(db.Integer, nullable=False, default=0) # Module Object ID 21 | url = db.Column(db.String(255), nullable=False) 22 | uuid = db.Column(db.String(100), nullable=False) 23 | imagewidth = db.Column(db.String(30), nullable=False, default="0") 24 | imageheight = db.Column(db.String(30), nullable=False, default="0") 25 | imagetype = db.Column(db.String(30), nullable=True) 26 | imageframes = db.Column(db.Integer, nullable=False, default=0) 27 | filesize = db.Column(db.Integer, nullable=False, default=0) 28 | mimetype = db.Column(db.String(100), nullable=True) 29 | isimage = db.Column(db.Boolean(), nullable=False, default=True) # is image, 1/True: Image 0/False: File 30 | iscover = db.Column(db.Boolean(), nullable=False, default=False) # is cover, 1/True: cover 0/False: not cover 31 | weight = db.Column(db.Integer, nullable=False, default=0) 32 | extparam = db.Column(db.String(255), nullable=True) 33 | storage = db.Column(db.String(100), nullable=False, default="local") 34 | sha1 = db.Column(db.String(40), nullable=True) 35 | md5 = db.Column(db.String(40), nullable=True) 36 | category_id = db.Column(db.Integer, nullable=False, default=0) 37 | category_main = db.Column(db.String(100), nullable=False, index=True, default='uncategorized') 38 | category_sub = db.Column(db.String(100), nullable=False, index=True, default='uncategorized') 39 | 40 | def __repr__(self): 41 | return '' % self.title 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/modules/attachments/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import JSONParameters, Parameters, PostFormParameters 15 | from .schema import * 16 | from app.common.param import PagerParameters 17 | 18 | class AttachmentsParameters(JSONParameters, AttachmentsSchema): 19 | class Meta(AttachmentsSchema.Meta): 20 | pass 21 | 22 | class ExtendPagerParameters(PagerParameters): 23 | module_name = base_fields.String(required=False) 24 | module_obj_id = base_fields.Integer(required=False) 25 | category_id = base_fields.Integer(required=False) 26 | 27 | class CEditorFileBrowserUploadParameters(PostFormParameters): 28 | CKEditorFuncNum = base_fields.Integer(required=False) 29 | CKEditor = base_fields.String(required=False) 30 | langCode = base_fields.String(required=False) 31 | ckCsrfToken = base_fields.String(required=False) 32 | 33 | class RemoteImgURLParameters(Parameters): 34 | remote_img_url = base_fields.String(required=False) -------------------------------------------------------------------------------- /app/modules/attachments/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from app.common.schema import BasicSchema,BasicPagerSchema 15 | from .model import * 16 | 17 | class AttachmentsSchema(ModelSchema): 18 | class Meta: 19 | model = Attachments 20 | 21 | class AttachmentsListPagerSchema(ModelSchema): 22 | total = base_fields.Integer() 23 | rows = base_fields.Nested(AttachmentsSchema, many=True) 24 | class Meta: 25 | pass 26 | 27 | class CKeditorUploadErrorSchema(ModelSchema): 28 | message = base_fields.String() 29 | class Meta: 30 | pass 31 | 32 | class AttachmentsCKeditorUploadSchema(ModelSchema): 33 | uploaded = base_fields.Integer() 34 | fileName = base_fields.String() 35 | url = base_fields.String() 36 | error = base_fields.Nested(CKeditorUploadErrorSchema) 37 | class Meta: 38 | pass 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/modules/category/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Category module 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-08-04 10:04:19 10 | """ 11 | 12 | from app.api import api 13 | 14 | def init_app(app, **kwargs): 15 | """ 16 | Init Category module. 17 | """ 18 | from . import resource 19 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/category/model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Category database models 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-08-04 10:04:19 10 | """ 11 | 12 | from app import db 13 | from sqlalchemy.schema import FetchedValue 14 | from app.common.mixin import * 15 | 16 | class Category(TableMixin, db.Model): 17 | """ 18 | Category database model. 19 | """ 20 | __tablename__ = 'category' 21 | ismenu = db.Column(db.Boolean(), nullable=True, unique=False, index=False, default='True') 22 | pid = db.Column(db.Integer(), nullable=False, unique=False, index=True, default='0') 23 | title = db.Column(db.String(255), nullable=False, unique=False, index=True) 24 | alias = db.Column(db.String(100), nullable=True, unique=False, index=False) 25 | weight = db.Column(db.Integer(), nullable=False, unique=False, index=True, default='0') 26 | keywords = db.Column(db.String(255), nullable=True, unique=False, index=False) 27 | summary = db.Column(db.Text(), nullable=True, unique=False, index=False) 28 | content = db.Column(db.Text(), nullable=True, unique=False, index=False) 29 | link_type = db.Column(db.String(30), nullable=True, unique=False, index=False) 30 | inner_link = db.Column(db.Integer(), nullable=True, unique=False, index=False, default='0') 31 | link_target = db.Column(db.String(255), nullable=True, unique=False, index=False, default='_blank') 32 | link = db.Column(db.String(255), nullable=True, unique=False, index=False) 33 | block_link = db.Column(db.Integer(), nullable=True, unique=False, index=False) 34 | article_link = db.Column(db.Integer(), nullable=True, unique=False, index=False) 35 | articles = db.Column(db.Integer(), nullable=True, unique=False, index=False) 36 | route_link = db.Column(db.String(255), nullable=True, unique=False, index=False) 37 | cover = db.Column(db.String(255), nullable=True, unique=False, index=False) 38 | views = db.Column(db.Integer(), nullable=True, unique=False, index=False, default='0') 39 | # tree 40 | treepath = db.Column(db.String(255), nullable=True) # tree path, for example: .2.120. , 0 does not need to be at the front # 树路径 41 | treegrade = db.Column(db.Integer, nullable=True) # Tree depth, top level is 0, .2.120. is 2 # 树深度 42 | # tree path weight, Weight for tree sort. for example: 2000.2.1500.120. , The weight of node(id = 2) is 2000, The weight of node(id = 120) is 1500 43 | # 树路径权重,用于树形排序。如:2000.2.1500.120. , 结点ID是2 的权重是2000 , 结点ID是120的权重是1500 44 | treepathweight = db.Column(db.String(255), nullable=True) 45 | 46 | def __repr__(self): 47 | return ( 48 | "<{class_name}(" 49 | "id={self.id}, " 50 | "title=\"{self.title}\"" 51 | ")>".format( 52 | class_name=self.__class__.__name__, 53 | self=self 54 | ) 55 | ) 56 | -------------------------------------------------------------------------------- /app/modules/category/param.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Category Module Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-08-04 10:04:19 11 | """ 12 | 13 | from flask_marshmallow import base_fields 14 | from marshmallow import validate 15 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 16 | from .schema import * 17 | from app.common.param import PagerParameters 18 | 19 | class CategoryParameters(JSONParameters, CategorySchema): 20 | """ 21 | Category Parameters 22 | """ 23 | class Meta(CategorySchema.Meta): 24 | pass 25 | 26 | class ExtendPagerParameters(PagerParameters): 27 | articles = base_fields.Integer(required=False) 28 | books_category_id = base_fields.Integer(required=False) 29 | nav_category_id = base_fields.Integer(required=False) 30 | is_book = base_fields.Boolean(required=False) 31 | is_main = base_fields.Boolean(required=False) 32 | 33 | class NewsExtendPagerParameters(PagerParameters): 34 | articles = base_fields.Integer(required=False) 35 | news_category_id = base_fields.Integer(required=False) 36 | 37 | class FieldParameters(Parameters): 38 | category = base_fields.Integer(required=False) 39 | field = base_fields.String(required=False) 40 | 41 | -------------------------------------------------------------------------------- /app/modules/category/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Category Module Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-08-04 10:04:19 11 | """ 12 | 13 | from flask_restplus_patched import ModelSchema 14 | from flask_marshmallow import base_fields 15 | from .model import * 16 | 17 | class CategorySchema(ModelSchema): 18 | """ 19 | Category schema 20 | """ 21 | class Meta: 22 | model = Category 23 | 24 | class CategoryListPagerSchema(CategorySchema): 25 | """ 26 | Category pager schema 27 | """ 28 | total = base_fields.Integer() 29 | rows = base_fields.Nested(CategorySchema, many=True) 30 | class Meta: 31 | pass 32 | 33 | class CategoryFieldSchema(ModelSchema): 34 | """ 35 | Category Field schema 36 | """ 37 | title = base_fields.String() 38 | field = base_fields.String() 39 | class Meta: 40 | pass -------------------------------------------------------------------------------- /app/modules/configs/__init__.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init configs module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init configs module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/configs/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class Configs(TableMixin, db.Model): 16 | """Model Class""" 17 | __tablename__ = 'configs' 18 | uid = db.Column(db.Integer, nullable=False) 19 | module = db.Column(db.String(100), nullable=False) 20 | section = db.Column(db.String(100), nullable=False) 21 | keys = db.Column(db.String(100), nullable=False) 22 | value = db.Column(db.Text(), nullable=True) 23 | __table_args__ = ( 24 | db.UniqueConstraint('module', 'section', 'keys', 'lang', name='uix_configs_module_section_keys_lang'), 25 | ) 26 | 27 | def __repr__(self): 28 | """Class Serialization""" 29 | return '' % (self.module + '.' + self.section + '.' + self.key + '.' + self.lang + ' = ' + self.value) 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/modules/configs/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from .schema import * 16 | from app.common.param import PagerParameters 17 | 18 | class ConfigsParameters(JSONParameters, ConfigsSchema): 19 | """ 20 | Configs Parameters 21 | """ 22 | class Meta(ConfigsSchema.Meta): 23 | pass 24 | 25 | class ExtendPagerParameters(PagerParameters): 26 | module_name = base_fields.String(required=False) 27 | section = base_fields.String(required=False) 28 | 29 | -------------------------------------------------------------------------------- /app/modules/configs/resource.py: -------------------------------------------------------------------------------- 1 | """resource 2 | 3 | API Resource Of Configs Module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from app.common.schema import * 15 | from app.common.param import * 16 | from app.common.wrap import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from sqlalchemy import or_ 19 | from .model import * 20 | from .schema import * 21 | from .param import * 22 | from .dao import * 23 | 24 | ns = Namespace("configs", description="configs API Resource") 25 | 26 | @ns.route("/") 27 | class ConfigsAPI(Resource): 28 | """ 29 | configs module resource main service: add/delete/edit/view 30 | """ 31 | @ns.parameters(ConfigsParameters(dump_only=['id'])) 32 | @ns.response(ConfigsSchema(many=False)) 33 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 34 | def post(self, args): 35 | """ 36 | add 37 | """ 38 | configs = None 39 | configsDao = ConfigsDao() 40 | try: 41 | configs = configsDao.add(args) 42 | except Exception as e: 43 | abort(500, e) 44 | return configs 45 | 46 | @ns.parameters(ConfigsParameters()) 47 | @ns.response(ConfigsSchema()) 48 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 49 | def put(self, args): 50 | """ 51 | edit 52 | """ 53 | record = None 54 | configsDao = ConfigsDao() 55 | try: 56 | record = configsDao.edit(args) 57 | except Exception as e: 58 | abort(500, e) 59 | return record 60 | 61 | @auth() 62 | @ns.parameters(IDSParameters()) 63 | @ns.response(BasicSchema(many=False)) 64 | def delete(self, args): 65 | """ 66 | delete 67 | """ 68 | result = False 69 | ids = args.get('ids') 70 | configsDao = ConfigsDao() 71 | try: 72 | result = configsDao.delete(ids) 73 | except Exception as e: 74 | abort(500, e) 75 | return {"status":result} 76 | 77 | @auth() 78 | @ns.response(ConfigsSchema()) 79 | @ns.response(code=HTTPStatus.NO_CONTENT) 80 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 81 | def get(self, args): 82 | """ 83 | view 84 | """ 85 | record = None 86 | configsDao = ConfigsDao() 87 | id = request.uid 88 | record = configsDao.getById(id) 89 | return record 90 | 91 | @ns.route("/list") 92 | class ConfigsListAPI(Resource): 93 | """ 94 | configs module resource list service 95 | """ 96 | @auth() 97 | @ns.parameters(ExtendPagerParameters()) 98 | @ns.response(ConfigsListPagerSchema(many=False)) 99 | @ns.response(code=500) 100 | def get(self,args): 101 | """ 102 | view list 103 | """ 104 | data = {} 105 | args.setdefault('search','') 106 | args.setdefault('sort','') 107 | args.setdefault('module_name','') 108 | args.setdefault('section','') 109 | search = args.pop('search') 110 | sort = args.pop('sort') 111 | order = args.pop('order') 112 | offset = args.pop('offset') 113 | limit = args.pop('limit') 114 | lang = args.pop('lang') 115 | module_name = args.pop('module_name') 116 | section = args.pop('section') 117 | configsDao = ConfigsDao() 118 | data = configsDao.getList(search, sort, order, offset, limit, lang, module_name, section) 119 | return data 120 | 121 | @ns.route("/modules") 122 | class ConfigsModulesListAPI(Resource): 123 | """ 124 | all modules list service # 查询所有模块列表 125 | """ 126 | #@auth() 127 | @ns.response(ConfigsSchema(only=['module'], many=True)) 128 | @ns.response(code=500) 129 | def post(self): 130 | """ 131 | view list 132 | """ 133 | configsDao = ConfigsDao() 134 | records = configsDao.getModules() 135 | return records 136 | 137 | @ns.route("/sections") 138 | class ConfigsSectionsListAPI(Resource): 139 | """ 140 | sections list service by module # 获取某个模块下的所有类别(section) 141 | """ 142 | #@auth() 143 | @ns.parameters(ConfigsParameters(only=['module'])) 144 | @ns.response(ConfigsSchema(only=['section'], many=True)) 145 | @ns.response(code=500) 146 | def post(self,args): 147 | """ 148 | view list 149 | """ 150 | module = args.get('module') 151 | configsDao = ConfigsDao() 152 | records = configsDao.getSections(module) 153 | return records 154 | 155 | @ns.route("/keys") 156 | class ConfigsKeysListAPI(Resource): 157 | """ 158 | keys list service by module, secton and lang # 由模块、类别和语言获取键值对 159 | """ 160 | #@auth() 161 | @ns.parameters(ConfigsParameters(only=['module', 'section', 'lang'])) 162 | @ns.response(ConfigsSchema(only=['keys', 'value', 'title'], many=True)) 163 | @ns.response(code=500) 164 | def post(self,args): 165 | """ 166 | view list 167 | """ 168 | module = args.get('module') 169 | section = args.get('section') 170 | lang = args.get('lang') 171 | configsDao = ConfigsDao() 172 | records = configsDao.getKeys(module, section, lang) 173 | return records 174 | 175 | @ns.route("/value") 176 | class ConfigsValueAPI(Resource): 177 | """ 178 | get value with module, section , lang and key # 由模块、类别、语言和键获取值 179 | """ 180 | #@auth() 181 | @ns.parameters(ConfigsParameters(only=['module', 'section', 'lang', 'keys'])) 182 | @ns.response(ConfigsSchema(many=False)) 183 | @ns.response(code=500) 184 | def post(self,args): 185 | """ 186 | view list 187 | """ 188 | module = args.get('module') 189 | section = args.get('section') 190 | lang = args.get('lang') 191 | key = args.get('keys') 192 | configsDao = ConfigsDao() 193 | records = configsDao.getValue(module, section, lang, key) 194 | return records 195 | 196 | @ns.route("/models") 197 | class ConfigsModelsListAPI(Resource): 198 | """ 199 | Get Models List 200 | """ 201 | @auth() 202 | @ns.response(ConfigsModelsSchema(many=True)) 203 | @ns.response(code=500) 204 | def get(self): 205 | """ 206 | view list 207 | """ 208 | configsDao = ConfigsDao() 209 | result = configsDao.getModels() 210 | return result 211 | 212 | -------------------------------------------------------------------------------- /app/modules/configs/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class ConfigsSchema(ModelSchema): 17 | """ 18 | Configs schema 19 | """ 20 | class Meta: 21 | model = Configs 22 | 23 | class ConfigsListPagerSchema(ModelSchema): 24 | """ 25 | Configs list pager schema 26 | """ 27 | total = base_fields.Integer() 28 | rows = base_fields.Nested(ConfigsSchema, many=True) 29 | class Meta: 30 | pass 31 | 32 | class ConfigsModelsSchema(ModelSchema): 33 | """ 34 | Get Models list 35 | """ 36 | model = base_fields.String() 37 | module = base_fields.String() 38 | module_name = base_fields.String() 39 | tablename = base_fields.String() 40 | model = base_fields.String() 41 | columns = base_fields.List(base_fields.String(), many=True) 42 | class Meta: 43 | pass 44 | 45 | -------------------------------------------------------------------------------- /app/modules/iris/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | IRIS module 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-08-23 16:18:40 10 | """ 11 | 12 | from app.api import api 13 | 14 | def init_app(app, **kwargs): 15 | """ 16 | Init IRIS module. 17 | """ 18 | from . import resource 19 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/iris/model.py: -------------------------------------------------------------------------------- 1 | """ 2 | IRIS database models 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-08-23 16:18:40 10 | """ 11 | 12 | from app import db 13 | from sqlalchemy.schema import FetchedValue 14 | from app.common.mixin import * 15 | 16 | class Iris(PureTableMixin, db.Model): 17 | """ 18 | IRIS database model. 19 | """ 20 | __tablename__ = 'iris' 21 | sepal_length = db.Column(db.Float(), nullable=True, unique=False, index=False) 22 | sepal_width = db.Column(db.Float(), nullable=True, unique=False, index=False) 23 | petal_length = db.Column(db.Float(), nullable=True, unique=False, index=False) 24 | petal_width = db.Column(db.Float(), nullable=True, unique=False, index=False) 25 | irisclass = db.Column(db.String(30), nullable=True, unique=False, index=False) 26 | 27 | def __repr__(self): 28 | return ( 29 | "<{class_name}(" 30 | "id={self.id}" 31 | ")>".format( 32 | class_name=self.__class__.__name__, 33 | self=self 34 | ) 35 | ) 36 | -------------------------------------------------------------------------------- /app/modules/iris/param.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | IRIS Module Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-08-23 16:18:40 11 | """ 12 | 13 | from flask_marshmallow import base_fields 14 | from marshmallow import validate 15 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 16 | from .schema import * 17 | 18 | 19 | class IrisParameters(JSONParameters, IrisSchema): 20 | """ 21 | IRIS Parameters 22 | """ 23 | class Meta(IrisSchema.Meta): 24 | pass 25 | 26 | class IrisLinearPredictParameters(Parameters): 27 | feature_select = base_fields.String(required=False) # Feature selection of iris, including sepals and petals length and width # 鸢尾花特征选择,包括萼片和花瓣长度、宽度 28 | feature_value = base_fields.Float(required=False) # Corresponding value of iris # 鸢尾花对应特征值 29 | linear_select = base_fields.String(required=False) # Linear regression algorithm selection # 线性回归算法选择 30 | feature_select_predict = base_fields.String(required=False) # Prediction feature selection of iris # 鸢尾花预测特征选择 31 | 32 | class IrisClassifyPredictParameters(Parameters): 33 | sepal_length_logic = base_fields.Float(required=False) 34 | sepal_width_logic = base_fields.Float(required=False) 35 | petal_length_logic = base_fields.Float(required=False) 36 | petal_width_logic = base_fields.Float(required=False) 37 | logic_select = base_fields.String(required=False) 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/modules/iris/resource.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | RESTful API IRIS resource 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-08-23 16:18:40 11 | """ 12 | 13 | from app import db 14 | from flask import current_app,request 15 | from app.common.schema import * 16 | from app.common.param import * 17 | from app.common.wrap import * 18 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 19 | from sqlalchemy import or_ 20 | from .model import * 21 | from .schema import * 22 | from .param import * 23 | from .dao import * 24 | 25 | 26 | ns = Namespace("iris", description="RESTful API IRIS resource") 27 | 28 | @ns.route("/") 29 | class IrisAPI(Resource): 30 | """ 31 | IRIS module resource main service: add/delete/edit/view 32 | """ 33 | @ns.parameters(IrisParameters(dump_only=['id'])) 34 | @ns.response(IrisSchema(many=False)) 35 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 36 | def post(self, args): 37 | """ 38 | add 39 | """ 40 | record = None 41 | irisDao = IrisDao() 42 | try: 43 | record = irisDao.add(args) 44 | except Exception as e: 45 | abort(500, e) 46 | return record 47 | 48 | @ns.parameters(IrisParameters()) 49 | @ns.response(IrisSchema()) 50 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 51 | def put(self, args): 52 | """ 53 | edit 54 | """ 55 | record = None 56 | irisDao = IrisDao() 57 | try: 58 | record = irisDao.edit(args) 59 | except Exception as e: 60 | abort(500, e) 61 | return record 62 | 63 | @auth() 64 | @ns.parameters(IDSParameters()) 65 | @ns.response(BasicSchema(many=False)) 66 | def delete(self, args): 67 | """ 68 | delete 69 | """ 70 | result = False 71 | ids = args.get('ids') 72 | irisDao = IrisDao() 73 | try: 74 | result = irisDao.delete(ids) 75 | except Exception as e: 76 | abort(500, e) 77 | return {"status":result} 78 | 79 | @auth() 80 | @ns.parameters(IDParameters()) 81 | @ns.response(IrisSchema()) 82 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 83 | def get(self, args): 84 | """ 85 | view 86 | """ 87 | record = None 88 | irisDao = IrisDao() 89 | id = args.get('id') 90 | record = irisDao.getById(id) 91 | return record 92 | 93 | @ns.route("/list") 94 | class IrisListAPI(Resource): 95 | """ 96 | IRIS module resource list service 97 | """ 98 | @auth() 99 | @ns.parameters(PagerParameters()) 100 | @ns.response(IrisListPagerSchema(many=False)) 101 | @ns.response(code=500) 102 | def get(self,args): 103 | """ 104 | view list 105 | """ 106 | data = {} 107 | args.setdefault('search','') 108 | args.setdefault('sort','') 109 | args.setdefault('lang','') 110 | search = args.pop('search') 111 | sort = args.pop('sort') 112 | order = args.pop('order') 113 | offset = args.pop('offset') 114 | limit = args.pop('limit') 115 | lang = args.pop('lang') 116 | 117 | irisDao = IrisDao() 118 | 119 | data = irisDao.getListByLang(search, sort, order, offset, limit, lang) 120 | 121 | return data 122 | 123 | @ns.route("/linear_predict") 124 | class IrisLinearPredictAPI(Resource): 125 | @auth() 126 | @ns.parameters(IrisLinearPredictParameters()) 127 | @ns.response(IrisPredictSchema(many=False)) 128 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 129 | def get(self, args): 130 | """Regression prediction 回归预测 131 | 132 | """ 133 | data = {} 134 | args.setdefault('feature_select','') 135 | args.setdefault('feature_value',1) 136 | args.setdefault('linear_select','') 137 | args.setdefault('feature_select_predict','') 138 | feature_select = args.pop('feature_select') 139 | feature_value = args.pop('feature_value') 140 | linear_select = args.pop('linear_select') 141 | feature_select_predict = args.pop('feature_select_predict') 142 | irisDao = IrisDao() 143 | data = irisDao.linearPredict(feature_select, feature_value, linear_select, feature_select_predict) 144 | return data 145 | 146 | @ns.route("/classify_predict") 147 | class IrisClassifyPredictAPI(Resource): 148 | @auth() 149 | @ns.parameters(IrisClassifyPredictParameters()) 150 | @ns.response(IrisPredictSchema(many=False)) 151 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 152 | def get(self, args): 153 | """ 154 | classify predict # 分类预测 155 | """ 156 | data = {} 157 | args.setdefault('sepal_length_logic',1) 158 | args.setdefault('sepal_width_logic',1) 159 | args.setdefault('petal_length_logic',1) 160 | args.setdefault('petal_width_logic',1) 161 | args.setdefault('logic_select','') 162 | sepal_length_logic = args.pop('sepal_length_logic') 163 | sepal_width_logic = args.pop('sepal_width_logic') 164 | petal_length_logic = args.pop('petal_length_logic') 165 | petal_width_logic = args.pop('petal_width_logic') 166 | logic_select = args.pop('logic_select') 167 | irisDao = IrisDao() 168 | data = irisDao.classifyPredict(sepal_length_logic, sepal_width_logic, petal_length_logic, petal_width_logic, logic_select) 169 | return data 170 | 171 | @ns.route("/show") 172 | class IrisLinearPredictAPI(Resource): 173 | @auth() 174 | @ns.response(IrisFigureSchema(many=False)) 175 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 176 | def get(self): 177 | """ 178 | Generate Figure 179 | 生成绘图图片,并返回图片路径 180 | """ 181 | data = {} 182 | irisDao = IrisDao() 183 | data = irisDao.show() 184 | return data 185 | -------------------------------------------------------------------------------- /app/modules/iris/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | IRIS Module Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-08-23 16:18:40 11 | """ 12 | 13 | from flask_restplus_patched import ModelSchema 14 | from flask_marshmallow import base_fields 15 | from .model import * 16 | 17 | class IrisSchema(ModelSchema): 18 | """ 19 | IRIS schema 20 | """ 21 | class Meta: 22 | model = Iris 23 | 24 | class IrisListPagerSchema(IrisSchema): 25 | """ 26 | IRIS pager schema 27 | """ 28 | total = base_fields.Integer() 29 | rows = base_fields.Nested(IrisSchema, many=True) 30 | class Meta: 31 | pass 32 | 33 | class IrisPredictSchema(ModelSchema): 34 | """ 35 | IRIS pager schema 36 | """ 37 | linear_result = base_fields.Float() 38 | classify_result = base_fields.String() 39 | metric = base_fields.Float() 40 | class Meta: 41 | pass 42 | 43 | class IrisFigureSchema(ModelSchema): 44 | """ 45 | IRIS pager schema 46 | """ 47 | figure_name = base_fields.String() 48 | class Meta: 49 | pass -------------------------------------------------------------------------------- /app/modules/logs/__init__.py: -------------------------------------------------------------------------------- 1 | """logs 2 | 3 | Init logs module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init logs module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/logs/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class Logs(TableMixin, db.Model): 16 | """Model Class""" 17 | __tablename__ = 'logs' 18 | uid = db.Column(db.Integer, index=True, nullable=False) 19 | url = db.Column(db.String(255), nullable=True) 20 | method = db.Column(db.String(10), nullable=True) 21 | referer = db.Column(db.String(255), nullable=True) 22 | path = db.Column(db.String(255), nullable=True) 23 | base_path = db.Column(db.String(255), nullable=True) 24 | route = db.Column(db.String(100), nullable=True) 25 | ip = db.Column(db.String(32), nullable=True) 26 | user_agent = db.Column(db.String(255), nullable=True) 27 | desc = db.Column(db.Text(), nullable=True) 28 | 29 | def __repr__(self): 30 | return ( 31 | "<{class_name}(" 32 | "id={self.id}, " 33 | "title=\"{self.title}\"" 34 | ")>".format( 35 | class_name=self.__class__.__name__, 36 | self=self 37 | ) 38 | ) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/modules/logs/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from .schema import * 16 | 17 | class LogsParameters(JSONParameters, LogsSchema): 18 | class Meta(LogsSchema.Meta): 19 | pass 20 | 21 | -------------------------------------------------------------------------------- /app/modules/logs/resource.py: -------------------------------------------------------------------------------- 1 | """resource 2 | 3 | API Resource Of Logs Module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from app.common.schema import * 15 | from app.common.param import * 16 | from app.common.wrap import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from .model import * 19 | from .schema import * 20 | from .param import * 21 | from sqlalchemy import or_ 22 | from .dao import * 23 | 24 | ns = Namespace("logs", description="logs API Resource") 25 | 26 | @ns.route("/") 27 | class LogsAPI(Resource): 28 | @ns.parameters(LogsParameters(dump_only=['id'])) 29 | @ns.response(LogsSchema(many=False)) 30 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 31 | def post(self, args): 32 | """ 33 | add 34 | """ 35 | logs = None 36 | logsDao = LogsDao() 37 | try: 38 | logs = logsDao.add(args) 39 | except Exception as e: 40 | abort(500, e) 41 | return logs 42 | 43 | @ns.parameters(LogsParameters()) 44 | @ns.response(LogsSchema()) 45 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 46 | def put(self, args): 47 | """ 48 | edit 49 | """ 50 | record = None 51 | logsDao = LogsDao() 52 | try: 53 | record = logsDao.edit(args) 54 | except Exception as e: 55 | abort(500, e) 56 | return record 57 | 58 | @ns.parameters(IDParameters()) 59 | @ns.response(LogsSchema()) 60 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 61 | def get(self, args): 62 | """ 63 | view 64 | """ 65 | record = None 66 | logsDao = LogsDao() 67 | id = args.get('id') 68 | record = logsDao.getById(id) 69 | return record 70 | 71 | @auth() 72 | @ns.parameters(IDSParameters()) 73 | @ns.response(BasicSchema(many=False)) 74 | def delete(self, args): 75 | """ 76 | del 77 | """ 78 | result = False 79 | ids = args.get('ids') 80 | logsDao = LogsDao() 81 | try: 82 | result = logsDao.delete(ids) 83 | except Exception as e: 84 | abort(500, e) 85 | return {"status":result} 86 | 87 | @ns.route("/list") 88 | class LogsListAPI(Resource): 89 | @auth() 90 | @ns.parameters(PagerParameters()) 91 | @ns.response(LogsListPagerSchema(many=False)) 92 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 93 | def get(self,args): 94 | """ 95 | list 96 | """ 97 | data = {} 98 | args.setdefault('search','') 99 | args.setdefault('sort','') 100 | search = args.pop('search') 101 | sort = args.pop('sort') 102 | order = args.pop('order') 103 | offset = args.pop('offset') 104 | limit = args.pop('limit') 105 | logsDao = LogsDao() 106 | data = logsDao.getList(search, sort, order, offset, limit) 107 | return data 108 | 109 | @ns.route("/user") 110 | class UserLogsAPI(Resource): 111 | @ns.parameters(LogsParameters(dump_only=['id'])) 112 | @ns.response(LogsSchema(many=False)) 113 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 114 | def post(self, args): 115 | """ 116 | add 117 | """ 118 | logs = None 119 | logsDao = LogsDao() 120 | try: 121 | logs = logsDao.add(args) 122 | except Exception as e: 123 | abort(500, e) 124 | return logs 125 | 126 | @ns.parameters(LogsParameters()) 127 | @ns.response(LogsSchema()) 128 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 129 | def put(self, args): 130 | """ 131 | edit 132 | """ 133 | record = None 134 | logsDao = LogsDao() 135 | try: 136 | record = logsDao.edit(args) 137 | except Exception as e: 138 | abort(500, e) 139 | return record 140 | 141 | @ns.parameters(IDParameters()) 142 | @ns.response(LogsSchema()) 143 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 144 | def get(self, args): 145 | """ 146 | view 147 | """ 148 | record = None 149 | logsDao = LogsDao() 150 | id = args.get('id') 151 | record = logsDao.getById(id) 152 | return record 153 | 154 | @auth() 155 | @ns.parameters(IDSParameters()) 156 | @ns.response(BasicSchema(many=False)) 157 | def delete(self, args): 158 | """ 159 | del 160 | """ 161 | result = False 162 | ids = args.get('ids') 163 | uid = request.uid 164 | logsDao = LogsDao() 165 | try: 166 | result = logsDao.deleteByUid(ids, uid) 167 | except Exception as e: 168 | abort(500, e) 169 | return {"status":result} 170 | 171 | @ns.route("/user/list") 172 | class UserLogsListAPI(Resource): 173 | @auth() 174 | @ns.parameters(PagerParameters()) 175 | @ns.response(LogsListPagerSchema(many=False)) 176 | @ns.response(code=500,description="Internal Server Exception") 177 | def get(self,args): 178 | """ 179 | list 180 | """ 181 | data = {} 182 | args.setdefault('search','') 183 | args.setdefault('sort','') 184 | search = args.pop('search') 185 | sort = args.pop('sort') 186 | order = args.pop('order') 187 | offset = args.pop('offset') 188 | limit = args.pop('limit') 189 | logsDao = LogsDao() 190 | uid = request.uid 191 | data = logsDao.getListByUid(search, sort, order, offset, limit, uid) 192 | return data 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /app/modules/logs/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class LogsSchema(ModelSchema): 17 | """ 18 | Logs schema . 19 | """ 20 | 21 | class Meta: 22 | model = Logs 23 | 24 | class LogsListPagerSchema(ModelSchema): 25 | total = base_fields.Integer() 26 | rows = base_fields.Nested(LogsSchema, many=True) 27 | class Meta: 28 | pass 29 | 30 | -------------------------------------------------------------------------------- /app/modules/notice_content/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Notice Content module 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from app.api import api 13 | 14 | def init_app(app, **kwargs): 15 | """ 16 | Init Notice Content module. 17 | """ 18 | from . import resource 19 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/notice_content/model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Notice Content database models 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from app import db 13 | from sqlalchemy.schema import FetchedValue 14 | from app.common.mixin import * 15 | 16 | class Notice_content(TableMixin, db.Model): 17 | """ 18 | Notice Content database model. 19 | """ 20 | __tablename__ = 'notice_content' 21 | 22 | title = db.Column(db.String(255), nullable=False, unique=False, index=True) 23 | icon = db.Column(db.String(100), nullable=True, unique=False, index=False, default='fa fa-circle-o') 24 | content = db.Column(db.Text(), nullable=True, unique=False, index=False) 25 | receiver = db.Column(db.Integer(), nullable=True, unique=False, index=False) 26 | module = db.Column(db.String(100), nullable=True, unique=False, index=False) 27 | reference = db.Column(db.String(255), nullable=True, unique=False, index=False) 28 | reference_params = db.Column(db.String(255), nullable=True, unique=False, index=False) 29 | status = db.Column(db.Boolean(), nullable=False, unique=False, index=True, default=True) 30 | weight = db.Column(db.Integer(), nullable=True, unique=False, index=False, default='0') 31 | 32 | 33 | 34 | def __repr__(self): 35 | return ( 36 | "<{class_name}(" 37 | "id={self.id}, " 38 | "title=\"{self.title}\"" 39 | ")>".format( 40 | class_name=self.__class__.__name__, 41 | self=self 42 | ) 43 | ) 44 | -------------------------------------------------------------------------------- /app/modules/notice_content/param.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Notice Content Module Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-11-30 02:22:26 11 | """ 12 | 13 | from flask_marshmallow import base_fields 14 | from marshmallow import validate 15 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 16 | from .schema import * 17 | from app.common.param import PagerParameters 18 | 19 | class Notice_contentParameters(JSONParameters, Notice_contentSchema): 20 | """ 21 | Notice Content Parameters 22 | """ 23 | class Meta(Notice_contentSchema.Meta): 24 | pass 25 | 26 | class ExtendPagerParameters(PagerParameters): 27 | receiver = base_fields.Integer(required=False) 28 | 29 | class ReadStatusParameters(JSONParameters): 30 | id = base_fields.Integer(required=True) 31 | status = base_fields.Boolean(required=True) -------------------------------------------------------------------------------- /app/modules/notice_content/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Notice Content Module Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | CREATEDATE: 2019-11-30 02:22:26 11 | """ 12 | 13 | from flask_restplus_patched import ModelSchema 14 | from flask_marshmallow import base_fields 15 | from .model import * 16 | 17 | class Notice_contentSchema(ModelSchema): 18 | """ 19 | Notice Content schema 20 | """ 21 | receive_created = base_fields.DateTime() 22 | receive_id = base_fields.Integer() 23 | class Meta: 24 | model = Notice_content 25 | 26 | class Notice_contentListPagerSchema(Notice_contentSchema): 27 | """ 28 | Notice Content pager schema 29 | """ 30 | total = base_fields.Integer() 31 | rows = base_fields.Nested(Notice_contentSchema, many=True) 32 | class Meta: 33 | pass -------------------------------------------------------------------------------- /app/modules/notice_content_admin/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | notice_content and admin relation module 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init notice_content_admin module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/notice_content_admin/dao.py: -------------------------------------------------------------------------------- 1 | """ 2 | notice_content and admin relation module Data Access Object 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from app import db 13 | from .model import * 14 | from flask import current_app 15 | from sqlalchemy import or_ 16 | 17 | class Notice_contentAdminDao(object): 18 | def delete(self, ids): 19 | """delete 20 | 21 | Args: 22 | ids (list): id list 23 | 24 | Returns: 25 | bool: True or False 26 | 27 | """ 28 | result = False 29 | if ids: 30 | try: 31 | Notice_contentAdmin.query.filter(Notice_contentAdmin.id.in_(tuple(ids))).delete(synchronize_session=False) 32 | db.session.commit() 33 | except Exception as e: 34 | db.session.rollback() 35 | raise Exception(e) 36 | result = True 37 | return result 38 | 39 | def add(self, args): 40 | """add 41 | 42 | Args: 43 | args (OrderedDict): form args 44 | 45 | Returns: 46 | object: Notice_contentAdmin object 47 | 48 | """ 49 | record = None 50 | try: 51 | record = Notice_contentAdmin(**args) 52 | db.session.add(record) 53 | db.session.commit() 54 | except Exception as e: 55 | db.session.rollback() 56 | raise Exception(e) 57 | return record 58 | 59 | def edit(self, args): 60 | """edit 61 | 62 | Args: 63 | args (OrderedDict): form args 64 | 65 | Returns: 66 | object: Notice_contentAdmin object 67 | 68 | """ 69 | id = args.pop('id') 70 | record = Notice_contentAdmin.query.get(id) 71 | if record : 72 | try: 73 | for k,v in args.items(): 74 | setattr(record,k,v) 75 | db.session.commit() 76 | except Exception as e: 77 | db.session.rollback() 78 | raise Exception(e) 79 | return record 80 | 81 | def getById(self, id): 82 | """view by id 83 | 84 | Args: 85 | id (int): id 86 | 87 | Returns: 88 | object: Notice_content object 89 | 90 | """ 91 | record = Notice_contentAdmin.query.get(id) 92 | return record 93 | 94 | def read_edit(self, id, status): 95 | """Read Status Modify # 已读状态修改 96 | 97 | Args: 98 | id (int): notice content id # 内容ID 99 | status (int): status, 1: Already read 0 : unread # 已读状态, 1:已读, 0: 未读 100 | 101 | Returns: 102 | bool: True or False 103 | """ 104 | result = False 105 | record = Notice_contentAdmin.query.get(id) 106 | if record : 107 | try: 108 | record.status = status 109 | db.session.commit() 110 | result = True 111 | except Exception as e: 112 | db.session.rollback() 113 | raise Exception(e) 114 | return result 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /app/modules/notice_content_admin/model.py: -------------------------------------------------------------------------------- 1 | """ 2 | notice_content and admin relation module database models 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class Notice_contentAdmin(TableMixin, db.Model): 16 | __tablename__ = 'notice_content_admin' 17 | notice_content_id = db.Column(db.Integer, nullable=False) 18 | admin_id = db.Column(db.Integer, nullable=False) 19 | __table_args__ = ( 20 | db.UniqueConstraint('notice_content_id', 'admin_id', name='uix_notice_content_admin_notice_content_id_admin_id'), 21 | ) 22 | 23 | def __repr__(self): 24 | return ( 25 | "<{class_name}(" 26 | "id={self.id}, " 27 | "title=\"{self.title}\"" 28 | ")>".format( 29 | class_name=self.__class__.__name__, 30 | self=self 31 | ) 32 | ) 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/modules/notice_content_admin/param.py: -------------------------------------------------------------------------------- 1 | """ 2 | notice_content and admin relation module Request Parameter 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from .schema import * 16 | 17 | class Notice_contentAdminParameters(JSONParameters, Notice_contentAdminSchema): 18 | class Meta(Notice_contentAdminSchema.Meta): 19 | pass 20 | -------------------------------------------------------------------------------- /app/modules/notice_content_admin/resource.py: -------------------------------------------------------------------------------- 1 | """ 2 | RESTful API notice_content and admin relation module resource 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from app.common.status import Status 15 | from app.common.schema import * 16 | from app.common.param import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from app.common.result import Result 19 | from .model import * 20 | from .schema import * 21 | from .param import * 22 | from sqlalchemy import or_ 23 | 24 | ns = Namespace("Notice_contentAdmin", description="RESTful API Notice_contentAdmin resource") 25 | -------------------------------------------------------------------------------- /app/modules/notice_content_admin/schema.py: -------------------------------------------------------------------------------- 1 | """ 2 | notice_content and admin relation module Response Schema 3 | 4 | PROJECT: BaoAI Backend 5 | AUTHOR: henry <703264459@qq.com> 6 | WEBSITE: http://www.baoai.co 7 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 8 | LICENSE: Apache-2.0 9 | CREATEDATE: 2019-11-30 02:22:26 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class Notice_contentAdminSchema(ModelSchema): 17 | class Meta: 18 | model = Notice_contentAdmin 19 | -------------------------------------------------------------------------------- /app/modules/profiles/__init__.py: -------------------------------------------------------------------------------- 1 | """profiles 2 | 3 | Init profiles module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init profiles module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/profiles/dao.py: -------------------------------------------------------------------------------- 1 | """dao 2 | 3 | Data Access Object 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from .model import Profiles 14 | from flask import current_app 15 | 16 | class ProfilesDao(object): 17 | def delete(self, ids): 18 | """delete 19 | 20 | Args: 21 | ids (list): id list 22 | 23 | Returns: 24 | bool: True or False 25 | 26 | """ 27 | result = False 28 | if ids: 29 | try: 30 | Profiles.query.filter(Profiles.id.in_(tuple(ids))).delete(synchronize_session=False) 31 | db.session.commit() 32 | except Exception as e: 33 | db.session.rollback() 34 | raise Exception(e) 35 | result = True 36 | return result 37 | 38 | def deleteByUid(self, uids): 39 | """delete by user id 40 | 41 | Args: 42 | ids (list): user id list 43 | 44 | Returns: 45 | bool: True or False 46 | 47 | """ 48 | result = False 49 | if ids: 50 | try: 51 | Profiles.query.filter(Profiles.uid.in_(tuple(uids))).delete(synchronize_session=False) 52 | db.session.commit() 53 | except Exception as e: 54 | db.session.rollback() 55 | raise Exception(e) 56 | result = True 57 | return result 58 | 59 | def add(self, args): 60 | """add 61 | 62 | Args: 63 | args (OrderedDict): form args 64 | 65 | Returns: 66 | object: Profiles object 67 | 68 | """ 69 | profiles = None 70 | try: 71 | profiles = Profiles(**args) 72 | db.session.add(profiles) 73 | db.session.commit() 74 | except Exception as e: 75 | db.session.rollback() 76 | raise Exception(e) 77 | return profiles 78 | 79 | def edit(self, args): 80 | """edit 81 | 82 | Args: 83 | args (OrderedDict): form args 84 | 85 | Returns: 86 | object: Profiles object 87 | 88 | """ 89 | id = args.pop('id') 90 | uid = args.pop('uid') 91 | record = Profiles.query.get(id) 92 | if record : 93 | try: 94 | for k,v in args.items(): 95 | setattr(record,k,v) 96 | db.session.commit() 97 | except Exception as e: 98 | db.session.rollback() 99 | raise Exception(e) 100 | return record 101 | 102 | def getById(self, id): 103 | """view by id 104 | 105 | Args: 106 | id (int): id 107 | 108 | Returns: 109 | object: Profiles object 110 | 111 | """ 112 | record = Profiles.query.get(id) 113 | return record 114 | 115 | def getByUid(self, uid): 116 | """view by id 117 | 118 | Args: 119 | uid (int): uid 120 | 121 | Returns: 122 | object: Profiles object 123 | 124 | """ 125 | record = Profiles.query.filter(Profiles.uid==uid).first() 126 | return record 127 | 128 | def getList(self, search, sort, order, offset, limit): 129 | """view list 130 | 131 | condition: pagination 132 | 133 | Args: 134 | search (str): search field, default is title field 135 | sort (str): sort field 136 | order (str): order type (asc/desc), default is asc 137 | offset (int): Paging offset 138 | limit (int): Maximum number of records per page 139 | 140 | Returns: 141 | object: data, for example: {"rows": lst, "total":pages.total} 142 | 143 | """ 144 | data = {} 145 | page = offset // limit + 1 146 | temp_query_obj = None 147 | pages = None 148 | if search.strip() != '' : 149 | temp_query_obj = Profiles.query.filter(Profiles.title.like("%{search}%".format(search=search))) 150 | if sort.strip() != "" and temp_query_obj == None: 151 | temp_query_obj = Profiles.query.order_by(eval("Profiles."+sort+"."+order+"()")) 152 | if sort.strip() != "" and temp_query_obj != None: 153 | temp_query_obj = temp_query_obj.order_by(eval("Profiles."+sort+"."+order+"()")) 154 | if temp_query_obj == None : 155 | pages = Profiles.query.order_by(Profiles.pid.asc()).order_by(Profiles.weight.desc()).paginate(page, limit) 156 | else : 157 | pages = temp_query_obj.paginate(page, limit) 158 | lst = [] 159 | if pages: 160 | for item in pages.items: 161 | lst.append(item) 162 | data = {"rows": lst, "total":pages.total} 163 | return data 164 | 165 | 166 | -------------------------------------------------------------------------------- /app/modules/profiles/model.py: -------------------------------------------------------------------------------- 1 | from app import db 2 | from app.common.mixin import * 3 | 4 | class Profiles(TableMixin, db.Model): 5 | """Model Class""" 6 | __tablename__ = 'profiles' 7 | uid = db.Column(db.Integer, unique=True, index=True, nullable=False) 8 | career = db.Column(db.String(50), nullable=True) 9 | education = db.Column(db.String(255), nullable=True) 10 | location = db.Column(db.String(255), nullable=True) 11 | postcode = db.Column(db.String(32), nullable=True) 12 | mobilephone = db.Column(db.String(32), nullable=True) 13 | tel = db.Column(db.String(32), nullable=True) 14 | skills = db.Column(db.String(255), nullable=True) 15 | motto = db.Column(db.String(255), nullable=True) 16 | firstname = db.Column(db.String(32), nullable=True) 17 | lastname = db.Column(db.String(32), nullable=True) 18 | qq = db.Column(db.String(16), nullable=True) 19 | weixin = db.Column(db.String(32), nullable=True) 20 | weibo = db.Column(db.String(32), nullable=True) 21 | idnumber = db.Column(db.String(18), nullable=True) 22 | birthday = db.Column(db.Date(), nullable=True) 23 | email_notice = db.Column(db.Boolean(), nullable=True, default=False) 24 | sms_notice = db.Column(db.Boolean(), nullable=True, default=False) 25 | weixin_notice = db.Column(db.Boolean(), nullable=True, default=False) 26 | 27 | def __repr__(self): 28 | """Class Serialization""" 29 | return '' % self.uid 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/modules/profiles/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from .schema import * 16 | 17 | class ProfilesParameters(JSONParameters, ProfilesSchema): 18 | class Meta(ProfilesSchema.Meta): 19 | pass 20 | 21 | -------------------------------------------------------------------------------- /app/modules/profiles/resource.py: -------------------------------------------------------------------------------- 1 | """resource 2 | 3 | API Resource Of Profiles Module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from flask_restplus._http import HTTPStatus 15 | from app.common.schema import * 16 | from app.common.param import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from .model import * 19 | from .schema import * 20 | from .param import * 21 | import random 22 | import string 23 | from sqlalchemy import or_ 24 | from .dao import * 25 | from app.common.wrap import auth 26 | 27 | ns = Namespace("profiles", description="profiles API Resource") 28 | 29 | @ns.route("/") 30 | class ProfilesAPI(Resource): 31 | @ns.parameters(ProfilesParameters(dump_only=['id'])) 32 | @ns.response(ProfilesSchema(many=False)) 33 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 34 | def post(self, args): 35 | """ 36 | add 37 | """ 38 | profiles = None 39 | profilesDao = ProfilesDao() 40 | try: 41 | profiles = profilesDao.add(args) 42 | except Exception as e: 43 | abort(500, e) 44 | return profiles 45 | 46 | @ns.parameters(ProfilesParameters()) 47 | @ns.response(ProfilesSchema()) 48 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 49 | def put(self, args): 50 | """ 51 | edit 52 | """ 53 | record = None 54 | profilesDao = ProfilesDao() 55 | try: 56 | record = profilesDao.edit(args) 57 | except Exception as e: 58 | abort(500, e) 59 | return record 60 | 61 | @auth() 62 | @ns.response(ProfilesSchema()) 63 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 64 | def get(self): 65 | """ 66 | view 67 | """ 68 | record = None 69 | profilesDao = ProfilesDao() 70 | uid = request.uid 71 | record = profilesDao.getByUid(uid) 72 | if not record : 73 | abort(500, 'Profiles Info is Null') 74 | return record 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/modules/profiles/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class ProfilesSchema(ModelSchema): 17 | """ 18 | Profiles schema . 19 | """ 20 | class Meta: 21 | model = Profiles 22 | 23 | -------------------------------------------------------------------------------- /app/modules/resources/__init__.py: -------------------------------------------------------------------------------- 1 | """resources 2 | 3 | Init resources module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init resources module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/resources/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class Resources(TableMixin, db.Model): 16 | __tablename__ = 'resources' 17 | pid = db.Column(db.Integer, nullable=False) 18 | route = db.Column(db.String(255), nullable=False) 19 | icon = db.Column(db.String(128), nullable=False, default='fa fa-circle-o') 20 | remark = db.Column(db.String(255), nullable=True) 21 | ismenu = db.Column(db.Boolean, nullable=False, default=True) # is menu 1 menu 0 feature 22 | weight = db.Column(db.Integer, nullable=False, default=0) # weight , Used for sorting,Descending order 23 | method = db.Column(db.String(20), nullable=False, default='GET') # Routing access method, value: GET POST DELETE PUT OPTION 24 | name = db.Column(db.String(50), nullable=False, unique=True) 25 | link_type = db.Column(db.String(30), nullable=True, unique=False, index=False) 26 | ''' 27 | Writing of ui-sref transfer parameters: 28 | ui-sref = "page ({parameter 1: parameter 1 value, parameter 2: parameter 2 value,... })" 29 | 30 | For example: 31 | {module_id:0} 32 | ''' 33 | params = db.Column(db.String(255), nullable=True) 34 | locked = db.Column(db.Boolean(), nullable=False, default=False) 35 | # tree 36 | treepath = db.Column(db.String(255), nullable=True) # tree path, for example: .2.120. , 0 does not need to be at the front 37 | treegrade = db.Column(db.Integer, nullable=True) # Tree depth, top level is 0, .2.120. is 2 38 | treepathweight = db.Column(db.String(255), nullable=True) # tree path weight, for example: 2000.2.1500.120. , The weight of node id = 2 is 2000, The weight of node id = 120 is 1500 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/modules/resources/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from .schema import * 16 | 17 | class ResourcesParameters(JSONParameters, ResourcesSchema): 18 | class Meta(ResourcesSchema.Meta): 19 | pass 20 | -------------------------------------------------------------------------------- /app/modules/resources/resource.py: -------------------------------------------------------------------------------- 1 | """resource 2 | 3 | API Resource Of Resources Module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from app.common.status import Status 15 | from app.common.schema import * 16 | from app.common.param import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from app.common.result import Result 19 | from app.common.wrap import auth 20 | from .model import * 21 | from .schema import * 22 | from .param import * 23 | from .dao import * 24 | from app.modules.roles.model import Roles 25 | from app.modules.adminroles.model import AdminRoles 26 | 27 | ns = Namespace("resources", description="resources API Resource") 28 | 29 | @ns.route("/") 30 | class ResourcesAPI(Resource): 31 | @auth() 32 | @ns.response(ResourcesSchema()) 33 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 34 | def get(self): 35 | """ 36 | view 37 | """ 38 | record = None 39 | resourcesDao = ResourcesDao() 40 | id = request.uid 41 | record = resourcesDao.getById(id) 42 | return record 43 | 44 | @auth() 45 | @ns.parameters(ResourcesParameters(dump_only=['id'])) 46 | @ns.response(ResourcesSchema()) 47 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR, description="Internal Server Exception") 48 | def post(self, args): 49 | """ 50 | add 51 | """ 52 | resources = None 53 | resourcesDao = ResourcesDao() 54 | try: 55 | resources = resourcesDao.add(args) 56 | except Exception as e: 57 | abort(500, e) 58 | return resources 59 | 60 | @auth() 61 | @ns.parameters(IDSParameters()) 62 | @ns.response(BasicSchema()) 63 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 64 | def delete(self, args): 65 | """ 66 | del 67 | """ 68 | result = False 69 | ids = args.get('ids') 70 | resourcesDao = ResourcesDao() 71 | try: 72 | result = resourcesDao.delete(ids) 73 | except Exception as e: 74 | abort(500, e) 75 | return {"status":result} 76 | 77 | @auth() 78 | @ns.parameters(ResourcesParameters()) 79 | @ns.response(ResourcesSchema()) 80 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 81 | def put(self, args): 82 | """ 83 | edit 84 | """ 85 | record = None 86 | resourcesDao = ResourcesDao() 87 | try: 88 | record = resourcesDao.edit(args) 89 | except Exception as e: 90 | abort(500, e) 91 | return record 92 | 93 | @ns.route("/list") 94 | class ResourcesListAPI(Resource): 95 | """ 96 | resources module resource list service 97 | """ 98 | @auth() 99 | @ns.parameters(PagerParameters()) 100 | @ns.response(ResourcesListPagerSchema(many=False)) 101 | @ns.response(code=500) 102 | def get(self,args): 103 | """ 104 | view list 105 | """ 106 | data = {} 107 | args.setdefault('search','') 108 | args.setdefault('sort','') 109 | search = args.pop('search') 110 | sort = args.pop('sort') 111 | order = args.pop('order') 112 | offset = args.pop('offset') 113 | limit = args.pop('limit') 114 | resourcesDao = ResourcesDao() 115 | data = resourcesDao.getList(search, sort, order, offset, limit) 116 | return data 117 | 118 | @ns.route("/menu") 119 | class ResourcesMenuAPI(Resource): 120 | 121 | @ns.response(ResourcesSchema(only=['id', 'pid', 'title', 'name'], many=True)) 122 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR, description="Internal Server Exception") 123 | def get(self): 124 | """ 125 | get resources menu 126 | condition: Resources.ismenu==1, Resources.status.in_((1,2)) 127 | """ 128 | record = None 129 | resourcesDao = ResourcesDao() 130 | try: 131 | record = resourcesDao.getMenu() 132 | except Exception as e: 133 | abort(500, e) 134 | return record 135 | 136 | @ns.route("/all") 137 | class ResourcesAllAPI(Resource): 138 | @ns.response(ResourcesSchema(only=['id', 'pid', 'title', 'name'], many=True)) 139 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 140 | def get(self): 141 | """ 142 | get all resources 143 | condition: Resources.status.in_((1,2)) 144 | """ 145 | record = None 146 | resourcesDao = ResourcesDao() 147 | try: 148 | record = resourcesDao.getAll() 149 | except Exception as e: 150 | abort(500, e) 151 | return record 152 | 153 | @ns.route("/routes") 154 | class ResourcesRoutesAPI(Resource): 155 | @auth() 156 | @ns.response(ResourcesSchema(many=True)) 157 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 158 | def get(self): 159 | """ 160 | get corresponding resources by user id 161 | """ 162 | record = None 163 | uid = request.uid 164 | resourcesDao = ResourcesDao() 165 | try: 166 | record = resourcesDao.getRoutes(uid) 167 | except Exception as e: 168 | abort(500, e) 169 | return record 170 | 171 | @ns.route("/weight") 172 | class ResourcesWeightAPI(Resource): 173 | @auth() 174 | @ns.response(ResourcesSchema(only=['weight'], many=True)) 175 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 176 | def get(self): 177 | """ 178 | get weight max value 179 | """ 180 | record = None 181 | resourcesDao = ResourcesDao() 182 | record = resourcesDao.getWeightMax() 183 | return record 184 | 185 | @ns.route("/reorder") 186 | class ResourcesReorderAPI(Resource): 187 | @auth() 188 | @ns.response(BasicSchema()) 189 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 190 | def get(self): 191 | """ 192 | del 193 | """ 194 | result = True 195 | resourcesDao = ResourcesDao() 196 | try: 197 | resourcesDao.setTreeInfoWithAllRecord() 198 | except Exception as e: 199 | abort(500, e) 200 | return {"status":result} -------------------------------------------------------------------------------- /app/modules/resources/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class ResourcesSchema(ModelSchema): 17 | """ 18 | Resources schema exposes all fields. 19 | """ 20 | class Meta: 21 | model = Resources 22 | 23 | class ResourcesListPagerSchema(ModelSchema): 24 | total = base_fields.Integer() 25 | rows = base_fields.Nested(ResourcesSchema, many=True) 26 | class Meta: 27 | pass 28 | -------------------------------------------------------------------------------- /app/modules/roles/__init__.py: -------------------------------------------------------------------------------- 1 | """roles 2 | 3 | Init roles module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init roles module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/roles/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class Roles(TableMixin, db.Model): 16 | __tablename__ = 'roles' 17 | pid = db.Column(db.Integer, nullable=False) 18 | resources = db.Column(db.Text, nullable=False) 19 | remark = db.Column(db.String(255), nullable=True) 20 | weight = db.Column(db.Integer, nullable=False, default=0) # Weight determines order, inverse order 21 | locked = db.Column(db.Boolean(), nullable=False, default=False) 22 | # tree 23 | treepath = db.Column(db.String(255), nullable=True) # tree path, for example: .2.120. , 0 does not need to be at the front 24 | treegrade = db.Column(db.Integer, nullable=True) # Tree depth, top level is 0, .2.120. is 2 25 | treepathweight = db.Column(db.String(255), nullable=True) # tree path weight, for example: 2000.2.1500.120. , The weight of node id = 2 is 2000, The weight of node id = 120 is 1500 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/modules/roles/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from .schema import * 16 | 17 | class RolesParameters(JSONParameters, RolesSchema): 18 | class Meta(RolesSchema.Meta): 19 | pass 20 | -------------------------------------------------------------------------------- /app/modules/roles/resource.py: -------------------------------------------------------------------------------- 1 | """resource 2 | 3 | API Resource Of Roles Module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from app.common.status import Status 15 | from app.common.schema import * 16 | from app.common.param import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from app.common.result import Result 19 | from app.common.wrap import auth 20 | from .model import * 21 | from .schema import * 22 | from .param import * 23 | from .dao import * 24 | from sqlalchemy import or_ 25 | 26 | ns = Namespace("roles", description="roles API Resource") 27 | 28 | @ns.route("/") 29 | class RolesAPI(Resource): 30 | @auth() 31 | @ns.response(RolesSchema()) 32 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 33 | def get(self): 34 | """ 35 | view 36 | """ 37 | record = None 38 | rolesDao = RolesDao() 39 | id = request.uid 40 | record = rolesDao.getById(id) 41 | return record 42 | 43 | @auth() 44 | @ns.parameters(RolesParameters(dump_only=['id'])) 45 | @ns.response(RolesSchema()) 46 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 47 | def post(self, args): 48 | """ 49 | add 50 | """ 51 | roles = None 52 | rolesDao = RolesDao() 53 | try: 54 | roles = rolesDao.add(args) 55 | except Exception as e: 56 | abort(500, e) 57 | return roles 58 | 59 | @auth() 60 | @ns.parameters(IDSParameters()) 61 | @ns.response(BasicSchema(many=False)) 62 | def delete(self, args): 63 | """ 64 | del 65 | """ 66 | result = False 67 | ids = args.get('ids') 68 | rolesDao = RolesDao() 69 | try: 70 | result = rolesDao.delete(ids) 71 | except Exception as e: 72 | abort(500, e) 73 | return {"status":result} 74 | 75 | @auth() 76 | @ns.parameters(RolesParameters()) 77 | @ns.response(RolesSchema(many=False)) 78 | @ns.response(code=HTTPStatus.CONFLICT) 79 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 80 | def put(self, args): 81 | """ 82 | edit 83 | """ 84 | record = None 85 | rolesDao = RolesDao() 86 | try: 87 | record = rolesDao.edit(args) 88 | except Exception as e: 89 | abort(500, e) 90 | return record 91 | 92 | @ns.route("/list") 93 | class RolesListAPI(Resource): 94 | """ 95 | roles module resource list service 96 | """ 97 | @auth() 98 | @ns.parameters(PagerParameters()) 99 | @ns.response(RolesListPagerSchema(many=False)) 100 | @ns.response(code=500) 101 | def get(self,args): 102 | """ 103 | view list 104 | """ 105 | data = {} 106 | args.setdefault('search','') 107 | args.setdefault('sort','') 108 | search = args.pop('search') 109 | sort = args.pop('sort') 110 | order = args.pop('order') 111 | offset = args.pop('offset') 112 | limit = args.pop('limit') 113 | rolesDao = RolesDao() 114 | data = rolesDao.getList(search, sort, order, offset, limit) 115 | return data 116 | 117 | @ns.route("/menu") 118 | class RolesMenuAPI(Resource): 119 | @auth() 120 | @ns.response(RolesSchema(only=['id', 'pid', 'title'], many=True)) 121 | @ns.response(code=HTTPStatus.INTERNAL_SERVER_ERROR) 122 | def get(self): 123 | """view roles list (status=1) # 获取角色菜单列表 124 | """ 125 | rolesDao = RolesDao() 126 | menu = rolesDao.getMenu() 127 | return menu -------------------------------------------------------------------------------- /app/modules/roles/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class RolesSchema(ModelSchema): 17 | class Meta: 18 | model = Roles 19 | 20 | class RolesListPagerSchema(ModelSchema): 21 | total = base_fields.Integer() 22 | rows = base_fields.Nested(RolesSchema, many=True) 23 | class Meta: 24 | pass 25 | 26 | -------------------------------------------------------------------------------- /app/modules/rolesresources/__init__.py: -------------------------------------------------------------------------------- 1 | """rolesresources 2 | 3 | Init rolesresources module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app.api import api 13 | def init_app(app, **kwargs): 14 | """ 15 | Init rolesresources module. 16 | """ 17 | from . import resource 18 | api.add_namespace(resource.ns) -------------------------------------------------------------------------------- /app/modules/rolesresources/dao.py: -------------------------------------------------------------------------------- 1 | """dao 2 | 3 | Data Access Object 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from .model import RolesResources 13 | from app import db 14 | 15 | class RolesResourcesDao(object): 16 | pass 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/modules/rolesresources/model.py: -------------------------------------------------------------------------------- 1 | """model 2 | 3 | Database Table Model 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from app.common.mixin import * 14 | 15 | class RolesResources(TableMixin, db.Model): 16 | __tablename__ = 'roles_resources' 17 | rid = db.Column(db.Integer, nullable=False) 18 | reid = db.Column(db.Integer, nullable=False) 19 | __table_args__ = ( 20 | db.UniqueConstraint('rid', 'reid', name='uix_roles_resources_rid_reid'), 21 | ) 22 | 23 | def __repr__(self): 24 | return '' % self.title 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/modules/rolesresources/param.py: -------------------------------------------------------------------------------- 1 | """param 2 | 3 | Request Parameter 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_marshmallow import base_fields 13 | from marshmallow import validate 14 | from flask_restplus_patched import Parameters, PostFormParameters, JSONParameters 15 | from app.common.param import BasicPagerParameters, PagerParameters, PagerJSONParameters 16 | from .schema import * 17 | 18 | class AdminRolesParameters(JSONParameters, RolesResourcesSchema): 19 | class Meta(RolesResourcesSchema.Meta): 20 | pass 21 | 22 | -------------------------------------------------------------------------------- /app/modules/rolesresources/resource.py: -------------------------------------------------------------------------------- 1 | """resource 2 | 3 | API Resource 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from app import db 13 | from flask import current_app,request 14 | from app.common.status import Status 15 | from app.common.schema import * 16 | from app.common.param import * 17 | from flask_restplus_patched import Resource, Namespace, abort, HTTPStatus 18 | from app.common.result import Result 19 | from .model import * 20 | from .schema import * 21 | from .param import * 22 | from sqlalchemy import or_ 23 | 24 | ns = Namespace("rolesresources", description="Rolesresources Namespace") 25 | 26 | -------------------------------------------------------------------------------- /app/modules/rolesresources/schema.py: -------------------------------------------------------------------------------- 1 | """schema 2 | 3 | Response Schema 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | 12 | from flask_restplus_patched import ModelSchema 13 | from flask_marshmallow import base_fields 14 | from .model import * 15 | 16 | class RolesResourcesSchema(ModelSchema): 17 | class Meta: 18 | model = RolesResources 19 | 20 | -------------------------------------------------------------------------------- /assets/img/baoai/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/admin.png -------------------------------------------------------------------------------- /assets/img/baoai/ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/ai.png -------------------------------------------------------------------------------- /assets/img/baoai/baoai_avatar_160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/baoai_avatar_160.png -------------------------------------------------------------------------------- /assets/img/baoai/baoai_favicon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/baoai_favicon_16.png -------------------------------------------------------------------------------- /assets/img/baoai/baoai_favicon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/baoai_favicon_32.png -------------------------------------------------------------------------------- /assets/img/baoai/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/favicon.ico -------------------------------------------------------------------------------- /assets/img/baoai/know.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/know.png -------------------------------------------------------------------------------- /assets/img/baoai/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/logo.png -------------------------------------------------------------------------------- /assets/img/baoai/quant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/quant.png -------------------------------------------------------------------------------- /assets/img/baoai/soft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/soft.jpg -------------------------------------------------------------------------------- /assets/img/baoai/sys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/sys.png -------------------------------------------------------------------------------- /assets/img/baoai/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/assets/img/baoai/web.png -------------------------------------------------------------------------------- /db/baoai.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/db/baoai.db -------------------------------------------------------------------------------- /deploy/ReadME.md: -------------------------------------------------------------------------------- 1 | # deploy文件夹 2 | 3 | 存放本工程的配置文件 4 | 5 | - `gunicorn_config.py`:gunicorn配置文件 6 | - `baoai.ini`:BaoAI API服务的supervisor配置文件 7 | - `beat.ini`:开启celery定时任务调度的supervisor配置文件 8 | - `worker.ini`:启动celery的supervisor配置文件 9 | - `nginx.conf`:前端Web服务器nginx配置文件,包含反向代理后端的API服务 10 | 11 | 12 | -------------------------------------------------------------------------------- /deploy/baoai.ini: -------------------------------------------------------------------------------- 1 | [program:baoai] 2 | directory = /baoai/BaoAIBack ;启动目录 3 | command = /baoai/BaoAIBack/run_baoai.sh ;启动命令 4 | autostart = true ;在supervisord启动的时候也启动 5 | startsecs = 5 ;启动5秒后没有异常退出,就当作已经正常启动了 6 | autorestart = true ;程序异常退出后自动重启 7 | startretries = 3 ;启动失败自动重试次数,默认是3 8 | user = root ;哪个用户启动 9 | redirect_stderr = true ;把stderr重定向到stdout,默认false 10 | stdout_logfile_maxbytes = 20MB ;stdout日志文件大小,默认50MB 11 | stdout_logfile_backups = 20 ;stdout日志文件备份数 12 | stdout_logfile = /baoai/BaoAIBack/log/baoai.log ;stdout日志文件,需要手动创建目录 -------------------------------------------------------------------------------- /deploy/beat.ini: -------------------------------------------------------------------------------- 1 | [program:beat] 2 | directory = /baoai/BaoAIBack ;启动目录 3 | command = /baoai/BaoAIBack/run_celery_beat.sh ;启动命令 4 | autostart = true ;在supervisord启动的时候也启动 5 | startsecs = 5 ;启动5秒后没有异常退出,就当作已经正常启动了 6 | autorestart = true ;程序异常退出后自动重启 7 | startretries = 3 ;启动失败自动重试次数,默认是3 8 | user = root ;哪个用户启动 9 | redirect_stderr = true ;把stderr重定向到stdout,默认false 10 | stdout_logfile_maxbytes = 20MB ;stdout日志文件大小,默认50MB 11 | stdout_logfile_backups = 20 ;stdout日志文件备份数 12 | stdout_logfile = /baoai/BaoAIBack/log/celery_beat.log ;stdout日志文件,需要手动创建目录 -------------------------------------------------------------------------------- /deploy/gunicorn_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | filename: gunicorn_config.py 3 | install : pip3 install gunicorn 4 | sample: 5 | ```shell 6 | #!/bin/sh 7 | basepath=$(cd `dirname $0`; pwd) 8 | cd ${basepath} 9 | venv/bin/gunicorn manage:app -c deploy/gunicorn_config.py 10 | ``` 11 | PROJECT: BaoAI Backend 12 | AUTHOR: henry <703264459@qq.com> 13 | WEBSITE: http://www.baoai.co 14 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 15 | LICENSE: Apache-2.0 16 | """ 17 | 18 | import sys 19 | import os 20 | import multiprocessing 21 | 22 | # 获取本文件的所在路径 23 | curr_dir = os.path.dirname(os.path.realpath(__file__)) 24 | # 获取上级目录的绝对路径 25 | last_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 26 | # 设置Flask App工作目录路径 27 | app_dir = last_dir 28 | 29 | _file_name = os.path.basename(__file__) 30 | 31 | sys.path.insert(0, app_dir) 32 | 33 | # === Server Socket === 34 | bind = "0.0.0.0:8000" # default: "127.0.0.1:8000" 35 | 36 | # === Server Socket End === 37 | 38 | 39 | 40 | # === Server Mechanics === 41 | 42 | chdir = app_dir 43 | 44 | # Switch worker processes to run as this user. 45 | # user = 46 | 47 | # === Server Mechanics End === 48 | 49 | 50 | 51 | # === Worker Processes === 52 | 53 | # 进程数 54 | workers = 2 55 | # workers = multiprocessing.cpu_count() * 2 + 1 56 | 57 | # 'gevent' or 'sync', default gevent 58 | worker_class = 'sync' 59 | 60 | # The maximum number of simultaneous clients. 61 | worker_connections = 1000 62 | 63 | # Workers silent for more than this many seconds are killed and restarted. 64 | timeout = 30 65 | 66 | # The maximum number of requests a worker will process before restarting. 67 | max_requests = 2000 68 | 69 | # Timeout for graceful workers restart. 70 | graceful_timeout = 30 71 | 72 | # === Worker Processes End === 73 | 74 | 75 | 76 | # === Logging === 77 | 78 | # 错误日志 79 | 80 | # 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置 81 | # The granularity of Error log outputs. 82 | loglevel = 'info' 83 | 84 | # The Error log file to write to. 85 | # Using '-' for FILE makes gunicorn log to stderr. 86 | # Changed in version 19.2: Log to stderr by default. 87 | # errorlog = '-' 88 | errorlog = "/dev/null" 89 | # accesslog = os.path.join(curr_dir, "err.err") 90 | 91 | # Redirect stdout/stderr to Error log. 92 | # default: False 93 | # capture_output = False 94 | 95 | # 访问日志 96 | 97 | # 访问日志文件的路径 98 | # The Access log file to write to. 99 | # '-' means log to stdout. 100 | accesslog = '-' 101 | # accesslog = "/dev/null" 102 | # accesslog = os.path.join(curr_dir, "acc.log") 103 | 104 | # 设置gunicorn访问日志格式,错误日志无法设置 105 | # """ 106 | # h remote address 107 | # l '-' 108 | # u currently '-', may be user name in future releases 109 | # t date of the request 110 | # r status line (e.g. ``GET / HTTP/1.1``) 111 | # s status 112 | # b response length or '-' 113 | # f referer 114 | # a user agent 115 | # T request time in seconds 116 | # D request time in microseconds 117 | # L request time in decimal seconds 118 | # p process ID 119 | # {Header}i request header 120 | # {Header}o response header 121 | # """ 122 | # default: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" 123 | # access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' 124 | 125 | # default: False 126 | # syslog = False 127 | 128 | # === Logging End === 129 | 130 | 131 | 132 | # === Process Naming === 133 | 134 | # A base to use with setproctitle for process naming. 135 | # proc_name = 136 | 137 | # === Process Naming End === 138 | -------------------------------------------------------------------------------- /deploy/nginx.conf: -------------------------------------------------------------------------------- 1 | user www www; 2 | worker_processes auto; 3 | 4 | error_log /data/wwwlogs/error_nginx.log crit; 5 | pid /var/run/nginx.pid; 6 | worker_rlimit_nofile 51200; 7 | 8 | events { 9 | use epoll; 10 | worker_connections 51200; 11 | multi_accept on; 12 | } 13 | 14 | http { 15 | include mime.types; 16 | default_type application/octet-stream; 17 | server_names_hash_bucket_size 128; 18 | client_header_buffer_size 32k; 19 | large_client_header_buffers 4 32k; 20 | client_max_body_size 1024m; 21 | client_body_buffer_size 10m; 22 | sendfile on; 23 | tcp_nopush on; 24 | keepalive_timeout 120; 25 | server_tokens off; 26 | tcp_nodelay on; 27 | 28 | fastcgi_connect_timeout 300; 29 | fastcgi_send_timeout 300; 30 | fastcgi_read_timeout 300; 31 | fastcgi_buffer_size 64k; 32 | fastcgi_buffers 4 64k; 33 | fastcgi_busy_buffers_size 128k; 34 | fastcgi_temp_file_write_size 128k; 35 | fastcgi_intercept_errors on; 36 | 37 | #Gzip Compression 38 | gzip on; 39 | gzip_buffers 16 8k; 40 | gzip_comp_level 6; 41 | gzip_http_version 1.1; 42 | gzip_min_length 256; 43 | gzip_proxied any; 44 | gzip_vary on; 45 | gzip_types 46 | text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml 47 | text/javascript application/javascript application/x-javascript 48 | text/x-json application/json application/x-web-app-manifest+json 49 | text/css text/plain text/x-component 50 | font/opentype application/x-font-ttf application/vnd.ms-fontobject 51 | image/x-icon; 52 | gzip_disable "MSIE [1-6]\.(?!.*SV1)"; 53 | 54 | ##Brotli Compression 55 | #brotli on; 56 | #brotli_comp_level 6; 57 | #brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml; 58 | 59 | ##If you have a lot of static files to serve through Nginx then caching of the files' metadata (not the actual files' contents) can save some latency. 60 | #open_file_cache max=1000 inactive=20s; 61 | #open_file_cache_valid 30s; 62 | #open_file_cache_min_uses 2; 63 | #open_file_cache_errors on; 64 | 65 | ######################## default ############################ 66 | upstream node { 67 | server 127.0.0.1:8000; 68 | } 69 | server { 70 | listen 80; 71 | server_name _; 72 | access_log /data/wwwlogs/access_nginx.log combined; 73 | root /baoai/baoaifront; 74 | index index.html index.htm index.php; 75 | if ($request_uri ~ ^/(static|login|register|password|app|api)) { 76 | set $cookie_nocache 1; 77 | } 78 | location / { 79 | index index.html index.htm index.php; 80 | try_files $uri $uri/ /index.html =404; 81 | # proxy_cache cache; 82 | # proxy_cache_valid 200 304 12h; 83 | # proxy_cache_valid any 10m; 84 | # add_header Nginx-Cache "$upstream_cache_status"; 85 | # proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; 86 | # proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; 87 | # proxy_no_cache $http_pargma $http_authorization; 88 | } 89 | 90 | location ^~ /static/ { 91 | proxy_pass http://node; 92 | proxy_set_header Host $http_host; 93 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 94 | # proxy_cache cache; 95 | # proxy_cache_valid 200 304 12h; 96 | # proxy_cache_valid any 10m; 97 | # add_header Nginx-Cache "$upstream_cache_status"; 98 | # proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; 99 | # proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; 100 | # proxy_no_cache $http_pargma $http_authorization; 101 | } 102 | 103 | location ^~ /api/ { # ƥ����??host/api/ ��url 104 | proxy_pass http://node; 105 | proxy_set_header Host $host; 106 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 107 | # proxy_cache cache; 108 | # proxy_cache_valid 200 304 12h; 109 | # proxy_cache_valid any 10m; 110 | # add_header Nginx-Cache "$upstream_cache_status"; 111 | # proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; 112 | # proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; 113 | # proxy_no_cache $http_pargma $http_authorization; 114 | } 115 | #error_page 404 /404.html; 116 | #error_page 502 /502.html; 117 | location /nginx_status { 118 | stub_status on; 119 | access_log off; 120 | allow 127.0.0.1; 121 | deny all; 122 | } 123 | 124 | # location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ { 125 | # expires 30d; 126 | # access_log off; 127 | # } 128 | # location ~ .*\.(js|css)?$ { 129 | # expires 7d; 130 | # access_log off; 131 | # } 132 | location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) { 133 | deny all; 134 | } 135 | } 136 | ########################## vhost ############################# 137 | include vhost/*.conf; 138 | } 139 | -------------------------------------------------------------------------------- /deploy/worker.ini: -------------------------------------------------------------------------------- 1 | [program:worker] 2 | directory = /baoai/BaoAIBack ;启动目录 3 | command = /baoai/BaoAIBack/run_celery_worker.sh ;启动命令 4 | autostart = true ;在supervisord启动的时候也启动 5 | startsecs = 5 ;启动5秒后没有异常退出,就当作已经正常启动了 6 | autorestart = true ;程序异常退出后自动重启 7 | startretries = 3 ;启动失败自动重试次数,默认是3 8 | user = root ;哪个用户启动 9 | redirect_stderr = true ;把stderr重定向到stdout,默认false 10 | stdout_logfile_maxbytes = 20MB ;stdout日志文件大小,默认50MB 11 | stdout_logfile_backups = 20 ;stdout日志文件备份数 12 | stdout_logfile = /baoai/BaoAIBack/log/celery_worker.log ;stdout日志文件,需要手动创建目录 -------------------------------------------------------------------------------- /flask_restplus/__about__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '0.11.0' 3 | __description__ = 'Fully featured framework for fast, easy and documented API development with Flask' 4 | -------------------------------------------------------------------------------- /flask_restplus/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | 4 | from . import fields, reqparse, apidoc, inputs, cors 5 | from .api import Api # noqa 6 | from .marshalling import marshal, marshal_with, marshal_with_field # noqa 7 | from .mask import Mask 8 | from .model import Model, OrderedModel, SchemaModel # noqa 9 | from .namespace import Namespace # noqa 10 | from .resource import Resource # noqa 11 | from .errors import abort, RestError, SpecsError, ValidationError 12 | from .swagger import Swagger 13 | from .__about__ import __version__, __description__ 14 | 15 | __all__ = ( 16 | '__version__', 17 | '__description__', 18 | 'Api', 19 | 'Resource', 20 | 'apidoc', 21 | 'marshal', 22 | 'marshal_with', 23 | 'marshal_with_field', 24 | 'Mask', 25 | 'Model', 26 | 'OrderedModel', 27 | 'SchemaModel', 28 | 'abort', 29 | 'cors', 30 | 'fields', 31 | 'inputs', 32 | 'reqparse', 33 | 'RestError', 34 | 'SpecsError', 35 | 'Swagger', 36 | 'ValidationError', 37 | ) 38 | -------------------------------------------------------------------------------- /flask_restplus/_http.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | This file is backported from Python 3.5 http built-in module. 4 | """ 5 | 6 | from enum import IntEnum 7 | 8 | 9 | class HTTPStatus(IntEnum): 10 | """HTTP status codes and reason phrases 11 | 12 | Status codes from the following RFCs are all observed: 13 | 14 | * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616 15 | * RFC 6585: Additional HTTP Status Codes 16 | * RFC 3229: Delta encoding in HTTP 17 | * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518 18 | * RFC 5842: Binding Extensions to WebDAV 19 | * RFC 7238: Permanent Redirect 20 | * RFC 2295: Transparent Content Negotiation in HTTP 21 | * RFC 2774: An HTTP Extension Framework 22 | """ 23 | def __new__(cls, value, phrase, description=''): 24 | obj = int.__new__(cls, value) 25 | obj._value_ = value 26 | 27 | obj.phrase = phrase 28 | obj.description = description 29 | return obj 30 | 31 | def __str__(self): 32 | return str(self.value) 33 | 34 | # informational 35 | CONTINUE = 100, 'Continue', 'Request received, please continue' 36 | SWITCHING_PROTOCOLS = (101, 'Switching Protocols', 37 | 'Switching to new protocol; obey Upgrade header') 38 | PROCESSING = 102, 'Processing' 39 | 40 | # success 41 | OK = 200, 'OK', 'Request fulfilled, document follows' 42 | CREATED = 201, 'Created', 'Document created, URL follows' 43 | ACCEPTED = (202, 'Accepted', 44 | 'Request accepted, processing continues off-line') 45 | NON_AUTHORITATIVE_INFORMATION = (203, 46 | 'Non-Authoritative Information', 'Request fulfilled from cache') 47 | NO_CONTENT = 204, 'No Content', 'Request fulfilled, nothing follows' 48 | RESET_CONTENT = 205, 'Reset Content', 'Clear input form for further input' 49 | PARTIAL_CONTENT = 206, 'Partial Content', 'Partial content follows' 50 | MULTI_STATUS = 207, 'Multi-Status' 51 | ALREADY_REPORTED = 208, 'Already Reported' 52 | IM_USED = 226, 'IM Used' 53 | 54 | # redirection 55 | MULTIPLE_CHOICES = (300, 'Multiple Choices', 56 | 'Object has several resources -- see URI list') 57 | MOVED_PERMANENTLY = (301, 'Moved Permanently', 58 | 'Object moved permanently -- see URI list') 59 | FOUND = 302, 'Found', 'Object moved temporarily -- see URI list' 60 | SEE_OTHER = 303, 'See Other', 'Object moved -- see Method and URL list' 61 | NOT_MODIFIED = (304, 'Not Modified', 62 | 'Document has not changed since given time') 63 | USE_PROXY = (305, 'Use Proxy', 64 | 'You must use proxy specified in Location to access this resource') 65 | TEMPORARY_REDIRECT = (307, 'Temporary Redirect', 66 | 'Object moved temporarily -- see URI list') 67 | PERMANENT_REDIRECT = (308, 'Permanent Redirect', 68 | 'Object moved temporarily -- see URI list') 69 | 70 | # client error 71 | BAD_REQUEST = (400, 'Bad Request', 72 | 'Bad request syntax or unsupported method') 73 | UNAUTHORIZED = (401, 'Unauthorized', 74 | 'No permission -- see authorization schemes') 75 | PAYMENT_REQUIRED = (402, 'Payment Required', 76 | 'No payment -- see charging schemes') 77 | FORBIDDEN = (403, 'Forbidden', 78 | 'Request forbidden -- authorization will not help') 79 | NOT_FOUND = (404, 'Not Found', 80 | 'Nothing matches the given URI') 81 | METHOD_NOT_ALLOWED = (405, 'Method Not Allowed', 82 | 'Specified method is invalid for this resource') 83 | NOT_ACCEPTABLE = (406, 'Not Acceptable', 84 | 'URI not available in preferred format') 85 | PROXY_AUTHENTICATION_REQUIRED = (407, 86 | 'Proxy Authentication Required', 87 | 'You must authenticate with this proxy before proceeding') 88 | REQUEST_TIMEOUT = (408, 'Request Timeout', 89 | 'Request timed out; try again later') 90 | CONFLICT = 409, 'Conflict', 'Request conflict' 91 | GONE = (410, 'Gone', 92 | 'URI no longer exists and has been permanently removed') 93 | LENGTH_REQUIRED = (411, 'Length Required', 94 | 'Client must specify Content-Length') 95 | PRECONDITION_FAILED = (412, 'Precondition Failed', 96 | 'Precondition in headers is false') 97 | REQUEST_ENTITY_TOO_LARGE = (413, 'Request Entity Too Large', 98 | 'Entity is too large') 99 | REQUEST_URI_TOO_LONG = (414, 'Request-URI Too Long', 100 | 'URI is too long') 101 | UNSUPPORTED_MEDIA_TYPE = (415, 'Unsupported Media Type', 102 | 'Entity body in unsupported format') 103 | REQUESTED_RANGE_NOT_SATISFIABLE = (416, 104 | 'Requested Range Not Satisfiable', 105 | 'Cannot satisfy request range') 106 | EXPECTATION_FAILED = (417, 'Expectation Failed', 107 | 'Expect condition could not be satisfied') 108 | UNPROCESSABLE_ENTITY = 422, 'Unprocessable Entity' 109 | LOCKED = 423, 'Locked' 110 | FAILED_DEPENDENCY = 424, 'Failed Dependency' 111 | UPGRADE_REQUIRED = 426, 'Upgrade Required' 112 | PRECONDITION_REQUIRED = (428, 'Precondition Required', 113 | 'The origin server requires the request to be conditional') 114 | TOO_MANY_REQUESTS = (429, 'Too Many Requests', 115 | 'The user has sent too many requests in ' 116 | 'a given amount of time ("rate limiting")') 117 | REQUEST_HEADER_FIELDS_TOO_LARGE = (431, 118 | 'Request Header Fields Too Large', 119 | 'The server is unwilling to process the request because its header ' 120 | 'fields are too large') 121 | 122 | # server errors 123 | INTERNAL_SERVER_ERROR = (500, 'Internal Server Error', 124 | 'Server got itself in trouble') 125 | NOT_IMPLEMENTED = (501, 'Not Implemented', 126 | 'Server does not support this operation') 127 | BAD_GATEWAY = (502, 'Bad Gateway', 128 | 'Invalid responses from another server/proxy') 129 | SERVICE_UNAVAILABLE = (503, 'Service Unavailable', 130 | 'The server cannot process the request due to a high load') 131 | GATEWAY_TIMEOUT = (504, 'Gateway Timeout', 132 | 'The gateway server did not receive a timely response') 133 | HTTP_VERSION_NOT_SUPPORTED = (505, 'HTTP Version Not Supported', 134 | 'Cannot fulfill request') 135 | VARIANT_ALSO_NEGOTIATES = 506, 'Variant Also Negotiates' 136 | INSUFFICIENT_STORAGE = 507, 'Insufficient Storage' 137 | LOOP_DETECTED = 508, 'Loop Detected' 138 | NOT_EXTENDED = 510, 'Not Extended' 139 | NETWORK_AUTHENTICATION_REQUIRED = (511, 140 | 'Network Authentication Required', 141 | 'The client needs to authenticate to gain network access') 142 | -------------------------------------------------------------------------------- /flask_restplus/apidoc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from flask import url_for, Blueprint, render_template 5 | 6 | 7 | class Apidoc(Blueprint): 8 | ''' 9 | Allow to know if the blueprint has already been registered 10 | until https://github.com/mitsuhiko/flask/pull/1301 is merged 11 | ''' 12 | def __init__(self, *args, **kwargs): 13 | self.registered = False 14 | super(Apidoc, self).__init__(*args, **kwargs) 15 | 16 | def register(self, *args, **kwargs): 17 | super(Apidoc, self).register(*args, **kwargs) 18 | self.registered = True 19 | 20 | 21 | apidoc = Apidoc('restplus_doc', __name__, 22 | template_folder='templates', 23 | static_folder='static', 24 | static_url_path='/swaggerui', 25 | ) 26 | 27 | 28 | @apidoc.add_app_template_global 29 | def swagger_static(filename): 30 | return url_for('restplus_doc.static', filename=filename) 31 | 32 | 33 | def ui_for(api): 34 | '''Render a SwaggerUI for a given API''' 35 | return render_template('swagger-ui.html', title=api.title, 36 | specs_url=api.specs_url) 37 | -------------------------------------------------------------------------------- /flask_restplus/cors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from datetime import timedelta 5 | from flask import make_response, request, current_app 6 | from functools import update_wrapper 7 | 8 | 9 | def crossdomain(origin=None, methods=None, headers=None, expose_headers=None, 10 | max_age=21600, attach_to_all=True, 11 | automatic_options=True, credentials=False): 12 | """ 13 | http://flask.pocoo.org/snippets/56/ 14 | """ 15 | if methods is not None: 16 | methods = ', '.join(sorted(x.upper() for x in methods)) 17 | if headers is not None and not isinstance(headers, str): 18 | headers = ', '.join(x.upper() for x in headers) 19 | if expose_headers is not None and not isinstance(expose_headers, str): 20 | expose_headers = ', '.join(x.upper() for x in expose_headers) 21 | if not isinstance(origin, str): 22 | origin = ', '.join(origin) 23 | if isinstance(max_age, timedelta): 24 | max_age = max_age.total_seconds() 25 | 26 | def get_methods(): 27 | if methods is not None: 28 | return methods 29 | 30 | options_resp = current_app.make_default_options_response() 31 | return options_resp.headers['allow'] 32 | 33 | def decorator(f): 34 | def wrapped_function(*args, **kwargs): 35 | if automatic_options and request.method == 'OPTIONS': 36 | resp = current_app.make_default_options_response() 37 | else: 38 | resp = make_response(f(*args, **kwargs)) 39 | if not attach_to_all and request.method != 'OPTIONS': 40 | return resp 41 | 42 | h = resp.headers 43 | 44 | h['Access-Control-Allow-Origin'] = origin 45 | h['Access-Control-Allow-Methods'] = get_methods() 46 | h['Access-Control-Max-Age'] = str(max_age) 47 | if credentials: 48 | h['Access-Control-Allow-Credentials'] = 'true' 49 | if headers is not None: 50 | h['Access-Control-Allow-Headers'] = headers 51 | if expose_headers is not None: 52 | h['Access-Control-Expose-Headers'] = expose_headers 53 | return resp 54 | 55 | f.provide_automatic_options = False 56 | return update_wrapper(wrapped_function, f) 57 | return decorator 58 | -------------------------------------------------------------------------------- /flask_restplus/errors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import flask 5 | 6 | from werkzeug.exceptions import HTTPException 7 | 8 | from ._http import HTTPStatus 9 | 10 | __all__ = ( 11 | 'abort', 12 | 'RestError', 13 | 'ValidationError', 14 | 'SpecsError', 15 | ) 16 | 17 | 18 | def abort(code=HTTPStatus.INTERNAL_SERVER_ERROR, message=None, **kwargs): 19 | ''' 20 | Properly abort the current request. 21 | 22 | Raise a `HTTPException` for the given status `code`. 23 | Attach any keyword arguments to the exception for later processing. 24 | 25 | :param int code: The associated HTTP status code 26 | :param str message: An optional details message 27 | :param kwargs: Any additional data to pass to the error payload 28 | :raise HTTPException: 29 | ''' 30 | try: 31 | flask.abort(code) 32 | except HTTPException as e: 33 | if message: 34 | kwargs['message'] = str(message) 35 | if kwargs: 36 | e.data = kwargs 37 | raise 38 | 39 | 40 | class RestError(Exception): 41 | '''Base class for all Flask-Restplus Errors''' 42 | def __init__(self, msg): 43 | self.msg = msg 44 | 45 | def __str__(self): 46 | return self.msg 47 | 48 | 49 | class ValidationError(RestError): 50 | '''An helper class for validation errors.''' 51 | pass 52 | 53 | 54 | class SpecsError(RestError): 55 | '''An helper class for incoherent specifications.''' 56 | pass 57 | -------------------------------------------------------------------------------- /flask_restplus/mask.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals, absolute_import 3 | 4 | import logging 5 | import re 6 | import six 7 | 8 | from collections import OrderedDict 9 | from inspect import isclass 10 | 11 | from .errors import RestError 12 | 13 | log = logging.getLogger(__name__) 14 | 15 | LEXER = re.compile(r'\{|\}|\,|[\w_:\-\*]+') 16 | 17 | 18 | class MaskError(RestError): 19 | '''Raised when an error occurs on mask''' 20 | pass 21 | 22 | 23 | class ParseError(MaskError): 24 | '''Raised when the mask parsing failed''' 25 | pass 26 | 27 | 28 | class Mask(OrderedDict): 29 | ''' 30 | Hold a parsed mask. 31 | 32 | :param str|dict|Mask mask: A mask, parsed or not 33 | :param bool skip: If ``True``, missing fields won't appear in result 34 | ''' 35 | def __init__(self, mask=None, skip=False, **kwargs): 36 | self.skip = skip 37 | if isinstance(mask, six.string_types): 38 | super(Mask, self).__init__() 39 | self.parse(mask) 40 | elif isinstance(mask, (dict, OrderedDict)): 41 | super(Mask, self).__init__(mask, **kwargs) 42 | else: 43 | self.skip = skip 44 | super(Mask, self).__init__(**kwargs) 45 | 46 | def parse(self, mask): 47 | ''' 48 | Parse a fields mask. 49 | Expect something in the form:: 50 | 51 | {field,nested{nested_field,another},last} 52 | 53 | External brackets are optionals so it can also be written:: 54 | 55 | field,nested{nested_field,another},last 56 | 57 | All extras characters will be ignored. 58 | 59 | :param str mask: the mask string to parse 60 | :raises ParseError: when a mask is unparseable/invalid 61 | 62 | ''' 63 | if not mask: 64 | return 65 | 66 | mask = self.clean(mask) 67 | fields = self 68 | previous = None 69 | stack = [] 70 | 71 | for token in LEXER.findall(mask): 72 | if token == '{': 73 | if previous not in fields: 74 | raise ParseError('Unexpected opening bracket') 75 | fields[previous] = Mask(skip=self.skip) 76 | stack.append(fields) 77 | fields = fields[previous] 78 | elif token == '}': 79 | if not stack: 80 | raise ParseError('Unexpected closing bracket') 81 | fields = stack.pop() 82 | elif token == ',': 83 | if previous in (',', '{', None): 84 | raise ParseError('Unexpected comma') 85 | else: 86 | fields[token] = True 87 | 88 | previous = token 89 | 90 | if stack: 91 | raise ParseError('Missing closing bracket') 92 | 93 | def clean(self, mask): 94 | '''Remove unecessary characters''' 95 | mask = mask.replace('\n', '').strip() 96 | # External brackets are optional 97 | if mask[0] == '{': 98 | if mask[-1] != '}': 99 | raise ParseError('Missing closing bracket') 100 | mask = mask[1:-1] 101 | return mask 102 | 103 | def apply(self, data): 104 | ''' 105 | Apply a fields mask to the data. 106 | 107 | :param data: The data or model to apply mask on 108 | :raises MaskError: when unable to apply the mask 109 | 110 | ''' 111 | from . import fields 112 | # Should handle lists 113 | if isinstance(data, (list, tuple, set)): 114 | return [self.apply(d) for d in data] 115 | elif isinstance(data, (fields.Nested, fields.List, fields.Polymorph)): 116 | return data.clone(self) 117 | elif type(data) == fields.Raw: 118 | return fields.Raw(default=data.default, attribute=data.attribute, mask=self) 119 | elif data == fields.Raw: 120 | return fields.Raw(mask=self) 121 | elif isinstance(data, fields.Raw) or isclass(data) and issubclass(data, fields.Raw): 122 | # Not possible to apply a mask on these remaining fields types 123 | raise MaskError('Mask is inconsistent with model') 124 | # Should handle objects 125 | elif (not isinstance(data, (dict, OrderedDict)) and hasattr(data, '__dict__')): 126 | data = data.__dict__ 127 | 128 | return self.filter_data(data) 129 | 130 | def filter_data(self, data): 131 | ''' 132 | Handle the data filtering given a parsed mask 133 | 134 | :param dict data: the raw data to filter 135 | :param list mask: a parsed mask tofilter against 136 | :param bool skip: whether or not to skip missing fields 137 | 138 | ''' 139 | out = {} 140 | for field, content in six.iteritems(self): 141 | if field == '*': 142 | continue 143 | elif isinstance(content, Mask): 144 | nested = data.get(field, None) 145 | if self.skip and nested is None: 146 | continue 147 | elif nested is None: 148 | out[field] = None 149 | else: 150 | out[field] = content.apply(nested) 151 | elif self.skip and field not in data: 152 | continue 153 | else: 154 | out[field] = data.get(field, None) 155 | 156 | if '*' in self.keys(): 157 | for key, value in six.iteritems(data): 158 | if key not in out: 159 | out[key] = value 160 | return out 161 | 162 | def __str__(self): 163 | return '{{{0}}}'.format(','.join([ 164 | ''.join((k, str(v))) if isinstance(v, Mask) else k 165 | for k, v in six.iteritems(self) 166 | ])) 167 | 168 | 169 | def apply(data, mask, skip=False): 170 | ''' 171 | Apply a fields mask to the data. 172 | 173 | :param data: The data or model to apply mask on 174 | :param str|Mask mask: the mask (parsed or not) to apply on data 175 | :param bool skip: If rue, missing field won't appear in result 176 | :raises MaskError: when unable to apply the mask 177 | 178 | ''' 179 | return Mask(mask, skip).apply(data) 180 | -------------------------------------------------------------------------------- /flask_restplus/representations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals, absolute_import 3 | 4 | try: 5 | from ujson import dumps 6 | except ImportError: 7 | from json import dumps 8 | 9 | from flask import make_response, current_app 10 | 11 | 12 | def output_json(data, code, headers=None): 13 | '''Makes a Flask response with a JSON encoded body''' 14 | 15 | settings = current_app.config.get('RESTPLUS_JSON', {}) 16 | 17 | # If we're in debug mode, and the indent is not set, we set it to a 18 | # reasonable value here. Note that this won't override any existing value 19 | # that was set. 20 | if current_app.debug: 21 | settings.setdefault('indent', 4) 22 | 23 | # always end the json dumps with a new line 24 | # see https://github.com/mitsuhiko/flask/pull/1262 25 | dumped = dumps(data, **settings) + "\n" 26 | 27 | resp = make_response(dumped, code) 28 | resp.headers.extend(headers or {}) 29 | return resp 30 | -------------------------------------------------------------------------------- /flask_restplus/resource.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from flask import request 5 | from flask.views import MethodView 6 | from werkzeug.wrappers import BaseResponse 7 | 8 | from .model import ModelBase 9 | 10 | from .utils import unpack 11 | 12 | 13 | class Resource(MethodView): 14 | ''' 15 | Represents an abstract RESTPlus resource. 16 | 17 | Concrete resources should extend from this class 18 | and expose methods for each supported HTTP method. 19 | If a resource is invoked with an unsupported HTTP method, 20 | the API will return a response with status 405 Method Not Allowed. 21 | Otherwise the appropriate method is called and passed all arguments 22 | from the url rule used when adding the resource to an Api instance. 23 | See :meth:`~flask_restplus.Api.add_resource` for details. 24 | ''' 25 | 26 | representations = None 27 | method_decorators = [] 28 | 29 | def __init__(self, api=None, *args, **kwargs): 30 | self.api = api 31 | 32 | def dispatch_request(self, *args, **kwargs): 33 | # Taken from flask 34 | meth = getattr(self, request.method.lower(), None) 35 | if meth is None and request.method == 'HEAD': 36 | meth = getattr(self, 'get', None) 37 | assert meth is not None, 'Unimplemented method %r' % request.method 38 | 39 | for decorator in self.method_decorators: 40 | meth = decorator(meth) 41 | 42 | self.validate_payload(meth) 43 | 44 | resp = meth(*args, **kwargs) 45 | 46 | if isinstance(resp, BaseResponse): 47 | return resp 48 | 49 | representations = self.representations or {} 50 | 51 | mediatype = request.accept_mimetypes.best_match(representations, default=None) 52 | if mediatype in representations: 53 | data, code, headers = unpack(resp) 54 | resp = representations[mediatype](data, code, headers) 55 | resp.headers['Content-Type'] = mediatype 56 | return resp 57 | 58 | return resp 59 | 60 | def __validate_payload(self, expect, collection=False): 61 | ''' 62 | :param ModelBase expect: the expected model for the input payload 63 | :param bool collection: False if a single object of a resource is 64 | expected, True if a collection of objects of a resource is expected. 65 | ''' 66 | # TODO: proper content negotiation 67 | data = request.get_json() 68 | if collection: 69 | data = data if isinstance(data, list) else [data] 70 | for obj in data: 71 | expect.validate(obj, self.api.refresolver, self.api.format_checker) 72 | else: 73 | expect.validate(data, self.api.refresolver, self.api.format_checker) 74 | 75 | def validate_payload(self, func): 76 | '''Perform a payload validation on expected model if necessary''' 77 | if getattr(func, '__apidoc__', False) is not False: 78 | doc = func.__apidoc__ 79 | validate = doc.get('validate', None) 80 | validate = validate if validate is not None else self.api._validate 81 | if validate: 82 | for expect in doc.get('expect', []): 83 | # TODO: handle third party handlers 84 | if isinstance(expect, list) and len(expect) == 1: 85 | if isinstance(expect[0], ModelBase): 86 | self.__validate_payload(expect[0], collection=True) 87 | if isinstance(expect, ModelBase): 88 | self.__validate_payload(expect, collection=False) 89 | -------------------------------------------------------------------------------- /flask_restplus/static/droid-sans.css: -------------------------------------------------------------------------------- 1 | /* droid-sans-400normal - latin */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-display: swap; 6 | font-weight: 400; 7 | src: 8 | local('Droid Sans Regular '), 9 | local('Droid Sans-Regular'), 10 | url('./files/droid-sans-latin-400.woff2') format('woff2'), /* Super Modern Browsers */ 11 | url('./files/droid-sans-latin-400.woff') format('woff'); /* Modern Browsers */ 12 | } 13 | 14 | /* droid-sans-700normal - latin */ 15 | @font-face { 16 | font-family: 'Droid Sans'; 17 | font-style: normal; 18 | font-display: swap; 19 | font-weight: 700; 20 | src: 21 | local('Droid Sans Bold '), 22 | local('Droid Sans-Bold'), 23 | url('./files/droid-sans-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 24 | url('./files/droid-sans-latin-700.woff') format('woff'); /* Modern Browsers */ 25 | } 26 | 27 | -------------------------------------------------------------------------------- /flask_restplus/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/favicon-16x16.png -------------------------------------------------------------------------------- /flask_restplus/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/favicon-32x32.png -------------------------------------------------------------------------------- /flask_restplus/static/files/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/files/.npmignore -------------------------------------------------------------------------------- /flask_restplus/static/files/droid-sans-latin-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/files/droid-sans-latin-400.woff -------------------------------------------------------------------------------- /flask_restplus/static/files/droid-sans-latin-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/files/droid-sans-latin-400.woff2 -------------------------------------------------------------------------------- /flask_restplus/static/files/droid-sans-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/files/droid-sans-latin-700.woff -------------------------------------------------------------------------------- /flask_restplus/static/files/droid-sans-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/flask_restplus/static/files/droid-sans-latin-700.woff2 -------------------------------------------------------------------------------- /flask_restplus/static/swagger-ui.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""} -------------------------------------------------------------------------------- /flask_restplus/templates/swagger-ui-css.html: -------------------------------------------------------------------------------- 1 | {# 3 | 5 | 7 | 9 | 11 | #} 12 | 13 | 14 | 34 | -------------------------------------------------------------------------------- /flask_restplus/templates/swagger-ui-libs.html: -------------------------------------------------------------------------------- 1 | {# 2 | {% if config.SWAGGER_UI_LANGUAGES %} 3 | 4 | {% for lang in config.SWAGGER_UI_LANGUAGES %} 5 | {% set filename = 'lang/{0}.js'.format(lang) %} 6 | 7 | {% endfor%} 8 | {% endif %} 9 | #} 10 | 11 | 12 | -------------------------------------------------------------------------------- /flask_restplus/templates/swagger-ui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ title }} 5 | {% include 'swagger-ui-css.html' %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | {% include 'swagger-ui-libs.html' %} 50 | 77 | 78 | 79 | 84 | -------------------------------------------------------------------------------- /flask_restplus/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import re 5 | 6 | from collections import OrderedDict 7 | from copy import deepcopy 8 | from six import iteritems 9 | 10 | from ._http import HTTPStatus 11 | 12 | 13 | FIRST_CAP_RE = re.compile('(.)([A-Z][a-z]+)') 14 | ALL_CAP_RE = re.compile('([a-z0-9])([A-Z])') 15 | 16 | 17 | __all__ = ('merge', 'camel_to_dash', 'default_id', 'not_none', 'not_none_sorted', 'unpack') 18 | 19 | 20 | def merge(first, second): 21 | ''' 22 | Recursively merges two dictionnaries. 23 | 24 | Second dictionnary values will take precedance over those from the first one. 25 | Nested dictionnaries are merged too. 26 | 27 | :param dict first: The first dictionnary 28 | :param dict second: The second dictionnary 29 | :return: the resulting merged dictionnary 30 | :rtype: dict 31 | ''' 32 | if not isinstance(second, dict): 33 | return second 34 | result = deepcopy(first) 35 | for key, value in iteritems(second): 36 | if key in result and isinstance(result[key], dict): 37 | result[key] = merge(result[key], value) 38 | else: 39 | result[key] = deepcopy(value) 40 | return result 41 | 42 | 43 | def camel_to_dash(value): 44 | ''' 45 | Transform a CamelCase string into a low_dashed one 46 | 47 | :param str value: a CamelCase string to transform 48 | :return: the low_dashed string 49 | :rtype: str 50 | ''' 51 | first_cap = FIRST_CAP_RE.sub(r'\1_\2', value) 52 | return ALL_CAP_RE.sub(r'\1_\2', first_cap).lower() 53 | 54 | 55 | def default_id(resource, method): 56 | '''Default operation ID generator''' 57 | return '{0}_{1}'.format(method, camel_to_dash(resource)) 58 | 59 | 60 | def not_none(data): 61 | ''' 62 | Remove all keys where value is None 63 | 64 | :param dict data: A dictionnary with potentialy some values set to None 65 | :return: The same dictionnary without the keys with values to ``None`` 66 | :rtype: dict 67 | ''' 68 | return dict((k, v) for k, v in iteritems(data) if v is not None) 69 | 70 | 71 | def not_none_sorted(data): 72 | ''' 73 | Remove all keys where value is None 74 | 75 | :param OrderedDict data: A dictionnary with potentialy some values set to None 76 | :return: The same dictionnary without the keys with values to ``None`` 77 | :rtype: OrderedDict 78 | ''' 79 | return OrderedDict((k, v) for k, v in sorted(iteritems(data)) if v is not None) 80 | 81 | 82 | def unpack(response, default_code=HTTPStatus.OK): 83 | ''' 84 | Unpack a Flask standard response. 85 | 86 | Flask response can be: 87 | - a single value 88 | - a 2-tuple ``(value, code)`` 89 | - a 3-tuple ``(value, code, headers)`` 90 | 91 | .. warning:: 92 | 93 | When using this function, you must ensure that the tuple is not the reponse data. 94 | To do so, prefer returning list instead of tuple for listings. 95 | 96 | :param response: A Flask style response 97 | :param int default_code: The HTTP code to use as default if none is provided 98 | :return: a 3-tuple ``(data, code, headers)`` 99 | :rtype: tuple 100 | :raise ValueError: if the response does not have one of the expected format 101 | ''' 102 | if not isinstance(response, tuple): 103 | # data only 104 | return response, default_code, {} 105 | elif len(response) == 1: 106 | # data only as tuple 107 | return response[0], default_code, {} 108 | elif len(response) == 2: 109 | # data and code 110 | data, code = response 111 | return data, code, {} 112 | elif len(response) == 3: 113 | # data, code and headers 114 | data, code, headers = response 115 | return data, code or default_code, headers 116 | else: 117 | raise ValueError('Too many response values') 118 | -------------------------------------------------------------------------------- /flask_restplus_patched/__init__.py: -------------------------------------------------------------------------------- 1 | from flask_restplus import * 2 | from .api import Api 3 | from .model import Schema, DefaultHTTPErrorSchema 4 | try: 5 | from .model import ModelSchema 6 | except ImportError: 7 | pass 8 | from .namespace import Namespace 9 | from .parameters import Parameters, PostFormParameters, PatchJSONParameters, JSONParameters 10 | from .swagger import Swagger 11 | from .resource import Resource 12 | from .http_exceptions import abort 13 | from ._http import HTTPStatus 14 | -------------------------------------------------------------------------------- /flask_restplus_patched/_http.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | This file is backported from Python 3.5 http built-in module. 4 | """ 5 | 6 | from enum import IntEnum 7 | 8 | 9 | class HTTPStatus(IntEnum): 10 | """HTTP status codes and reason phrases 11 | 12 | Status codes from the following RFCs are all observed: 13 | 14 | * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616 15 | * RFC 6585: Additional HTTP Status Codes 16 | * RFC 3229: Delta encoding in HTTP 17 | * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518 18 | * RFC 5842: Binding Extensions to WebDAV 19 | * RFC 7238: Permanent Redirect 20 | * RFC 2295: Transparent Content Negotiation in HTTP 21 | * RFC 2774: An HTTP Extension Framework 22 | """ 23 | def __new__(cls, value, phrase, description=''): 24 | obj = int.__new__(cls, value) 25 | obj._value_ = value 26 | 27 | obj.phrase = phrase 28 | obj.description = description 29 | return obj 30 | 31 | def __str__(self): 32 | return str(self.value) 33 | 34 | # informational 35 | CONTINUE = 100, 'Continue', 'Request received, please continue' 36 | SWITCHING_PROTOCOLS = (101, 'Switching Protocols', 37 | 'Switching to new protocol; obey Upgrade header') 38 | PROCESSING = 102, 'Processing' 39 | 40 | # success 41 | OK = 200, 'OK', 'Request fulfilled, document follows' 42 | CREATED = 201, 'Created', 'Document created, URL follows' 43 | ACCEPTED = (202, 'Accepted', 44 | 'Request accepted, processing continues off-line') 45 | NON_AUTHORITATIVE_INFORMATION = (203, 46 | 'Non-Authoritative Information', 'Request fulfilled from cache') 47 | NO_CONTENT = 204, 'No Content', 'Request fulfilled, nothing follows' 48 | RESET_CONTENT = 205, 'Reset Content', 'Clear input form for further input' 49 | PARTIAL_CONTENT = 206, 'Partial Content', 'Partial content follows' 50 | MULTI_STATUS = 207, 'Multi-Status' 51 | ALREADY_REPORTED = 208, 'Already Reported' 52 | IM_USED = 226, 'IM Used' 53 | 54 | # redirection 55 | MULTIPLE_CHOICES = (300, 'Multiple Choices', 56 | 'Object has several resources -- see URI list') 57 | MOVED_PERMANENTLY = (301, 'Moved Permanently', 58 | 'Object moved permanently -- see URI list') 59 | FOUND = 302, 'Found', 'Object moved temporarily -- see URI list' 60 | SEE_OTHER = 303, 'See Other', 'Object moved -- see Method and URL list' 61 | NOT_MODIFIED = (304, 'Not Modified', 62 | 'Document has not changed since given time') 63 | USE_PROXY = (305, 'Use Proxy', 64 | 'You must use proxy specified in Location to access this resource') 65 | TEMPORARY_REDIRECT = (307, 'Temporary Redirect', 66 | 'Object moved temporarily -- see URI list') 67 | PERMANENT_REDIRECT = (308, 'Permanent Redirect', 68 | 'Object moved temporarily -- see URI list') 69 | 70 | # client error 71 | BAD_REQUEST = (400, 'Bad Request', 72 | 'Bad request syntax or unsupported method') 73 | UNAUTHORIZED = (401, 'Unauthorized', 74 | 'No permission -- see authorization schemes') 75 | PAYMENT_REQUIRED = (402, 'Payment Required', 76 | 'No payment -- see charging schemes') 77 | FORBIDDEN = (403, 'Forbidden', 78 | 'Request forbidden -- authorization will not help') 79 | NOT_FOUND = (404, 'Not Found', 80 | 'Nothing matches the given URI') 81 | METHOD_NOT_ALLOWED = (405, 'Method Not Allowed', 82 | 'Specified method is invalid for this resource') 83 | NOT_ACCEPTABLE = (406, 'Not Acceptable', 84 | 'URI not available in preferred format') 85 | PROXY_AUTHENTICATION_REQUIRED = (407, 86 | 'Proxy Authentication Required', 87 | 'You must authenticate with this proxy before proceeding') 88 | REQUEST_TIMEOUT = (408, 'Request Timeout', 89 | 'Request timed out; try again later') 90 | CONFLICT = 409, 'Conflict', 'Request conflict' 91 | GONE = (410, 'Gone', 92 | 'URI no longer exists and has been permanently removed') 93 | LENGTH_REQUIRED = (411, 'Length Required', 94 | 'Client must specify Content-Length') 95 | PRECONDITION_FAILED = (412, 'Precondition Failed', 96 | 'Precondition in headers is false') 97 | REQUEST_ENTITY_TOO_LARGE = (413, 'Request Entity Too Large', 98 | 'Entity is too large') 99 | REQUEST_URI_TOO_LONG = (414, 'Request-URI Too Long', 100 | 'URI is too long') 101 | UNSUPPORTED_MEDIA_TYPE = (415, 'Unsupported Media Type', 102 | 'Entity body in unsupported format') 103 | REQUESTED_RANGE_NOT_SATISFIABLE = (416, 104 | 'Requested Range Not Satisfiable', 105 | 'Cannot satisfy request range') 106 | EXPECTATION_FAILED = (417, 'Expectation Failed', 107 | 'Expect condition could not be satisfied') 108 | UNPROCESSABLE_ENTITY = 422, 'Unprocessable Entity', 'Unprocessable Entity' 109 | LOCKED = 423, 'Locked' 110 | FAILED_DEPENDENCY = 424, 'Failed Dependency' 111 | UPGRADE_REQUIRED = 426, 'Upgrade Required' 112 | PRECONDITION_REQUIRED = (428, 'Precondition Required', 113 | 'The origin server requires the request to be conditional') 114 | TOO_MANY_REQUESTS = (429, 'Too Many Requests', 115 | 'The user has sent too many requests in ' 116 | 'a given amount of time ("rate limiting")') 117 | REQUEST_HEADER_FIELDS_TOO_LARGE = (431, 118 | 'Request Header Fields Too Large', 119 | 'The server is unwilling to process the request because its header ' 120 | 'fields are too large') 121 | 122 | # server errors 123 | INTERNAL_SERVER_ERROR = (500, 'Internal Server Error', 124 | 'Server got itself in trouble') 125 | NOT_IMPLEMENTED = (501, 'Not Implemented', 126 | 'Server does not support this operation') 127 | BAD_GATEWAY = (502, 'Bad Gateway', 128 | 'Invalid responses from another server/proxy') 129 | SERVICE_UNAVAILABLE = (503, 'Service Unavailable', 130 | 'The server cannot process the request due to a high load') 131 | GATEWAY_TIMEOUT = (504, 'Gateway Timeout', 132 | 'The gateway server did not receive a timely response') 133 | HTTP_VERSION_NOT_SUPPORTED = (505, 'HTTP Version Not Supported', 134 | 'Cannot fulfill request') 135 | VARIANT_ALSO_NEGOTIATES = 506, 'Variant Also Negotiates' 136 | INSUFFICIENT_STORAGE = 507, 'Insufficient Storage' 137 | LOOP_DETECTED = 508, 'Loop Detected' 138 | NOT_EXTENDED = 510, 'Not Extended' 139 | NETWORK_AUTHENTICATION_REQUIRED = (511, 140 | 'Network Authentication Required', 141 | 'The client needs to authenticate to gain network access') 142 | -------------------------------------------------------------------------------- /flask_restplus_patched/api.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify,current_app 2 | from flask_restplus import Api as OriginalApi 3 | from flask_restplus._http import HTTPStatus 4 | from werkzeug import cached_property 5 | 6 | from .namespace import Namespace 7 | from .swagger import Swagger 8 | 9 | 10 | class Api(OriginalApi): 11 | 12 | @cached_property 13 | def __schema__(self): 14 | # The only purpose of this method is to pass custom Swagger class 15 | if not current_app.config['SWAGGERUI'] : 16 | return None 17 | return Swagger(self).as_dict() 18 | 19 | def init_app(self, app, **kwargs): 20 | # This solves the issue of late resources registration: 21 | # https://github.com/frol/flask-restplus-server-example/issues/110 22 | # https://github.com/noirbizarre/flask-restplus/pull/483 23 | self.app = app 24 | 25 | super(Api, self).init_app(app, **kwargs) 26 | app.errorhandler(HTTPStatus.UNPROCESSABLE_ENTITY.value)(handle_validation_error) 27 | 28 | def namespace(self, *args, **kwargs): 29 | # The only purpose of this method is to pass a custom Namespace class 30 | _namespace = Namespace(*args, **kwargs) 31 | self.add_namespace(_namespace) 32 | return _namespace 33 | 34 | 35 | # 处理422 输入参数异常 Return validation errors as JSON 36 | def handle_validation_error(err): 37 | #exc = err.data['exc'] 38 | exc = err.data 39 | return jsonify({ 40 | 'status': HTTPStatus.UNPROCESSABLE_ENTITY.value, 41 | 'message': exc['messages'] 42 | }), HTTPStatus.UNPROCESSABLE_ENTITY.value 43 | 44 | 45 | -------------------------------------------------------------------------------- /flask_restplus_patched/http_exceptions.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | HTTP exceptions collection 4 | -------------------------- 5 | """ 6 | 7 | from flask_restplus.errors import abort as restplus_abort 8 | from flask_restplus._http import HTTPStatus 9 | 10 | 11 | API_DEFAULT_HTTP_CODE_MESSAGES = { 12 | HTTPStatus.UNAUTHORIZED.value: ( 13 | "The server could not verify that you are authorized to access the " 14 | "URL requested. You either supplied the wrong credentials (e.g. a bad " 15 | "password), or your browser doesn't understand how to supply the " 16 | "credentials required." 17 | ), 18 | HTTPStatus.FORBIDDEN.value: ( 19 | "You don't have the permission to access the requested resource." 20 | ), 21 | HTTPStatus.UNPROCESSABLE_ENTITY.value: ( 22 | "The request was well-formed but was unable to be followed due to semantic errors." 23 | ) 24 | } 25 | 26 | 27 | def abort(code, message=None, **kwargs): 28 | """ 29 | Custom abort function used to provide extra information in the error 30 | response, namely, ``status`` and ``message`` info. 31 | """ 32 | if message is None: 33 | if code in API_DEFAULT_HTTP_CODE_MESSAGES: # pylint: disable=consider-using-get 34 | message = API_DEFAULT_HTTP_CODE_MESSAGES[code] 35 | else: 36 | message = HTTPStatus(code).description # pylint: disable=no-value-for-parameter 37 | restplus_abort(code=code, status=code, message=message, **kwargs) 38 | -------------------------------------------------------------------------------- /flask_restplus_patched/model.py: -------------------------------------------------------------------------------- 1 | from apispec.ext.marshmallow.swagger import fields2jsonschema, field2property 2 | import flask_marshmallow 3 | from werkzeug import cached_property 4 | 5 | from flask_restplus.model import Model as OriginalModel 6 | 7 | 8 | class SchemaMixin(object): 9 | 10 | def __deepcopy__(self, memo): 11 | # XXX: Flask-RESTplus makes unnecessary data copying, while 12 | # marshmallow.Schema doesn't support deepcopyng. 13 | return self 14 | 15 | 16 | class Schema(SchemaMixin, flask_marshmallow.Schema): 17 | pass 18 | 19 | 20 | if flask_marshmallow.has_sqla: 21 | class ModelSchema(SchemaMixin, flask_marshmallow.sqla.ModelSchema): 22 | pass 23 | 24 | 25 | class DefaultHTTPErrorSchema(Schema): 26 | status = flask_marshmallow.base_fields.Integer() 27 | message = flask_marshmallow.base_fields.String() 28 | 29 | def __init__(self, http_code, **kwargs): 30 | super(DefaultHTTPErrorSchema, self).__init__(**kwargs) 31 | self.fields['status'].default = http_code 32 | 33 | 34 | class Model(OriginalModel): 35 | 36 | def __init__(self, name, model, **kwargs): 37 | # XXX: Wrapping with __schema__ is not a very elegant solution. 38 | super(Model, self).__init__(name, {'__schema__': model}, **kwargs) 39 | 40 | @cached_property 41 | def __schema__(self): 42 | schema = self['__schema__'] 43 | if isinstance(schema, flask_marshmallow.Schema): 44 | return fields2jsonschema(schema.fields) 45 | elif isinstance(schema, flask_marshmallow.base_fields.FieldABC): 46 | return field2property(schema) 47 | raise NotImplementedError() 48 | -------------------------------------------------------------------------------- /flask_restplus_patched/resource.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # pylint: disable=protected-access 3 | import flask 4 | from flask_restplus import Resource as OriginalResource 5 | from flask_restplus._http import HTTPStatus 6 | from werkzeug.exceptions import HTTPException 7 | 8 | 9 | class Resource(OriginalResource): 10 | """ 11 | Extended Flast-RESTPlus Resource to add options method 12 | """ 13 | 14 | @classmethod 15 | def _apply_decorator_to_methods(cls, decorator): 16 | """ 17 | This helper can apply a given decorator to all methods on the current 18 | Resource. 19 | 20 | NOTE: In contrast to ``Resource.method_decorators``, which has a 21 | similar use-case, this method applies decorators directly and override 22 | methods in-place, while the decorators listed in 23 | ``Resource.method_decorators`` are applied on every request which is 24 | quite a waste of resources. 25 | """ 26 | for method in cls.methods: 27 | method_name = method.lower() 28 | decorated_method_func = decorator(getattr(cls, method_name)) 29 | setattr(cls, method_name, decorated_method_func) 30 | 31 | def options(self, *args, **kwargs): 32 | """ 33 | Check which methods are allowed. 34 | 35 | Use this method if you need to know what operations are allowed to be 36 | performed on this endpoint, e.g. to decide wether to display a button 37 | in your UI. 38 | 39 | The list of allowed methods is provided in `Allow` response header. 40 | """ 41 | # This is a generic implementation of OPTIONS method for resources. 42 | # This method checks every permissions provided as decorators for other 43 | # methods to provide information about what methods `current_user` can 44 | # use. 45 | method_funcs = [getattr(self, m.lower()) for m in self.methods] 46 | allowed_methods = [] 47 | request_oauth_backup = getattr(flask.request, 'oauth', None) 48 | for method_func in method_funcs: 49 | if getattr(method_func, '_access_restriction_decorators', None): 50 | if not hasattr(method_func, '_cached_fake_method_func'): 51 | fake_method_func = lambda *args, **kwargs: True 52 | # `__name__` is used in `login_required` decorator, so it 53 | # is required to fake this also 54 | fake_method_func.__name__ = 'options' 55 | 56 | # Decorate the fake method with the registered access 57 | # restriction decorators 58 | for decorator in method_func._access_restriction_decorators: 59 | fake_method_func = decorator(fake_method_func) 60 | 61 | # Cache the `fake_method_func` to avoid redoing this over 62 | # and over again 63 | method_func.__dict__['_cached_fake_method_func'] = fake_method_func 64 | else: 65 | fake_method_func = method_func._cached_fake_method_func 66 | 67 | flask.request.oauth = None 68 | try: 69 | fake_method_func(self, *args, **kwargs) 70 | except HTTPException: 71 | # This method is not allowed, so skip it 72 | continue 73 | 74 | allowed_methods.append(method_func.__name__.upper()) 75 | flask.request.oauth = request_oauth_backup 76 | 77 | return flask.Response( 78 | status=HTTPStatus.NO_CONTENT, 79 | headers={'Allow': ", ".join(allowed_methods)} 80 | ) 81 | -------------------------------------------------------------------------------- /flask_restplus_patched/swagger.py: -------------------------------------------------------------------------------- 1 | from apispec.ext.marshmallow.swagger import schema2parameters 2 | from flask_restplus.swagger import Swagger as OriginalSwagger 3 | 4 | 5 | class Swagger(OriginalSwagger): 6 | 7 | def parameters_for(self, doc): 8 | schema = doc['params'] 9 | 10 | if not schema: 11 | return [] 12 | if isinstance(schema, list): 13 | return schema 14 | if isinstance(schema, dict) and all(isinstance(field, dict) for field in schema.values()): 15 | return list(schema.values()) 16 | 17 | if 'in' in schema.context and 'json' in schema.context['in']: 18 | default_location = 'body' 19 | else: 20 | default_location = 'query' 21 | return schema2parameters(schema, default_in=default_location, required=True) 22 | -------------------------------------------------------------------------------- /kill_python.sh: -------------------------------------------------------------------------------- 1 | ps aux|grep python |grep -v grep|grep -v supervisord|awk '{print $2}'|xargs kill -9 2 | -------------------------------------------------------------------------------- /logging_config.py: -------------------------------------------------------------------------------- 1 | """logging_config 2 | 3 | Log Config File 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | import logging.config 12 | # File size in bytes # 文件大小,单位是字节 13 | LOG_FILE_MAX_BYTES = 100 * 1024 * 1024 14 | # The number of rotations # 轮转数量 15 | LOG_FILE_BACKUP_COUNT = 3 16 | log_config = \ 17 | { 18 | "version":1, 19 | "disable_existing_loggers":False, 20 | "formatters":{ 21 | "normal_formatter":{ 22 | "format":"%(asctime)s %(levelname)s %(process)d %(thread)d [%(pathname)s:%(lineno)s] %(message)s" 23 | }, 24 | "sql_formatter": { 25 | "format": "%(asctime)s %(levelname)s [%(process)d %(thread)d] %(message)s" 26 | } 27 | }, 28 | "handlers":{ 29 | "console":{ 30 | "class":"logging.StreamHandler", 31 | "level":"DEBUG", 32 | "formatter":"normal_formatter", 33 | "stream":"ext://sys.stdout" 34 | }, 35 | "debug_file_handler":{ 36 | "class":"logging.handlers.RotatingFileHandler", 37 | "level":"DEBUG", 38 | "formatter":"normal_formatter", 39 | "filename":"log/debug.log", 40 | "maxBytes":LOG_FILE_MAX_BYTES, 41 | "backupCount":LOG_FILE_BACKUP_COUNT, 42 | "encoding":"utf8" 43 | }, 44 | "info_file_handler":{ 45 | "class":"logging.handlers.RotatingFileHandler", 46 | "level":"INFO", 47 | "formatter":"normal_formatter", 48 | "filename":"log/info.log", 49 | "maxBytes":LOG_FILE_MAX_BYTES, 50 | "backupCount":LOG_FILE_BACKUP_COUNT, 51 | "encoding":"utf8" 52 | }, 53 | "dao_file_handler":{ 54 | "class":"logging.handlers.RotatingFileHandler", 55 | "level":"DEBUG", 56 | "formatter":"normal_formatter", 57 | "filename":"log/dao.log", 58 | "maxBytes":LOG_FILE_MAX_BYTES, 59 | "backupCount":LOG_FILE_BACKUP_COUNT, 60 | "encoding":"utf8" 61 | }, 62 | "sql_file_handler":{ 63 | "class":"logging.handlers.RotatingFileHandler", 64 | "level":"DEBUG", 65 | "formatter":"sql_formatter", 66 | "filename":"log/sql.log", 67 | "maxBytes":LOG_FILE_MAX_BYTES, 68 | "backupCount":LOG_FILE_BACKUP_COUNT, 69 | "encoding":"utf8" 70 | }, 71 | "error_file_handler":{ 72 | "class":"logging.handlers.RotatingFileHandler", 73 | "level":"ERROR", 74 | "formatter":"normal_formatter", 75 | "filename":"log/errors.log", 76 | "maxBytes":LOG_FILE_MAX_BYTES, 77 | "backupCount":LOG_FILE_BACKUP_COUNT, 78 | "encoding":"utf8" 79 | }, 80 | "flask_file_handler":{ 81 | "class":"logging.handlers.RotatingFileHandler", 82 | "level":"DEBUG", 83 | "formatter":"normal_formatter", 84 | "filename":"log/flask.log", 85 | "maxBytes":LOG_FILE_MAX_BYTES, 86 | "backupCount":LOG_FILE_BACKUP_COUNT, 87 | "encoding":"utf8" 88 | }, 89 | "stock_update_file_handler":{ 90 | "class":"logging.handlers.RotatingFileHandler", 91 | "level":"INFO", 92 | "formatter":"normal_formatter", 93 | "filename":"log/stock_update.log", 94 | "maxBytes":LOG_FILE_MAX_BYTES, 95 | "backupCount":LOG_FILE_BACKUP_COUNT, 96 | "encoding":"utf8" 97 | } 98 | }, 99 | "loggers":{ 100 | "dao":{ 101 | "level":"DEBUG", 102 | "handlers":["dao_file_handler"], 103 | "propagate":"no" 104 | }, 105 | "sqlalchemy.engine":{ 106 | "level":"DEBUG", 107 | "handlers":["sql_file_handler"], 108 | "propagate":"no" 109 | }, 110 | "flask.app":{ 111 | "level":"DEBUG", 112 | "handlers":["flask_file_handler"], 113 | "propagate":"no" 114 | }, 115 | "stock_update":{ 116 | "level":"DEBUG", 117 | "handlers":["stock_update_file_handler"], 118 | "propagate":"no" 119 | } 120 | }, 121 | "root":{ 122 | "level":"INFO", 123 | "handlers":["console","debug_file_handler","error_file_handler"] 124 | } 125 | } 126 | 127 | logging.config.dictConfig(log_config) 128 | # Usage # 使用方法 129 | # daoLogger = logging.getLogger("dao") 130 | # daoLogger.info('dao') 131 | # daoLoggerSub1 = logging.getLogger("dao.sub1") 132 | # daoLoggerSub1.error('daoSub1') -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | """manage 2 | 3 | BaoAI Backend Main File 4 | 5 | PROJECT: BaoAI Backend 6 | VERSION: 2.0.0 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | import os 13 | from flask_migrate import Migrate, MigrateCommand 14 | from flask_script import Manager, Shell, Server, Command 15 | from flask_script.commands import Clean, ShowUrls 16 | from app import create_app, db 17 | 18 | # app,celery = create_app() 19 | app = create_app() 20 | manager = Manager(app) 21 | migrate = Migrate(app, db) 22 | 23 | def make_shell_context(): 24 | from app.modules.admin.model import Admin 25 | return dict(app=app, db=db, Admin=Admin) 26 | 27 | # Get BaoAI version and URL # 获取BaoAI版本及官方URL 28 | @manager.command 29 | def baoai(): 30 | print('BaoAI v2.0.0 - http://www.baoai.co') 31 | 32 | manager.add_command("runserver", Server(host='0.0.0.0', port=5000)) 33 | manager.add_command("shell", Shell(make_context=make_shell_context)) 34 | manager.add_command("db", MigrateCommand) # Database Manage # 数据库管理 35 | manager.add_command("clean", Clean()) # Clean Cache File # 清理缓存文件 36 | manager.add_command("url", ShowUrls()) # Print All URL # 打印所有URL 37 | 38 | if __name__ == "__main__": 39 | manager.run() 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /remote_debug.py: -------------------------------------------------------------------------------- 1 | """remote_debug 2 | 3 | Remote Debug Main File For Visual Studio Code 4 | 仅用于Visual Studio Code的远程调试入口文件 5 | 6 | PROJECT: BaoAI Backend 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | import os 13 | from app import create_app, db 14 | 15 | if __name__ == '__main__': 16 | app = create_app(os.getenv('FLASK_CONFIG') or 'default') 17 | app.debug = False 18 | app.run(host='localhost', port=5002) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.7.1 2 | aiodns==1.1.1 3 | aiohttp==3.6.1 4 | alabaster==0.7.12 5 | alembic==0.8.10 6 | amqp==2.5.1 7 | aniso8601==4.1.0 8 | apispec==0.38.0 9 | arrow==0.15.2 10 | asn1crypto==0.24.0 11 | astor==0.8.0 12 | async-timeout==3.0.1 13 | attrs==18.2.0 14 | Babel==2.7.0 15 | backcall==0.1.0 16 | backtrader==1.9.74.123 17 | backtrader-plotting==0.5.3 18 | bcrypt==3.1.6 19 | beautifulsoup4==4.8.0 20 | billiard==3.6.1.0 21 | bleach==1.5.0 22 | bokeh==1.0.4 23 | bs4==0.0.1 24 | ccxt==1.18.1225 25 | celery==4.3.0 26 | certifi==2019.3.9 27 | cffi==1.12.2 28 | chardet==3.0.4 29 | Click==7.0 30 | colorama==0.4.1 31 | colorlog==4.0.2 32 | cryptography==2.6.1 33 | cycler==0.10.0 34 | dash==1.4.0 35 | dash-core-components==1.3.0 36 | dash-html-components==1.0.1 37 | dash-renderer==1.1.1 38 | dash-table==4.4.0 39 | decorator==4.4.0 40 | defusedxml==0.6.0 41 | dnspython==1.16.0 42 | docutils==0.14 43 | empyrical==0.5.3 44 | entrypoints==0.3 45 | eventlet==0.25.1 46 | fabric==2.4.0 47 | Fabric3==1.13.1.post1 48 | Flask==1.0.2 49 | Flask-Compress==1.4.0 50 | Flask-Cors==3.0.2 51 | Flask-Login==0.4.0 52 | flask-marshmallow==0.7.0 53 | Flask-Migrate==2.4.0 54 | flask-restplus==0.11.0 55 | flask-restplus-marshmallow==0.2.20 56 | Flask-Script==2.0.6 57 | flask-sqlacodegen==1.1.6.1 58 | Flask-SQLAlchemy==2.2 59 | flower==0.9.3 60 | future==0.18.0 61 | gast==0.2.2 62 | greenlet==0.4.15 63 | grpcio==1.23.0 64 | gunicorn==20.0.4 65 | h5py==2.9.0 66 | html5lib==0.9999999 67 | IbPy2==0.8.0 68 | idna==2.8 69 | idna-ssl==1.1.0 70 | imagesize==1.1.0 71 | importlib-metadata==0.23 72 | inflect==2.1.0 73 | invoke==1.2.0 74 | ipykernel==5.1.2 75 | ipython==7.7.0 76 | ipython-genutils==0.2.0 77 | ipywidgets==7.5.1 78 | itsdangerous==1.1.0 79 | jedi==0.15.1 80 | Jinja2==2.10 81 | joblib==0.13.2 82 | jsonpickle==1.2 83 | jsonschema==3.0.0 84 | jupyter==1.0.0 85 | jupyter-client==5.3.1 86 | jupyter-console==6.0.0 87 | jupyter-core==4.5.0 88 | Keras==2.1.6 89 | kiwisolver==1.1.0 90 | kombu==4.6.4 91 | lockfile==0.12.2 92 | lxml==4.4.1 93 | Mako==1.0.7 94 | Markdown==3.1.1 95 | MarkupSafe==1.1.1 96 | marshmallow==2.18.1 97 | marshmallow-sqlalchemy==0.12.0 98 | matplotlib==3.1.1 99 | mistune==0.8.4 100 | monotonic==1.5 101 | more-itertools==7.2.0 102 | mpld3==0.3 103 | multidict==4.5.2 104 | multitasking==0.0.9 105 | nbconvert==5.6.0 106 | nbformat==4.4.0 107 | notebook==6.0.1 108 | numexpr==2.7.0 109 | numpy==1.17.0 110 | oandapy==0.0.9 111 | opencv-python==4.1.0.25 112 | packaging==19.0 113 | pandas==0.25.1 114 | pandas-datareader==0.7.4 115 | pandocfilters==1.4.2 116 | paramiko==2.4.2 117 | parso==0.5.1 118 | passlib==1.7.1 119 | pdir2==0.3.1.post2 120 | permission==0.4.1 121 | pexpect==4.7.0 122 | pickleshare==0.7.5 123 | Pillow==6.1.0 124 | plotly==4.1.1 125 | pony==0.7.9 126 | prometheus-client==0.7.1 127 | prompt-toolkit==2.0.9 128 | protobuf==3.9.1 129 | psutil==5.6.3 130 | ptyprocess==0.6.0 131 | py-cpuinfo==5.0.0 132 | pyasn1==0.4.5 133 | pycares==3.0.0 134 | pycparser==2.19 135 | pyfolio==0.9.2 136 | Pygments==2.4.2 137 | PyMySQL==0.9.3 138 | PyNaCl==1.3.0 139 | pyparsing==2.4.0 140 | pyrsistent==0.14.11 141 | pysimplemodel==2.3.3 142 | pytdx==1.72 143 | python-dateutil==2.8.0 144 | python-editor==1.0.4 145 | python-status==1.0.1 146 | pytz==2018.9 147 | pywinpty==0.5.5 148 | PyYAML==3.13 149 | pyzmq==18.1.0 150 | qtconsole==4.5.4 151 | redis==3.3.8 152 | requests==2.21.0 153 | retrying==1.3.3 154 | scikit-learn==0.21.3 155 | scipy==1.3.1 156 | seaborn==0.9.0 157 | Send2Trash==1.5.0 158 | simplejson==3.16.0 159 | six==1.12.0 160 | sklearn==0.0 161 | snowballstemmer==1.2.1 162 | soupsieve==1.9.3 163 | Sphinx==2.1.0 164 | sphinx-bootstrap-theme==0.7.1 165 | sphinx-rtd-theme==0.4.3 166 | sphinxcontrib-applehelp==1.0.1 167 | sphinxcontrib-devhelp==1.0.1 168 | sphinxcontrib-htmlhelp==1.0.2 169 | sphinxcontrib-jsmath==1.0.1 170 | sphinxcontrib-qthelp==1.0.2 171 | sphinxcontrib-serializinghtml==1.1.3 172 | sqlacodegen==2.0.1 173 | SQLAlchemy==1.1.5 174 | SQLAlchemy-Utils==0.32.12 175 | sqlautocode==0.7 176 | supervisor==4.1.0 177 | tensorboard==1.8.0 178 | tensorflow==1.8.0 179 | termcolor==1.1.0 180 | terminado==0.8.2 181 | testpath==0.4.2 182 | tornado==5.1.1 183 | traitlets==4.3.2 184 | tushare==1.2.48 185 | typing-extensions==3.7.4 186 | urllib3==1.24.1 187 | var-dump==1.2 188 | vine==1.3.0 189 | wcwidth==0.1.7 190 | webargs==5.1.2 191 | Werkzeug==0.14.1 192 | widgetsnbextension==3.5.1 193 | wincertstore==0.2 194 | wrapt==1.11.2 195 | yarl==1.1.0 196 | yfinance==0.1.45 197 | zipp==0.6.0 198 | -------------------------------------------------------------------------------- /requirements_talib.txt: -------------------------------------------------------------------------------- 1 | TA-Lib==0.4.17 -------------------------------------------------------------------------------- /run_baoai.bat: -------------------------------------------------------------------------------- 1 | venv\Scripts\python manage.py runserver --threaded -------------------------------------------------------------------------------- /run_baoai.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | basepath=$(cd `dirname $0`; pwd) 3 | cd ${basepath} 4 | venv/bin/gunicorn manage:app -c deploy/gunicorn_config.py 5 | 6 | -------------------------------------------------------------------------------- /run_www.bat: -------------------------------------------------------------------------------- 1 | venv\Scripts\python www_manage.py runserver --threaded -------------------------------------------------------------------------------- /run_www.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | basepath=$(cd `dirname $0`; pwd) 3 | cd ${basepath} 4 | venv/bin/gunicorn www_manage:app -c deploy/gunicorn_config.py 5 | 6 | -------------------------------------------------------------------------------- /static/ai/iris/3c099ad2-4ed1-11ea-a3e0-b42e9995d7c8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/static/ai/iris/3c099ad2-4ed1-11ea-a3e0-b42e9995d7c8.png -------------------------------------------------------------------------------- /static/ai/iris/c988de40-5330-11ea-aa9b-b42e9995d7c8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/static/ai/iris/c988de40-5330-11ea-aa9b-b42e9995d7c8.png -------------------------------------------------------------------------------- /static/uploads/2020/01/02/e4a77d02199947d4b0066aa9bd4e8b57.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/static/uploads/2020/01/02/e4a77d02199947d4b0066aa9bd4e8b57.jpg -------------------------------------------------------------------------------- /static/uploads/2020/01/03/eff0930192824964b3dbc0a716c8c5a9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/static/uploads/2020/01/03/eff0930192824964b3dbc0a716c8c5a9.png -------------------------------------------------------------------------------- /static/uploads/2020/02/14/4fcc3b62d0f94421b56226a691eeb2fd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanbaonet/baoaiback/0fb1b604185a8bd8b72c1d2d527fb94bbaf46a86/static/uploads/2020/02/14/4fcc3b62d0f94421b56226a691eeb2fd.png -------------------------------------------------------------------------------- /www/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask, Blueprint 3 | from flask_sqlalchemy import SQLAlchemy 4 | from flask_cors import CORS 5 | from config import config 6 | from celery import Celery 7 | import arrow 8 | import logging_config 9 | 10 | db = SQLAlchemy() 11 | Config = config[os.getenv('FLASK_CONFIG') or 'default'] 12 | celery = None 13 | 14 | def create_app(): 15 | app = Flask(__name__, static_url_path=Config.APP_STATIC_URL_PATH, static_folder=Config.STATIC_FOLDER, template_folder=Config.SITE_TEMPLATE_FOLDER) 16 | app.config.from_object(Config) 17 | db.init_app(app) 18 | db.app = app 19 | # 解决禁止跨域请求的问题 20 | if app.config['CORS_ENABLED']: 21 | CORS(app, supports_credentials=True) 22 | 23 | # init Celery 24 | # global celery 25 | # celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'], backend=app.config['CELERY_RESULT_BACKEND']) 26 | # celery.conf.update(app.config) 27 | 28 | from . import modules 29 | modules.init_app(app) 30 | 31 | @app.template_global('now') 32 | def now_datetime(format='YYYY-MM-DD HH:mm:ss'): 33 | return arrow.now().format(format) 34 | return app #, celery 35 | -------------------------------------------------------------------------------- /www/modules/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import current_app 3 | import traceback 4 | 5 | def file_name(file_dir): 6 | modules_dirs = [] 7 | for root, dirs, files in os.walk(file_dir): 8 | dirs.remove('__pycache__') 9 | modules_dirs = dirs 10 | break 11 | # print('root_dir:', root) # 当前目录路径 12 | # print('sub_dirs:', dirs) # 当前路径下所有子目录 13 | # print('files:', files) # 当前路径下所有非目录子文件 14 | return modules_dirs 15 | 16 | 17 | def init_app(app, **kwargs): 18 | basedir = os.path.abspath(os.path.dirname(__file__)) 19 | modules_dirs = file_name(basedir) 20 | from importlib import import_module 21 | # for module_name in app.config['ENABLED_MODULES']: 22 | for module_name in modules_dirs: 23 | # import_module('.%s' % module_name, package=__name__).init_app(app, **kwargs) 24 | try: 25 | import_module('.%s' % module_name, package=__name__).init_app(app, **kwargs) 26 | except Exception as e: 27 | print('%s module exception, traceback:\n%s' % (module_name, traceback.format_exc())) -------------------------------------------------------------------------------- /www/modules/about/__init__.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init main module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from flask import Blueprint 12 | about = Blueprint('about', __name__) 13 | def init_app(app, **kwargs): 14 | """ 15 | Init Article module. 16 | """ 17 | from . import views # , task 18 | app.register_blueprint(about) 19 | -------------------------------------------------------------------------------- /www/modules/about/task.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init task module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from www import celery 12 | from celery.schedules import crontab 13 | 14 | @celery.task 15 | def test1(arg1, arg2): 16 | result = arg1 + arg2 17 | return result 18 | 19 | @celery.task 20 | def test2(arg): 21 | print(arg) 22 | 23 | @celery.on_after_configure.connect 24 | def setup_periodic_tasks(sender, **kwargs): 25 | sender.add_periodic_task( 26 | crontab(hour=7, minute=30, day_of_week=1), 27 | test2.s('Happy Mondays!'), 28 | ) 29 | 30 | -------------------------------------------------------------------------------- /www/modules/about/views.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init views module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from flask import render_template,url_for,redirect,flash,current_app,\ 12 | request,abort, make_response 13 | from . import about 14 | 15 | @about.route('/about', methods=['GET','POST']) 16 | def index(): 17 | baoai = {} 18 | baoai['title'] = 'BaoAI' 19 | return render_template('about/index.html',baoai=baoai) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /www/modules/main/__init__.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init main module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from flask import Blueprint 12 | main = Blueprint('main', __name__) 13 | def init_app(app, **kwargs): 14 | """ 15 | Init Article module. 16 | """ 17 | from . import views # , task 18 | app.register_blueprint(main) 19 | -------------------------------------------------------------------------------- /www/modules/main/task.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init task module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from www import celery 12 | from celery.schedules import crontab 13 | 14 | @celery.task 15 | def test1(arg1, arg2): 16 | result = arg1 + arg2 17 | return result 18 | 19 | @celery.task 20 | def test2(arg): 21 | print(arg) 22 | 23 | @celery.on_after_configure.connect 24 | def setup_periodic_tasks(sender, **kwargs): 25 | sender.add_periodic_task( 26 | crontab(hour=7, minute=30, day_of_week=1), 27 | test2.s('Happy Mondays!'), 28 | ) 29 | 30 | -------------------------------------------------------------------------------- /www/modules/main/views.py: -------------------------------------------------------------------------------- 1 | """configs 2 | 3 | Init views module 4 | 5 | PROJECT: BaoAI Backend 6 | AUTHOR: henry <703264459@qq.com> 7 | WEBSITE: http://www.baoai.co 8 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 9 | LICENSE: Apache-2.0 10 | """ 11 | from flask import render_template,url_for,redirect,flash,current_app,\ 12 | request,abort, make_response 13 | from . import main 14 | from flask_sqlalchemy import get_debug_queries 15 | 16 | @main.after_app_request 17 | def after_request(response): 18 | for query in get_debug_queries(): 19 | if query.duration >= current_app.config['FLASKY_SLOW_DB_QUERY_TIME']: 20 | current_app.logger.warning( 21 | 'Slow query: %s\nParameters:%s\nDuration:%fs\nContext: %s\n' 22 | %(query.statement, query.parameters, query.duration, query.context)) 23 | return response 24 | 25 | @main.route('/', methods=['GET','POST']) 26 | def index(): 27 | baoai = {} 28 | baoai['title'] = 'BaoAI' 29 | return render_template('main/index.html',baoai=baoai) 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /www/templates/about/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{baoai.title}} 8 | 9 | 10 |

11 | BaoAI : {{now('YYYY-MM-DD')}} 12 |

13 | 14 | -------------------------------------------------------------------------------- /www/templates/main/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{baoai.title}} 8 | 9 | 10 |

11 | BaoAI : {{now('YYYY-MM-DD')}} 12 |

13 | 14 | -------------------------------------------------------------------------------- /www_manage.py: -------------------------------------------------------------------------------- 1 | """manage 2 | 3 | BaoAI Backend WWW Main File 4 | 5 | PROJECT: BaoAI Backend 6 | VERSION: 1.0.0 7 | AUTHOR: henry <703264459@qq.com> 8 | WEBSITE: http://www.baoai.co 9 | COPYRIGHT: Copyright © 2016-2020 广州源宝网络有限公司 Guangzhou Yuanbao Network Co., Ltd. ( http://www.ybao.org ) 10 | LICENSE: Apache-2.0 11 | """ 12 | import os 13 | from flask_migrate import Migrate, MigrateCommand 14 | from flask_script import Manager, Shell, Server 15 | from flask_script.commands import Clean, ShowUrls 16 | from www import create_app, db 17 | 18 | app = create_app() 19 | manager = Manager(app) 20 | migrate = Migrate(app, db) 21 | 22 | # Get BaoAI version and URL # 获取BaoAI版本及官方URL 23 | @manager.command 24 | def baoai(): 25 | print('BaoAI v2.0.0 - http://www.baoai.co') 26 | 27 | manager.add_command("runserver", Server(host="0.0.0.0", port=5005)) 28 | manager.add_command("db", MigrateCommand) # Database Manage # 数据库管理 29 | manager.add_command("clean", Clean()) # Clean Cache File # 清理缓存文件 30 | manager.add_command("url", ShowUrls()) # Print All URL # 打印所有URL 31 | 32 | if __name__ == '__main__': 33 | manager.run() 34 | --------------------------------------------------------------------------------