Showing preview only (336K chars total). Download the full file or copy to clipboard to get everything.
Repository: ymm-tech/gods-pen-server
Branch: master
Commit: 907172f65ff0
Files: 105
Total size: 296.6 KB
Directory structure:
gitextract_t7mniftx/
├── .dockerignore
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .gitmodules
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app/
│ ├── controller/
│ │ ├── category.js
│ │ ├── component.js
│ │ ├── group.js
│ │ ├── groupUser.js
│ │ ├── home.js
│ │ ├── interface.js
│ │ ├── kaptcha.js
│ │ ├── mock.js
│ │ ├── notice.js
│ │ ├── ossupload.js
│ │ ├── pages.js
│ │ ├── project.js
│ │ ├── projectUser.js
│ │ ├── proxy.js
│ │ ├── resources.js
│ │ ├── statistics.js
│ │ ├── tags.js
│ │ ├── template.js
│ │ ├── test.js
│ │ └── user.js
│ ├── extend/
│ │ ├── application.js
│ │ ├── context.js
│ │ ├── dingd.js
│ │ ├── email/
│ │ │ └── tpl/
│ │ │ ├── active.html
│ │ │ ├── notice.html
│ │ │ └── password.html
│ │ ├── helper.js
│ │ ├── noticeMessage.js
│ │ ├── token.js
│ │ └── tools.js
│ ├── middleware/
│ │ ├── login_handler.js
│ │ └── response_handler.js
│ ├── model/
│ │ ├── category.js
│ │ ├── component.js
│ │ ├── component_use.js
│ │ ├── group.js
│ │ ├── group_project.js
│ │ ├── group_user.js
│ │ ├── history_interface.js
│ │ ├── interface.js
│ │ ├── interface_draf.js
│ │ ├── interface_log.js
│ │ ├── interface_user.js
│ │ ├── pages.js
│ │ ├── pages_history.js
│ │ ├── project.js
│ │ ├── project_data.js
│ │ ├── resTagsRel.js
│ │ ├── resources.js
│ │ ├── tag.js
│ │ ├── tag_interface.js
│ │ ├── tags.js
│ │ ├── template.js
│ │ ├── user.js
│ │ ├── user_grade.js
│ │ ├── user_login.js
│ │ ├── user_login_log.js
│ │ ├── user_notice.js
│ │ ├── user_notice_type.js
│ │ ├── user_project.js
│ │ └── valid_code.js
│ ├── router.js
│ ├── schedule/
│ │ ├── es_delete.js
│ │ ├── es_report.js
│ │ ├── pu_uv_ratio.js
│ │ └── pvcount.js
│ └── service/
│ ├── base.js
│ ├── category.js
│ ├── component.js
│ ├── group.js
│ ├── groupUser.js
│ ├── kaptcha.js
│ ├── mock.js
│ ├── notice.js
│ ├── pages.js
│ ├── project.js
│ ├── projectUser.js
│ ├── resources.js
│ ├── statistics.js
│ ├── tags.js
│ ├── template.js
│ └── user.js
├── app.js
├── config/
│ ├── config.app.js
│ ├── config.default.js
│ ├── config.dev.js
│ ├── config.production.js
│ └── plugin.js
├── config-parser.js
├── docker-entrypoint.sh
├── index.js
├── package.json
├── process.json
├── sql/
│ └── init.sql
└── sub-build/
└── .gitkeep
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
logs/
npm-debug.log
node_modules/
coverage/
.idea/
run/
.DS_Store
*.swp
.vscode/
config/config.dev.js
config/config.docker.js
.git
dist/
sub/**/node_modules/
sub/**/dist/
config.yaml
config/config.dev.js.bak
config/config.local.js
================================================
FILE: .eslintignore
================================================
coverage
================================================
FILE: .eslintrc.js
================================================
module.exports = {
root: true,
parserOptions: {
sourceType: 'module'
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
"no-multi-spaces": 0,
"no-eval": 0,
"no-new": 0,
// allow paren-less arrow functions
'arrow-parens': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
"eqeqeq": 0, // 必须使用全等
"comma-dangle": [ 2, "never" ] //定义数组或对象最后多余的逗号
}
}
================================================
FILE: .gitignore
================================================
logs/
npm-debug.log
node_modules/
coverage/
.idea/
run/
.DS_Store
*.swp
.vscode/
config/config.docker.js
.travis.yml
appveyor.yml
config.yaml
config/config.dev.js.bak
config/config.local.js
memo.md
================================================
FILE: .gitmodules
================================================
[submodule "sub/gods-pen-admin"]
path = sub/gods-pen-admin
url = https://github.com/ymm-tech/gods-pen-admin.git
[submodule "sub/gods-pen"]
path = sub/gods-pen
url = https://github.com/ymm-tech/gods-pen.git
================================================
FILE: CHANGELOG.md
================================================
## V1.0.0
- 新增flutter 布局 去除动画等
- 页面类型
- 后台设置/更新类型
- 编辑器组件分类型显示
- 编辑器样式设置面板分类型展示
- 组件类型
- 创建时可选择组件类型
- 被正确筛选展示在同类型页面的组件列表
- 手动package.json修改类型 0 web 1 flutter
- cli
- 指定发布环境 -e
- 指定token -t
- 组件类型
- 权限
- flutter页面 编辑器开发者以上可保存
- flutter页面 编辑器管理者以上可发布
- flutter页面 历史记录开发者以上可恢复到草稿
- flutter页面 历史记录管理者以上可覆盖发布
- 其他页面无限制,(ml-server可配置)
- 画布
- 编辑器支持画布切换
- tview支持不同画布渲染
- nostack模式
- nostack/stack 正常切换
- 子组件stacked 展示正常
- 拖动,方向键等操作符合直觉
- childLimit/leaf
- 点击添加组件,组件个数被限制/或禁止
- 拖动添加组件,组件个数被限制/或禁止
- 组件树中移动(入),组件个数被限制/或禁止
- 预览样式/pc端tview样式
- 脚本/组合组件/页面模板优化tag交互
- 脚本保存
- 编辑器内发布,默认私有,可至后台设为公有
- 列表内发布,默认私有,可至后台设为公有
- 后台数据
- 新增团队和项目维度的统计数据、uvpv、页面数
- 新增指定时间内单页面累计独立访客数统计
- hi there
- 根节点支持脚本
- 样式换新
- fixed
- 编辑器内更优雅的(伪)fixed实现,不闪屏
- pc预览内更优雅的(伪)fixed实现
- bugfix
- 开发者布局下,脚本面板未提前打开时点击脚本不能自动打开,且手动切换后,显示空白
- object类型的输入框不显示
- 组件设置过margin时,拖动组件位置计算错误,不符合直觉
- 组件为fixed定位时,通过bottom、right定位,一碰就变成left、top定位,实际显示不能以底部、右侧对齐
================================================
FILE: Dockerfile
================================================
FROM node:10.13
COPY . /app
WORKDIR /app
RUN npm install pm2@3.5.0 -g --registry=https://registry.npm.taobao.org
RUN make install-lib
RUN chmod +x /app/docker-entrypoint.sh
ONBUILD COPY ./config.yaml /app
ONBUILD RUN node ./config-parser.js
ONBUILD RUN make build-all
ENTRYPOINT [ "./docker-entrypoint.sh" ]
CMD ["pm2-runtime", "start", "process.json", "--env=docker"]
EXPOSE 7051
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Full Truck Alliance
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
# 路径配置
admin = sub/gods-pen-admin
editor = sub/gods-pen
# 安装依赖
install-lib: install-server install-admin install-editor
@yarn cache clean && echo '所有依赖安装完成'
install-server:
@yarn --pure-lockfile --production && echo 'server依赖安装完成'
install-admin:
@cd $(admin) && yarn --pure-lockfile && echo 'admin依赖安装完成'
install-editor:
@cd $(editor) && yarn --pure-lockfile && echo 'editor依赖安装完成'
# 构建后台
build-admin:
cd $(admin) && npm run build-docker && cp -rf ./dist/. ../../sub-build/
# 构建编辑器(含终端页面)
build-editor:
cd $(editor) && npm run editor:build-docker && npm run client:build-docker && cp -rf ./dist/. ../../sub-build/
# 清理文件
clear-sub:
-rm -rf sub
# 依次构建
build-all: build-admin build-editor clear-sub
@echo '构建完成'
# 启动服务
run-server:
pm2 start process.json --no-daemon --env=docker
# 初始化子模块
init-sub:
git submodule update --init --recursive
# 更新子模块
update-sub:
git submodule update
# 构建镜像
docker-build:
@read -p '输入镜像tag:' version; docker image build -t godspen\:$$version .
================================================
FILE: README.md
================================================
# 码良服务端
<p align="center"><a href="https://godspen.ymm56.com/" target="_blank" rel="noopener noreferrer"><img width="200" src="https://godspen.ymm56.com/doc/logo.png"></a></p>
<p align="center">
<a href="https://godspen.ymm56.com/"><img src="https://img.shields.io/github/license/ymm-tech/gods-pen" alt="License"></a>
<a href="https://godspen.ymm56.com/"><img src="https://img.shields.io/github/package-json/v/ymm-tech/gods-pen"></a>
</p>
## :house: 官网
官网: https://godspen.ymm56.com/
使用手册: https://godspen.ymm56.com/doc/cookbook/introduce.html
在线体验: https://godspen.ymm56.com/admin/#/home
私有部署: https://godspen.ymm56.com/doc/cookbook/install.html

<p style="font-size:18px;" align="center">:point_right: `喜欢别忘了加star支持我们,你的支持是我们坚持的动力` :point_left:</p>
## 项目构成
码良系统由3个项目构成,分别是 [gods-pen-server](https://github.com/ymm-tech/gods-pen-server) 码良服务端、
[gods-pen-admin](https://github.com/ymm-tech/gods-pen-admin)
码良管理后台以及于7月份就已经开源的 [gods-pen](https://github.com/ymm-tech/gods-pen) 码良编辑器。
## 详细部署文档
https://godspen.ymm56.com/doc/cookbook/source.html
## 配置说明
码良依赖 es、redis、mysql、邮件、oss服务,因此需要配置这些服务的信息
config/ 文件夹下存放了开发配置和生产配置
### 前期准备
除过 mysql 以外,其他服务都开箱即可使用,无需进行初始化之类的操作
**mysql 需要使用 sql/init.sql 来初始化表结构和表数据**
### 开发配置
本地开发时,使用的是配置文件为 config/config.dev.js
### 生成配置
服务器部署时,使用的是配置文件为 config/config.production.js
## 开发
开发
```bash
npm run dev
```
debug(在vscode中端点调试)
```bash
npm run debug
```
## 部署
启动服务
```bash
npm run serve
```
终止服务
```bash
npm run stop
```
查看日志
```bash
tail $HOME/logs/master-stdout.log -n 500 -f # stdout
tail $HOME/logs/master-stderr.log -n 500 -f # stderr
```
================================================
FILE: app/controller/category.js
================================================
'use strict'
module.exports = app => {
class CategoryController extends app.Controller {
* list () {
const {
ctx
} = this
const searchRule = {}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.category.list(ctx.request.body)
}
* save () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: false,
allowEmpty: true
},
name: {
type: 'string',
max: 20,
required: true
},
type: {
type: 'int',
max: 5,
required: true
},
// desc: {
// type: 'string',
// max: 64,
// allowEmpty: true
// }
}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.category.save(ctx.request.body)
}
* delete () {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(createRule)
yield ctx.service.category.delete(ctx.request.body.id)
}
}
return CategoryController
}
================================================
FILE: app/controller/component.js
================================================
'use strict'
module.exports = app => {
class ComponentController extends app.Controller {
* find () {
const {
ctx
} = this
const searchRule = {
name: {
type: 'string',
}
}
ctx.validate(searchRule)
console.log('validate success')
var obj = ctx.request.body
obj.name = obj.name.replace(/\%/gi, '')
if (!obj.name) {
throw this.ctx.getError({
msg: '搜索条件为空'
})
}
obj.uid = ctx.request.uid
obj.like = false
ctx.body = yield ctx.service.component.list(obj)
}
* searchByName () {
const {
ctx
} = this
const searchRule = {}
ctx.validate(searchRule)
console.log('validate success')
var obj = ctx.request.body
obj.name = obj.name.replace(/\%/gi, '')
obj.uid = ctx.request.uid
obj.like = true
ctx.body = yield ctx.service.component.list(obj)
}
* searchAllStatusByName () {
const {
ctx
} = this
const searchRule = {}
ctx.validate(searchRule)
console.log('validate success')
var obj = ctx.request.body
obj.name = obj.name.replace(/\%/gi, '')
obj.uid = ctx.request.uid
obj.like = true
obj.status = 'all'
ctx.body = yield ctx.service.component.list(obj)
}
* info () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'string'
}
}
ctx.validate(searchRule, ctx.query)
var obj = ctx.query
obj.uid = ctx.request.uid
ctx.body = yield ctx.service.component.info(obj)
}
* updata () {
const {
ctx
} = this
const searchRule = {
desc: {
type: 'string',
required: true
},
visibilitylevel: {
type: 'int',
required: true
},
}
ctx.validate(searchRule)
var obj = ctx.request.body
obj.userId = ctx.request.uid
ctx.body = yield ctx.service.component.updata(obj)
}
* save () {
const {
ctx
} = this
const searchRule = {
version: {
type: 'string',
required: true
},
name: {
type: 'string',
required: true
},
desc: {
type: 'string',
required: true
},
path: {
type: 'string',
required: true
},
visibilitylevel: {
type: 'int',
required: true
},
}
ctx.validate(searchRule)
var obj = ctx.request.body
obj.userId = ctx.request.uid
ctx.body = yield ctx.service.component.save(obj)
}
/**
* 组件使用过一次
*/
* useone () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'number',
required: true
}
}
ctx.validate(searchRule)
var obj = ctx.request.body
obj.userId = ctx.request.uid
ctx.body = yield ctx.service.component.useone(obj)
}
* delete () {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(createRule)
var obj = ctx.request.body
obj.uid = ctx.request.uid
yield ctx.service.component.delete(obj)
}
async import () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: true
},
token: {
type: 'string',
required: true
},
}
ctx.validate(searchRule)
const obj = ctx.request.body
const userId = ctx.request.uid
const result = await ctx.service.component.import(obj, userId)
ctx.body = result
}
}
return ComponentController
}
================================================
FILE: app/controller/group.js
================================================
'use strict';
module.exports = app => {
class controller extends app.Controller {
* add () {
const { ctx } = this;
const createRule = {
description: { type: 'string' },
name: { type: 'string', required: true, allowEmpty: false },
};
// 1.校验参数
ctx.validate(createRule);
let groupInfo = {
description: ctx.request.body.description,
logo: ctx.request.body.logo,
name: ctx.request.body.name,
uid: ctx.request.uid,
}
ctx.body = yield ctx.service.group.add(groupInfo);
}
* delete () {
const { ctx } = this;
const createRule = {
id: { type: 'number', required: true }
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = {};
obj.groupId = ctx.request.body.id;
obj.uid = ctx.request.uid;
yield ctx.service.group.delete(obj);
}
* list () {
const { ctx } = this;
// type: 0 || 1 => 0表示所有 1表示我创建的
const createRule = {
type: { type: 'string', required: true },
count: { type: 'string' },
start: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
// 查询数据返回
const obj = {};
obj.type = ctx.query.type;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.group.list(obj);
}
* update () {
const { ctx } = this;
const createRule = {
id: { type: 'number', required: true, min: 1 }
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.group.update(obj);
}
* info () {
const { ctx } = this;
// type: 0 || 1 => 0表示所有 1表示我创建的
const createRule = {
id: { type: 'string', required: true },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
const obj = {};
obj.groupId = ctx.query.id;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.group.info(obj);
}
}
return controller;
};
================================================
FILE: app/controller/groupUser.js
================================================
'use strict';
module.exports = app => {
class controller extends app.Controller {
* add () {
const { ctx } = this;
const createRule = {
groupId: { type: 'string', required: true },
userId: { type: 'array', required: true },
role: { type: 'string', required: true },
};
// 1.校验参数
ctx.validate(createRule);
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
yield ctx.service.groupUser.add(obj);
}
* delete () {
const { ctx } = this;
const createRule = {
groupId: { type: 'string', required: true },
userId: { type: 'number', required: true },
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
yield ctx.service.groupUser.delete(obj);
}
* list () {
const { ctx } = this;
const createRule = {
id: { type: 'string', required: true },
count: { type: 'string' },
start: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule,ctx.query);
// 查询数据返回
const obj = {};
obj.groupId = ctx.query.id;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.groupUser.list(obj);
}
* update () {
const { ctx } = this;
const createRule = {
// groupId: { type: 'string', required: true },
userId: { type: 'string', required: true, min: 1 },
role: { type: 'string', required: true },
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
yield ctx.service.groupUser.update(obj);
}
}
return controller;
};
================================================
FILE: app/controller/home.js
================================================
'use strict';
module.exports = app => {
class HomeController extends app.Controller {
* index() {
this.ctx.body = 'hi, egg';
this.redirect(`http://cn.bing.com/search?q=1`);
}
}
return HomeController;
};
================================================
FILE: app/controller/interface.js
================================================
'use strict'
module.exports = app => {
class HomeController extends app.Controller {
* add () {
const { ctx } = this
const createRule = {
method: { type: 'string', required: true, allowEmpty: false },
name: { type: 'string', required: true, allowEmpty: false, max: 50 },
path: { type: 'string', required: true, allowEmpty: false },
projectId: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:插入接口数据
const id = yield ctx.service.interface.add(params)
ctx.body = { id }
}
* delete () {
const { ctx } = this
const createRule = {
apiId: { type: 'number', required: true, min: 1 }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:删除接口数据
yield ctx.service.interface.delete(params)
}
* update () {
const { ctx } = this
const createRule = {
request: { type: 'string' },
response: { type: 'string' },
method: { type: 'string', required: true, allowEmpty: false },
name: { type: 'string', required: true, allowEmpty: false, max: 50 },
path: { type: 'string', required: true, allowEmpty: false },
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:修改接口数据
yield ctx.service.interface.update(params)
}
* updataInterfaceMock () {
const { ctx } = this
const createRule = {
content: { type: 'string' },
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:修改接口数据
yield ctx.service.interface.updataInterfaceMock(params)
}
/**
* 对外的草稿同步接口
*/
* apiUpload () {
const { ctx } = this
const createRule = {
projectId: { type: 'number' },
token: { type: 'string' }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
console.log(params)
// 2:修改接口数据
yield ctx.service.interface.draf(params)
}
/**
* 查询某项目ID同步的api列表
*/
* getApiByProjectId () {
const { ctx } = this
const createRule = {
projectId: { type: 'string' }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
console.log(params)
// 2:修改接口数据
const listApis = yield ctx.service.interface.searchApiByProjectId(params)
ctx.body = listApis
}
* batchUpdate () {
const { ctx } = this
const createRule = {
apis: { type: 'array' },
projectId: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:批量修改接口数据
yield ctx.service.interface.batchUpdate(params)
}
* getDetailDrafById () {
const { ctx } = this
const createRule = {
id: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.query)
let params = ctx.query
// 2:查询接口数据
const api = yield ctx.service.interface.getDetailDrafById(params)
ctx.body = api
}
* draftList () {
const { ctx } = this
const createRule = {
projectId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.query)
let params = ctx.query
// 2:查询接口数据
const listApis = yield ctx.service.interface.draftList(params)
ctx.body = listApis
}
/**
* 废弃接口文档
* @param {*} obj
*/
* deprecated (obj) {
const { ctx } = this
const createRule = {
id: { type: 'number', required: true },
deprecated: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.request.body)
let params = ctx.request.body
const listApis = yield ctx.service.interface.deprecated(params)
ctx.body = listApis
}
* list () {
const { ctx } = this
const createRule = {
projectId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.query)
let params = ctx.query
// 2:查询接口数据
const listApis = yield ctx.service.interface.list(params)
ctx.body = listApis
}
* getInterfaceInfo () {
const { ctx } = this
const createRule = {
apiId: { type: 'string', required: true },
type: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.query)
let params = ctx.query
params.uid = ctx.request.uid
// 2:查询接口数据
const apiInfo = yield ctx.service.interface.getInterfaceInfo(params)
ctx.body = apiInfo
}
* addApiTag () {
const { ctx } = this
const createRule = {
tagName: { type: 'string', required: true, max: 50 },
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:插入接口数据
const id = yield ctx.service.interface.addApiTag(params)
ctx.body = { tagId: id }
}
* requestRelease () {
const { ctx } = this
const createRule = {
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:发布接口
yield ctx.service.interface.requestRelease(params)
}
* getApproveList () {
const { ctx } = this
let params = {}
params.uid = ctx.request.uid
// 2:查询接口数据
const listApis = yield ctx.service.interface.getApproveList(params)
ctx.body = listApis
}
* deleteApiTag () {
const { ctx } = this
const createRule = {
tagId: { type: 'number', required: true },
apiId: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:删除接口标签
yield ctx.service.interface.deleteApiTag(params)
}
* auditApi () {
const { ctx } = this
const createRule = {
status: { type: 'number', required: true },
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:审核接口标签
yield ctx.service.interface.auditApi(params)
}
* addTag () {
const { ctx } = this
const createRule = {
projectId: { type: 'number', required: true },
name: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:审核接口标签
const id = yield ctx.service.interface.addTag(params)
ctx.body = { id }
}
* updateTag () {
const { ctx } = this
const createRule = {
tagId: { type: 'number', required: true },
name: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:审核接口标签
yield ctx.service.interface.updateTag(params)
ctx.body = { id: params.tagId, name: params.name }
}
* deleteTag () {
const { ctx } = this
const createRule = {
tagId: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:删除标签
yield ctx.service.interface.deleteTag(params)
}
* getTags () {
const { ctx } = this
const createRule = {
projectId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.request.query)
let params = ctx.request.query
params.uid = ctx.request.uid
// 2:删除标签
const tags = yield ctx.service.interface.getTags(params)
ctx.body = { tags }
}
* getPersons () {
const { ctx } = this
const createRule = {
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.request.query)
let params = ctx.request.query
params.uid = ctx.request.uid
// 2:接口关注人列表
const persons = yield ctx.service.interface.getPersons(params)
ctx.body = { persons }
}
* addPerson () {
const { ctx } = this
const createRule = {
apiId: { type: 'number', required: true },
userId: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:关注接口
yield ctx.service.interface.addPerson(params)
}
* deletePerson () {
const { ctx } = this
const createRule = {
apiId: { type: 'number', required: true },
userId: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:删除关注人
yield ctx.service.interface.deletePerson(params)
}
* addData () {
const { ctx } = this
const createRule = {
projectId: { type: 'number', required: true },
name: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:审核接口标签
const id = yield ctx.service.interface.addData(params)
ctx.body = { id }
}
* updateData () {
const { ctx } = this
const createRule = {
id: { type: 'number', required: true },
name: { type: 'string', required: true },
content: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:审核接口标签
yield ctx.service.interface.updateData(params)
ctx.body = { id: params.id, name: params.name, content: params.content }
}
* deleteData () {
const { ctx } = this
const createRule = {
id: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:删除标签
yield ctx.service.interface.deleteData(params)
}
* getDatas () {
const { ctx } = this
const createRule = {
projectId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.request.query)
let params = ctx.request.query
params.uid = ctx.request.uid
// 2:删除标签
const datas = yield ctx.service.interface.getDatas(params)
ctx.body = { datas }
}
* getHistoryList () {
const { ctx } = this
const createRule = {
apiId: { type: 'string', required: true }
}
// 1.校验参数
ctx.validate(createRule, ctx.query)
let params = ctx.query
// 2:查询接口数据
const listApis = yield ctx.service.interface.getHistoryList(params)
ctx.body = listApis
}
* updateHistoryStatus () {
const { ctx } = this
const createRule = {
id: { type: 'number', required: true },
type: { type: 'number', required: true },
status: { type: 'number', required: true }
}
// 1.校验参数
ctx.validate(createRule)
let params = ctx.request.body
params.uid = ctx.request.uid
// 2:修改文档状态
yield ctx.service.interface.updateHistoryStatus(params)
}
}
return HomeController
}
================================================
FILE: app/controller/kaptcha.js
================================================
module.exports = app => {
class HomeController extends app.Controller {
* init (ctx) {
const info = {
img: '',
}
info.key = (parseInt((Math.random() * 9000)) + 1000) + '';
info.img = yield ctx.service.kaptcha.getCaptcha(info.key)
ctx.session.kaptcha = info.key;
this.ctx.body = {
img: info.img
};
}
}
return HomeController;
};
================================================
FILE: app/controller/mock.js
================================================
'use strict';
module.exports = app => {
class controller extends app.Controller {
* infoOption () {
const { ctx } = this;
ctx.noWarp = true
ctx.body = ''
ctx.status = 200
}
* info () {
const { ctx } = this;
let info = {};
if (ctx.params[ 0 ] && ctx.params[ 1 ]) {
if (/^\d*$/.test(ctx.params[ 0 ])) {
info.id = ctx.params[ 0 ];
info.path = ctx.params[ 1 ];
}
}
info.type = ctx.request.method.toLowerCase();
// 获取请求类型
ctx.noWarp = true;
ctx.body = yield ctx.service.mock.info(info);
}
* insertOrUpdate () {
const { ctx } = this;
const createRule = {
mockRequest: { type: 'string' },
type: { type: 'number', required: true },
apiId: { type: 'number' },
};
// 1.校验参数
ctx.validate(createRule);
const mockInfo = ctx.request.body;
mockInfo.uid = ctx.request.uid;
ctx.body = yield ctx.service.mock.add(mockInfo);
}
}
return controller;
};
================================================
FILE: app/controller/notice.js
================================================
'use strict';
module.exports = app => {
class controller extends app.Controller {
// 拉取最新20条未读消息
* pullMessage () {
const { ctx } = this;
const obj = {};
obj.uid = ctx.request.uid;
obj.readStatus = 1;
ctx.body = yield ctx.service.notice.pullMessage(obj);
}
// 获取站内信消息列表
* listMessage () {
const { ctx } = this;
const createRule = {
type: { type: 'string' },
status: { type: 'string' },
count: { type: 'string' },
start: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
// 查询数据返回
const obj = ctx.query;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.notice.listMessage(obj);
}
// 修改站内信读取状态
* changeReadStatus () {
const { ctx } = this;
const obj = ctx.request.body;
yield ctx.service.notice.changeReadStatus(obj);
}
// 获取站内信详情
* getMessageInfo () {
const { ctx } = this;
const obj = ctx.request.query;
ctx.body = yield ctx.service.notice.getMessageInfo(obj);
}
// 获取站内信消息数
* getMessageNums () {
const { ctx } = this;
const obj = {};
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.notice.getMessageNums(obj);
}
// 获取消息设置详情
* getNoticeType () {
const { ctx } = this;
const obj = ctx.request.query;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.notice.getNoticeType(obj);
}
// 修改消息设置详情
* updateNoticeType () {
const { ctx } = this;
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.notice.updateNoticeType(obj);
}
}
return controller;
};
================================================
FILE: app/controller/ossupload.js
================================================
const OSS = require('ali-oss')
const co = require('co')
const sendToWormhole = require('stream-wormhole');
const mime = require('mime');
module.exports = app => {
let ossClient = new OSS({
region: app.config.oss.region,
accessKeyId: app.config.oss.accessKeyId,
accessKeySecret: app.config.oss.accessKeySecret,
bucket: app.config.oss.bucket,
})
class OssUploadController extends app.Controller {
async uploadByUrls () {
const { ctx } = this
const rule = {
urls: {
type: 'array',
itemType: 'string',
rule: {
format: /^https?:\/\/.+$/
},
required: true
}
}
ctx.validate(rule)
let {urls} = ctx.request.body
let transHeaders = ctx.request.headers
let status = {}
await Promise.all(
urls.map(url => {
return co(function * () {
let assetStream
try {
assetStream = yield ctx.curl(url, {
method: 'get',
headers: Object.assign({
'user-agent': transHeaders['user-agent'],
'accept-encoding': transHeaders['accept-encoding'],
'accept': transHeaders['accept'],
}),
timeout: 50000,
streaming: true
})
} catch (e) {
console.log(e)
}
let fileName = url.replace(/\?.+$/, '').split('/').slice(-1)[0]
if (!assetStream) {
status[fileName] = 0
return
}
try {
let result = yield ossClient.putStream(fileName, assetStream.res)
console.log(result)
status[fileName] = 1
} catch (e) {
console.log(e)
status[fileName] = 0
}
})
})
).catch(e => {
console.log(e)
})
ctx.body = status
}
/**
* f = new FormData()
* f.append('files', File)
* f.append('files', File) // 支持多个文件
* f.append('base64', 'data:image/png;base64,xxxxxx')
* f.append('base64', 'data:image/png;base64,xxxxxx') // 支持多个base64
*
*/
async uploadFile () {
const { ctx } = this
const parts = await ctx.multipart()
let part
let files = []
while ((part = await parts()) != null) {
let valid, ext, filedata, result = {}
switch (part.length && part.length > 0 ? part[0] : part.fieldname) {
case 'files':
if (valid = part.filename) {
filedata = part
ext = mime.getExtension(part.mime)
}
break
case 'base64':
if (valid = part[1].match(/^data:(.+);base64,(.+)$/)) {
ext = mime.getExtension(valid[1])
filedata = Buffer.from(valid[2], 'base64')
}
break
}
if (valid) {
try {
result = await upload(filedata,
[Date.now(), (Math.random() + 1) * 1000000000 | 0].map(v => v.toString(16)).join('') + (ext ? `.${ext}` : ''))
} catch (e) {
console.log(e)
await sendToWormhole(part)
}
files.push({
originName: part.filename || '',
mime: part.mime || '',
name: result.name,
path: result.path,
})
}
}
ctx.body = files
async function upload (filedata, fileName) {
let result
await co(function * () {
result = yield ossClient.put(fileName, filedata)
})
return {
name: result && result.name,
path: result && result.url.replace(/^http(?!s)/, 'https')
}
}
}
}
return OssUploadController
}
================================================
FILE: app/controller/pages.js
================================================
'use strict'
const co = require('co')
const PSD = require('psd')
const fs = require('fs')
const path = require('path')
const request = require('request')
const rimraf = require('rimraf')
const sendToWormhole = require('stream-wormhole')
const OSS = require('ali-oss')
module.exports = app => {
class PagesController extends app.Controller {
/**
* 记录页面pv
*/
* pv() {
const {
ctx
} = this
var obj = ctx.request.body
obj.pageKey = 'key_' + obj.pageKey
this.ctx.helper.tools.TSDB.putMetric(
'h5.page.maliang.pv' + obj.pageKey,
new Date().getTime(),
1, {
uuid: obj.uuid
}
)
}
* pvuv() {
const {
ctx
} = this
var obj = ctx.request.body
obj.pageKey = 'key_' + obj.pageKey
var myTsdbClient = this.ctx.helper.tools.TSDB
var getUv = function (array) {
var dps = {}
for (let index = 0; index < array.length; index++) {
let element = array[index];
for (const key in element.dps) {
if (dps[key]) {
dps[key] += 1
} else {
dps[key] = 1
}
}
}
return dps
}
console.log('获取pv' + obj.pageKey)
let pv = yield myTsdbClient.query(
obj.startTime,
obj.endTime, [{
"aggregator": "sum",
"metric": "h5.page.maliang.pv" + obj.pageKey,
filters: [],
downsample: myTsdbClient.composeDownsampleString('sum', obj.step)
}]
)
console.log('获取pv完成')
if (pv.error || !pv[0]) {
pv = {}
} else {
pv = pv[0].dps
}
console.log('获取uv' + obj.pageKey)
// let uv = yield myTsdbClient.query(
// obj.startTime,
// obj.endTime, [ {
// "aggregator": "sum",
// "metric": "h5.page.maliang.pv" + obj.pageKey,
// filters: [ {
// "type": "wildcard",
// "tagk": "uuid",
// "filter": "*",
// "groupBy": true
// } ],
// downsample: myTsdbClient.composeDownsampleString('sum', obj.step)
// } ]
// )
// if (uv.error) {
// uv = {}
// } else {
// uv = getUv(uv)
// }
ctx.body = {
pv: pv,
uv: {}
}
}
* save() {
const {
ctx
} = this
const searchRule = {
name: {
type: 'string',
max: 50,
required: true,
allowEmpty: false
}
}
ctx.validate(searchRule)
console.log('validate success')
var obj = ctx.request.body
delete obj.updateTime
delete obj.createTime
if (obj.id) {
const role = yield ctx.service.base.getUserRole(ctx.request.uid, obj.projectId)
var permissionLimit = this.app.config.permissionLimit[obj.type] || {}
if (permissionLimit.save && !(role <= permissionLimit.save)) throw this.ctx.getError({
msg: '您缺少“开发者”或更高权限,无法保存,请联系该页面管理人员添加权限'
})
}
ctx.body = yield ctx.service.pages.save(obj)
}
* list() {
const {
ctx
} = this
const searchRule = {
projectId: {
type: 'int',
required: true,
allowEmpty: false
},
status: {
type: 'int',
required: false,
allowEmpty: true
}
}
ctx.validate(searchRule)
const query = ctx.request.body;
query.uid = ctx.request.uid;
ctx.body = yield ctx.service.pages.list(query)
}
* publiclist() {
const {
ctx
} = this
const searchRule = {}
ctx.validate(searchRule)
ctx.body = yield ctx.service.pages.publiclist(ctx.request.body)
}
* info() {
const {
ctx
} = this
const createRule = {
id: {
type: 'string',
required: true
}
}
ctx.validate(createRule)
var body = ctx.request.body
body.uid = ctx.request.uid;
var info = yield ctx.service.pages.info(body)
ctx.body = info
}
async detail() {
const {
ctx
} = this
const createRule = {
pageKey: {
type: 'string',
required: true
},
scene: {
type: 'string',
required: false,
}
}
ctx.validate(createRule)
var body = ctx.request.body
var retval
var fromCache
var role
switch (body.scene) {
case 'render':
retval = await this.ctx.app.getCache(body.pageKey) // string
fromCache = !ctx.helper.tools.isEmpty(retval) && !ctx.helper.tools.isEmptyObject(retval)
if (fromCache) break
case 'preview':
case 'copy':
case 'edit':
case 'history':
case 'history_preview':
retval = await co(function* () {
return yield ctx.service.pages.detail(ctx.request.body)
})
if (['render', 'preview', 'history_preview'].includes(body.scene)) {
retval.content = ctx.helper.tools.nodeTreeScriptTransform(retval.content)
}
if (body.scene == 'edit') {
role = await co(function* () {
return yield ctx.service.base.getUserRole(ctx.request.uid, retval.projectId)
})
if (role >= 4) throw ctx.getError({
status: 403,
msg: '您没有此页面的权限'
})
}
if (body.scene == 'render') {
this.ctx.app.setCache(body.pageKey, retval)
}
}
if (fromCache) {
ctx.noWarp = 1
ctx.body = `{"code":1,"msg":"success","data":${retval}}`
console.info(`从缓存中获取详情 ${body.scene}`)
} else {
ctx.body = retval
console.info(`从数据库中获取详情 ${body.scene}`)
}
}
*changeStatus() {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: true,
allowEmpty: false
},
status: {
type: 'int',
required: true,
allowEmpty: false
}
}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.pages.changeStatus(ctx.request.body)
}
* setHomePage() {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: true,
allowEmpty: false
}
}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.pages.setHomePage(ctx.request.body)
}
* delete() {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(createRule)
const query = {
id: ctx.request.body.id,
uid: ctx.request.uid
}
yield ctx.service.pages.delete(query)
}
* publish() {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
},
projectId: {
type: 'int',
required: true
},
pageKey: {
type: 'string',
required: true
},
content: {
type: 'string',
required: true,
allowEmpty: false
}
}
ctx.validate(createRule)
var body = ctx.request.body
body.uid = ctx.request.uid;
const role = yield ctx.service.base.getUserRole(ctx.request.uid, body.projectId)
var permissionLimit = this.app.config.permissionLimit[body.type] || {}
if (permissionLimit.publish && !(role <= permissionLimit.publish)) throw this.ctx.getError({
msg: '您缺少“管理员”或更高权限,无法发布,请联系该页面管理人员'
})
yield ctx.service.pages.publish(body)
this.ctx.app.delCache(body.pageKey) // 更新缓存 实际是清除缓存,待下次请求自动进行缓存
}
* count() {
const {
ctx
} = this
var num = yield ctx.service.pages.count()
ctx.body = {
count: num
}
}
* history() {
const {
ctx
} = this
const searchRule = {
pageId: {
type: 'string',
required: true,
allowEmpty: false
}
}
ctx.validate(searchRule, ctx.query)
var body = ctx.query
body.uid = ctx.request.uid
ctx.body = yield ctx.service.pages.history(body)
}
* getNameBykeys() {
const {
ctx
} = this
let rule = {
ids: {
type: 'array',
itemType: 'string',
}
}
ctx.validate(rule)
let names = yield ctx.service.pages.getNameBykeys(ctx.request.body)
ctx.body = {
names
}
}
* historyDelete() {
const {
ctx
} = this
const searchRule = {
id: {
type: 'string',
required: true,
allowEmpty: false
}
}
ctx.validate(searchRule, ctx.query)
var body = ctx.query
body.uid = ctx.request.uid
ctx.body = yield ctx.service.pages.deleteHistory(body)
}
* historyPublish() {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
},
projectId: {
type: 'int',
required: true
},
type: {
type: 'int',
required: false
},
pageKey: {
type: 'string',
required: true
},
content: {
type: 'string',
required: true,
allowEmpty: false
}
}
ctx.validate(createRule)
var body = ctx.request.body
body.uid = ctx.request.uid;
const role = yield ctx.service.base.getUserRole(ctx.request.uid, body.projectId)
var permissionLimit = this.app.config.permissionLimit[body.type] || {}
if (permissionLimit.publish && !(role <= permissionLimit.publish)) throw this.ctx.getError({
msg: '您缺少“管理员”或更高权限,无法发布,请联系该页面管理人员'
})
yield ctx.service.pages.historyPublish(body)
this.ctx.app.delCache(body.pageKey) // 更新缓存 实际是清除缓存,待下次请求自动进行缓存
}
* historyToDraft() {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
},
projectId: {
type: 'int',
required: true
},
type: {
type: 'int',
required: false
},
pageKey: {
type: 'string',
required: true
},
content: {
type: 'string',
required: true,
allowEmpty: false
}
}
ctx.validate(createRule)
var body = ctx.request.body
body.uid = ctx.request.uid;
const role = yield ctx.service.base.getUserRole(ctx.request.uid, body.projectId)
var permissionLimit = this.app.config.permissionLimit[body.type] || {}
if (permissionLimit.save && !(role <= permissionLimit.save)) throw this.ctx.getError({
msg: '您缺少“开发者”或更高权限,无法保存,请联系该页面管理人员添加权限'
})
yield ctx.service.pages.historyToDraft(body)
}
* updateFork() {
const createRule = {
id: {
type: 'int',
required: true
},
}
const ctx = this.ctx
ctx.validate(createRule)
yield ctx.service.pages.updateFork(ctx.request.body)
}
/**
* psd 文件解析
*/
async psdToPage() {
const {
ctx
} = this;
let currentPathDir = `psd_image`
const SERVER_PATH = './'
fs.existsSync(path.join(SERVER_PATH, currentPathDir)) || fs.mkdirSync(path.join(SERVER_PATH, currentPathDir))
const query = ctx.request.body || {}
const type = /^https?:\/\//.test(query.url) ? 'url' : 'file'
if (type === 'url' && !ctx.helper.tools.isSafeUrl(query.url)) {
ctx.body = 'hello world'
return
}
let stream = type === 'url' ? request(query.url) : await ctx.getFileStream()
let filename = type === 'url' ? (query.url.match(/\/([^/]+)$/) || [ '', Date.now().toString(32) + Math.random().toString(32).slice(2, 4) ])[1] : stream.filename // stream对象也包含了文件名,大小等基本信息
// 创建文件写入路径
let target = path.join(SERVER_PATH, currentPathDir + '/' + filename)
const result = await new Promise((resolve, reject) => {
// 创建文件写入流
const remoteFileStrem = fs.createWriteStream(target)
let errFlag
const onerror = (err) => {
errFlag = true
sendToWormhole(stream)
remoteFileStrem.destroy()
console.error(err)
reject(err)
}
// url 读取流校验
if (type === 'url') {
stream = stream.on('response', (response) => {
const isPsd = response.headers['content-type'] === 'image/vnd.adobe.photoshop'
if (!isPsd) onerror(new Error('不是 psd 资源'))
})
.on('error', err => onerror(err))
}
// 以管道方式写入流
stream.pipe(remoteFileStrem)
// 监听error事件
remoteFileStrem.on('error', err => onerror(err))
// 监听写入完成事件
remoteFileStrem.on('finish', () => {
if (errFlag) return
resolve({ filename, name: type === 'url' ? filename : stream.fields.name })
})
})
console.log(result)
if (!result.filename) return false
let psd = await PSD.open(path.join(SERVER_PATH, currentPathDir + '/' + filename))
let descendantsList = psd.tree().descendants()
descendantsList.reverse()
let psdSourceList = []
for (var i = 0; i < descendantsList.length; i++) {
if (descendantsList[i].isGroup()) continue
if (!descendantsList[i].visible) continue
try {
await descendantsList[i].saveAsPng(path.join(SERVER_PATH, currentPathDir + `/${i}.png`))
const fName = `ml/psd-img/${[Date.now(), (Math.random() + 1) * 1000000000 | 0].map(v => v.toString(16)).join('')}.png`
let src = await this.upload(fs.createReadStream(SERVER_PATH + `psd_image/${i}.png`), fName)
console.log('src', src)
psdSourceList.push({
...descendantsList[i].export(),
type: 'picture',
// imageSrc: SERVER_PATH + `psd_image/${i}.png`,
// path: binaryToBase(a)
src
})
rimraf(SERVER_PATH + `psd_image/${i}.png`, function (err) { // 删除当前目录下的 test.txt
console.log(err)
})
} catch (e) {
// 转换不出来的图层先忽
console.log(e)
continue
}
}
rimraf(SERVER_PATH + `psd_image/`, function (err) {
// 删除当前目录
// console.log(err)
});
ctx.body = {
elements: psdSourceList,
document: psd.tree().export().document
}
}
async upload(filedata, fileName) {
let ossClient = new OSS({
region: app.config.oss.region,
accessKeyId: app.config.oss.accessKeyId,
accessKeySecret: app.config.oss.accessKeySecret,
bucket: app.config.oss.bucket,
})
let result
await co(function* () {
result = yield ossClient.put(fileName, filedata)
})
// console.log('result', result)
return (result && result.url.replace(/^http(?!s)/, 'https')) || ''
}
async featuringPages () {
const createRule = {
month: {
type: 'int',
},
}
const ctx = this.ctx
ctx.validate(createRule)
const list = await ctx.service.pages.featuringPages({ monthBefore: ctx.request.body.month })
ctx.body = list
}
async updateFeatured () {
const createRule = {
id: {
type: 'int',
required: false,
},
value: {
type: 'int',
required: true,
},
key: {
type: 'string',
required: false,
}
}
const ctx = this.ctx
ctx.validate(createRule)
const res = ctx.request.body || {}
const result = await ctx.service.pages.updateFeatured({
id: res.id,
uid: ctx.request.uid,
value: res.value,
key: res.key
})
ctx.body = {
success: result
}
}
}
return PagesController
}
================================================
FILE: app/controller/project.js
================================================
'use strict';
module.exports = app => {
class controller extends app.Controller {
* add () {
const { ctx } = this;
const createRule = {
name: { type: 'string', required: true, allowEmpty: false },
groupId: { type: 'number' },
};
// 1.校验参数
ctx.validate(createRule);
const projectInfo = ctx.request.body;
projectInfo.uid = ctx.request.uid;
ctx.body = yield ctx.service.project.add(projectInfo);
}
* delete () {
const { ctx } = this;
const createRule = {
id: { type: 'number', required: true }
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = {};
obj.projectId = ctx.request.body.id;
obj.uid = ctx.request.uid;
yield ctx.service.project.delete(obj);
}
* list () {
const { ctx } = this;
const createRule = {
count: { type: 'string' },
start: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
// 查询数据返回
const obj = {};
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.project.list(obj);
}
* update () {
const { ctx } = this;
const createRule = {
name: { type: 'string', required: true, allowEmpty: false },
groupId: { type: 'number' },
id: { type: 'number' },
};
// 1.校验参数
ctx.validate(createRule);
const projectInfo = ctx.request.body;
projectInfo.uid = ctx.request.uid;
yield ctx.service.project.update(projectInfo);
}
* info () {
const { ctx } = this;
const createRule = {
id: { type: 'string', required: true },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
const obj = {};
obj.projectId = ctx.query.id;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.project.info(obj);
}
* groupProject () {
const { ctx } = this;
const createRule = {
count: { type: 'string' },
start: { type: 'string' },
groupId: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
// 查询数据返回
const obj = ctx.query;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.project.groupProjects(obj);
}
* favorateProject () {
const { ctx } = this;
const createRule = {
id: { type: 'number', required: true, },
};
// 1.校验参数
ctx.validate(createRule);
const projectInfo = ctx.request.body;
projectInfo.uid = ctx.request.uid;
yield ctx.service.project.favorateProject(projectInfo);
}
* cancelFavorateProject () {
const { ctx } = this;
const createRule = {
id: { type: 'number', required: true, },
};
// 1.校验参数
ctx.validate(createRule);
const projectInfo = ctx.request.body;
projectInfo.uid = ctx.request.uid;
yield ctx.service.project.cancelFavorateProject(projectInfo);
}
* getFavorateProject () {
const { ctx } = this;
const createRule = {
count: { type: 'string' },
start: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
// 查询数据返回
const obj = {};
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.project.getFavorateProject(obj);
}
}
return controller;
};
================================================
FILE: app/controller/projectUser.js
================================================
'use strict';
module.exports = app => {
class controller extends app.Controller {
* add () {
const { ctx } = this;
const createRule = {
projectId: { type: 'string', required: true },
userId: { type: 'array', required: true },
role: { type: 'string', required: true },
};
// 1.校验参数
ctx.validate(createRule);
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
yield ctx.service.projectUser.add(obj);
}
* delete () {
const { ctx } = this;
const createRule = {
projectId: { type: 'string', required: true },
userId: { type: 'number', required: true },
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
yield ctx.service.projectUser.delete(obj);
}
* list () {
const { ctx } = this;
const createRule = {
projectId: { type: 'string', required: true },
count: { type: 'string' },
start: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule, ctx.query);
// 查询数据返回
const obj = {};
obj.projectId = ctx.query.projectId;
obj.uid = ctx.request.uid;
ctx.body = yield ctx.service.projectUser.list(obj);
}
* update () {
const { ctx } = this;
const createRule = {
projectId: { type: 'string', required: true },
userId: { type: 'string', required: true, min: 1 },
role: { type: 'string', required: true },
};
// 1.校验参数
ctx.validate(createRule);
// 2:验证参数
const obj = ctx.request.body;
obj.uid = ctx.request.uid;
yield ctx.service.projectUser.update(obj);
}
}
return controller;
};
================================================
FILE: app/controller/proxy.js
================================================
const fs = require('fs')
const Controller = require('egg').Controller
const ua = require('random-ua')
const mime = require('mime')
const sendToWormhole = require('stream-wormhole')
const util = require('util')
const request = util.promisify(require('request'))
class ProxyController extends Controller {
async imgCorsProxy () {
const { ctx } = this
const url = ctx.query['url']
const responseType = ctx.query['responseType']
if (!ctx.helper.tools.isSafeUrl(url)) {
ctx.body = 'hello world'
return
}
let useStream = responseType == 'blob'
const result = await ctx.curl(url, {
method: 'GET',
streaming: useStream
})
const contentType = result.res.headers['content-type']
const isImage = /^image\//.test(contentType)
if (!isImage) {
if (useStream) sendToWormhole(result.res)
ctx.body = `"${url}" is not a image resource`
return
}
if (useStream) {
ctx.type = contentType
ctx.body = result.res
} else {
ctx.body = `data:${contentType};base64,${Buffer.from(
result.res.data,
'binary'
).toString('base64')}`
}
ctx.set('cache-control', 'max-age=31536000')
}
async transparentProxy () {
const { ctx } = this
const rule = {
url: {
type: 'string',
required: true
},
headers: {
type: 'object',
},
method: {
type: 'string'
},
body: {
type: 'object',
}
}
ctx.validate(rule)
let {url, headers, method, body} = ctx.request.body
let transHeaders = ctx.request.headers
const result = await ctx.curl(url, {
method: (method || 'get').toUpperCase(),
data: body,
headers: Object.assign({
'user-agent': transHeaders['user-agent'],
'accept-encoding': transHeaders['accept-encoding'],
'accept': transHeaders['accept'],
'referer': transHeaders['referer'],
}, headers),
timeout: 5000,
streaming: true
})
Object.entries(result.res.headers).map(([key, val]) => {
if (key == 'access-control-allow-origin' || key == 'content-security-policy') return
ctx.set(key, val)
})
ctx.body = result.res
}
async word2html () {
const { ctx } = this
const part = ctx.request.files && ctx.request.files[0] || {}
if (part.fieldname !== 'file' || !['docx', 'doc'].includes(mime.getExtension(part.mime))) {
ctx.cleanupRequestFiles()
throw this.ctx.getError({
msg: '仅支持docx或者doc类型文件'
})
}
const options = {
'method': 'POST',
'url': 'https://s6.aconvert.com/convert/convert-batch-win.php',
'headers': {
'Origin': 'https://www.aconvert.com',
'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
'User-Agent': ua.generate(),
'Content-Type': 'multipart/form-data',
'Accept': '*/*',
'Referer': 'https://www.aconvert.com/cn/document/doc-to-html/',
'Connection': 'keep-alive',
'DNT': '1'
},
formData: {
'file': fs.createReadStream(part.filepath),
'targetformat': 'html',
'code': '86000',
'filelocation': 'local'
}
}
const result = await request(options)
.then(({body}) => JSON.parse(body))
.catch(e => console.error(e))
if (!result || !result.filename) {
ctx.cleanupRequestFiles()
throw this.ctx.getError({
msg: '文档解析失败'
})
}
ctx.cleanupRequestFiles()
const html = await ctx.curl(`https://s6.aconvert.com/convert/p3r68-cdx67/${result.filename}`, {dataType: 'text'})
.then(v => v.data)
.catch(e => {
console.error(e)
return ''
})
ctx.body = {
html
}
}
}
module.exports = ProxyController
================================================
FILE: app/controller/resources.js
================================================
'use strict'
module.exports = app => {
class ResourcesController extends app.Controller {
* info () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'string'
}
}
ctx.validate(searchRule, ctx.query)
var obj = ctx.query
obj.uid = ctx.request.uid
ctx.body = yield ctx.service.resources.info(obj)
}
* list () {
const {
ctx
} = this
const searchRule = {
name: {
type: 'string',
max: 50,
required: false,
allowEmpty: true
},
categoryId: {
type: 'int',
required: false,
allowEmpty: true
},
page: {
type: 'int',
required: false,
allowEmpty: true
},
pageSize: {
type: 'int',
required: false,
allowEmpty: true
}
}
ctx.validate(searchRule)
console.log('validate success')
var obj = ctx.request.body
obj.uid = ctx.request.uid
ctx.body = yield ctx.service.resources.list(obj)
}
* save () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: false,
allowEmpty: true
},
name: {
type: 'string',
allowEmpty: true,
required: false
},
icon: {
type: 'string',
allowEmpty: true,
required: false
},
content: {
type: 'string',
allowEmpty: true,
required: false
},
tags: {
type: 'array',
required: false
}
}
ctx.validate(searchRule)
var obj = ctx.request.body
obj.uid = ctx.request.uid
ctx.body = yield ctx.service.resources.save(obj)
}
* delete () {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(createRule)
var obj = ctx.request.body
obj.uid = ctx.request.uid
yield ctx.service.resources.delete(obj)
}
* addUseCount () {
const {
ctx
} = this
const rule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(rule)
var obj = ctx.request.body
ctx.body = yield ctx.service.resources.addUseCount(obj)
}
}
return ResourcesController
}
================================================
FILE: app/controller/statistics.js
================================================
const Controller = require('egg').Controller
const co = require('co')
class StatisticsController extends Controller {
async report () {
const ctx = this.ctx
const query = ctx.request.query
/* 允许的 query构造,除此以外被放到 extras 字段上
** {
** finger_print: 'xxxxx',
env:
duration:
** app_id: 'tview',
** page_id: "page_7",
** action: "pageview",
utm_campaign
** label: "red",
html: '' // 代码片段
** }
*/
let FIELDS = ['finger_print', 'env', 'app_id', 'page_id', 'action', 'utm_campaign', 'label', 'html', 'duration', '_t']
let filteredQuery = Object.keys(query)
.filter(k => FIELDS.indexOf(k) > -1)
.reduce((o, k) => {
if (k != '_t' && k != 'duration') o[k] = query[k]
if (k == 'duration') o['durations'] = +query[k] || 0 // 类型冲突,重建索引太慢,只能换个字段名了
delete query[k]
return o
}, {})
let report = {
referrer: ctx.get('referrer'),
create_time: Date.now(),
ip: ctx.ip || null,
ua: ctx.get('User-Agent'),
...filteredQuery
}
if (Object.keys(query).length > 0) {
report.extras = JSON.stringify(query)
}
if (report.action === 'pageview' && report.app_id === 'tview') {
this.app.addPV('report')
}
ctx.service.statistics.report(report)
ctx.status = 200
ctx.body = JSON.stringify(report, null, 2)
}
async getPUV () {
const ctx = this.ctx
let rule = {
pageId: {
type: 'string',
required: false
},
pageIds: {
type: 'array',
required: false
},
// default tview
appId: {
type: 'string',
required: false
},
timePeriod: {
type: 'array',
itemType: 'int',
},
interval: {
type: 'string',
required: false
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.getPUV(ctx.request.body)
ctx.body = result
}
async getPages () {
const ctx = this.ctx
let rule = {
timePeriod: {
type: 'array',
itemType: 'int',
},
// default tview
appId: {
type: 'string',
required: false
},
size: {
type: 'int',
required: false
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.getPages(ctx.request.body)
ctx.body = result
}
async getUtms () {
const ctx = this.ctx
let rule = {
timePeriod: {
type: 'array',
itemType: 'int',
},
pageId: {
type: 'string',
required: true
},
// default tview
appId: {
type: 'string',
required: false
},
size: {
type: 'int',
required: false
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.getUtms(ctx.request.body)
ctx.body = result
}
async getViewTime () {
const ctx = this.ctx
let rule = {
appId: {
type: 'string',
required: true
},
pageId: {
type: 'string',
allowEmpty: true,
required: false
},
timePeriod: {
type: 'array',
itemType: 'int',
},
utm: {
type: 'string',
allowEmpty: true,
required: false
},
avgonly: {
type: 'int',
allowEmpty: true,
required: false
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.getViewTime(ctx.request.body)
ctx.body = result
}
async getActions () {
const ctx = this.ctx
let rule = {
// default tview
appId: {
type: 'string',
required: false
},
pageId: {
type: 'string',
required: true
},
timePeriod: {
type: 'array',
itemType: 'int',
},
size: {
type: 'int',
required: false
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.getActions(ctx.request.body)
ctx.body = result
}
async actionTrack () {
const ctx = this.ctx
let rule = {
// default tview
appId: {
type: 'string',
required: false
},
pageId: {
type: 'string',
required: true
},
timePeriod: {
type: 'array',
itemType: 'int',
},
size: {
type: 'int',
required: false
},
interval: {
type: 'string',
required: false
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.actionTrack(ctx.request.body)
ctx.body = result
}
async getTodayOutline () {
const ctx = this.ctx
let rule = {
appId: {
type: 'string',
required: true
}
}
ctx.validate(rule)
let result = await ctx.service.statistics.getTodayOutline(ctx.request.body)
ctx.body = result
}
async getProjectReport () {
const ctx = this.ctx
let rule = {
projectId: {
type: 'int',
required: true
},
timePeriod: {
type: 'array',
itemType: 'int',
},
interval: {
type: 'string',
required: false
}
}
ctx.validate(rule)
let params = ctx.request.body
let pages = await co(function * () {
return yield ctx.service.pages.list(params)
})
let pageKeys = pages.map(p => p.key)
let puv
if (!pageKeys || !pageKeys.length) puv = {sum: {}, histogram: []}
else puv = await ctx.service.statistics.getPUV({pageIds: pageKeys, timePeriod: params.timePeriod, interval: params.interval})
ctx.body = Object.assign({count: pages.length}, puv)
}
async getGroupReport () {
const ctx = this.ctx
let rule = {
groupId: {
type: 'number',
required: true
},
timePeriod: {
type: 'array',
itemType: 'int',
},
interval: {
type: 'string',
required: false
}
}
ctx.validate(rule)
let params = ctx.request.body
let projects = await co(function * () {
return yield ctx.service.project.groupProjects(params)
})
let projectIds = projects.map(p => p.id)
params.projectId = projectIds
let pages = await co(function * () {
return yield ctx.service.pages.list(params)
})
let pageKeys = pages.map(p => p.key)
let puv
if (!pageKeys || !pageKeys.length) puv = {sum: {}, histogram: []}
else puv = await ctx.service.statistics.getPUV({pageIds: pageKeys, timePeriod: params.timePeriod, interval: params.interval})
ctx.body = Object.assign({pageCount: pages.length, projectCount: projects.length}, puv)
}
}
module.exports = StatisticsController
================================================
FILE: app/controller/tags.js
================================================
'use strict'
module.exports = app => {
class TagsController extends app.Controller {
* list () {
const {
ctx
} = this
const searchRule = {
name: {
type: 'string',
max: 50,
required: false,
allowEmpty: true
},
categoryId: {
type: 'int',
required: false,
allowEmpty: true
}
}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.tags.list(ctx.request.body)
}
* add () {
const {
ctx
} = this
const searchRule = {
name: {
type: 'string',
max: 50,
required: false,
allowEmpty: true
},
categoryId: {
type: 'int',
required: false,
allowEmpty: true
}
}
ctx.validate(searchRule)
ctx.body = yield ctx.service.tags.add(ctx.request.body)
}
/**
* 组件使用过一次
*/
* useone () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'number',
required: true
}
}
ctx.validate(searchRule)
var obj = ctx.request.body
obj.userId = ctx.request.uid
ctx.body = yield ctx.service.tags.useone(obj)
}
}
return TagsController
}
================================================
FILE: app/controller/template.js
================================================
'use strict'
module.exports = app => {
class TemplateController extends app.Controller {
* list () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
max: 5,
required: false,
allowEmpty: true
},
name: {
type: 'string',
max: 20,
allowEmpty: true,
required: false
},
categoryId: {
type: 'int',
max: 5,
required: false,
allowEmpty: true
}
}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.template.list(ctx.request.body)
}
* detail () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(searchRule)
ctx.body = yield ctx.service.template.detail(ctx.request.body)
}
* save () {
const {
ctx
} = this
const searchRule = {
id: {
type: 'int',
required: false,
allowEmpty: true
},
name: {
type: 'string',
max: 20,
required: true
},
categoryId: {
type: 'int',
max: 5,
allowEmpty: true,
required: false
},
content: {
type: 'string',
allowEmpty: true
},
// image: {
// type: 'string',
// allowEmpty: true,
// required: false
// },
// desc: {
// type: 'string',
// max: 64,
// required: false,
// allowEmpty: true
// }
}
ctx.validate(searchRule)
console.log('validate success')
ctx.body = yield ctx.service.template.save(ctx.request.body)
}
* delete () {
const {
ctx
} = this
const createRule = {
id: {
type: 'int',
required: true
}
}
ctx.validate(createRule)
yield ctx.service.template.delete(ctx.request.body.id)
}
}
return TemplateController
}
================================================
FILE: app/controller/test.js
================================================
'use strict';
module.exports = app => {
class HomeController extends app.Controller {
* index() {
this.ctx.body = 'hi, egg';
}
}
return HomeController;
};
================================================
FILE: app/controller/user.js
================================================
'use strict';
var OSS = require('ali-oss');
var moment = require('moment')
var crypto = require('crypto')
const co = require('co')
module.exports = app => {
function getAdminUrl (ctx, path) {
if (/https?:\/\//.test(app.config.ADMIN_PATH)) return `${app.config.ADMIN_PATH.replace(/\/$/g, '')}/${path}`
let protocol = 'http://'
const domain = (({header, host}) => {
let referer = header.referer
if (/^https/.test(referer)) protocol = 'https://'
let d = host
try {
d = referer.replace(/^(https?:)?\/\//, '').split('/')[0]
} catch (e) {
console.log(e)
}
return d
})(ctx)
return `${protocol}${domain}/${app.config.ADMIN_PATH.replace(/^\/|\/$/g, '')}/${path}`;
}
class HomeController extends app.Controller {
* login () {
const { ctx } = this;
const createRule = {
account: { type: 'string' },
kaptcha: { type: 'string' },
password: { type: 'string' },
};
// 1.校验参数
ctx.validate(createRule);
// 2:校验验证码
if (ctx.session.kaptcha != ctx.request.body.kaptcha) {
throw ctx.getError({ msg: '验证码不正确' });
}
// 3:校验用户名及密码
yield ctx.service.user.login(ctx.request.body);
}
* oauthCode () {
const { ctx } = this;
const createRule = {
channel: { type: 'string', required: true, allowEmpty: false },
};
// 1.校验参数
ctx.validate(createRule);
let url = ''
if (ctx.request.body.channel === 'github') {
url = `https://github.com/login/oauth/authorize?client_id=${app.config.github.clientId}&redirect_uri=${getAdminUrl(ctx, 'login.html?from=github')}`
}
if (!url) {
throw ctx.getError({ msg: '暂不支持该第三方登录' });
return
}
console.log(url)
// 2.跳转第三方登录页面
this.ctx.body = {
redirect: url
}
}
async oauthLogin () {
const { ctx } = this;
const createRule = {
channel: { type: 'string', required: true, allowEmpty: false },
code: { type: 'string', required: true, allowEmpty: false }
};
let params = ctx.request.body
// 1.校验参数
ctx.validate(createRule);
let user = {}
if (params.channel == 'github') {
console.log('a')
// 获取token
let accessToken = await app.curl('https://github.com/login/oauth/access_token', {
method: 'POST',
data: {
client_id: app.config.github.clientId,
client_secret:app.config.github.clientSecret,
code: params.code,
redirect_uri: '',
state: ''
}
});
let token = ''
try {
token = accessToken.data.toString().match(/access_token=([^&]+)/)[1]
} catch (e) {
throw ctx.getError({ msg: '第三方账号授权失败' });
}
if (!token) {
throw ctx.getError({ msg: '第三方账号授权失败' });
}
let info = (await app.curl(`https://api.github.com/user?access_token=${token}`, {
method: 'GET',
dataType: 'json',
headers: {
Authorization: token
},
}) || {}).data
user = {
oauth: `github_${info.id}`,
name: info.name,
email: info.email
}
}
// 3:登录
await co(function * () {
yield ctx.service.user.oauthLogin(user);
})
}
* register () {
const { ctx } = this;
const registerRule = {
email: 'email',
password: { type: 'string', required: true, allowEmpty: false },
kaptcha: { type: 'string', min: 4, max: 4 },
name: { type: 'string', min: 2, max: 32 },
};
ctx.validate(registerRule);
// 2:校验验证码
if (ctx.session.kaptcha != ctx.request.body.kaptcha) {
throw ctx.getError({ msg: '验证码不正确' });
}
let obj = ctx.request.body;
obj.emailStatus = 1;
yield ctx.service.user.register(obj);
}
* activeEmail () {
const { ctx } = this;
const activeEmailRule = {
code: { type: 'string', required: true, allowEmpty: false }
};
ctx.validate(activeEmailRule, ctx.request.body);
// 解析code码
const showCode = ctx.helper.tools.decrypt(ctx.request.body.code, app.config.appConfig.desKey);
if (!ctx.helper.tools.isEmpty(showCode)) {
const params = {};
const index = showCode.indexOf('||');
params.email = showCode.substr(0, index);
params.code = showCode.substr(index + 2);
const message = yield ctx.service.user.activeEmail(params);
if (ctx.helper.tools.isEmpty(message)) {
// 激活成功,跳转制定页面
this.ctx.redirect(getAdminUrl(this.ctx, 'index.html'));
} else {
ctx.body = message;
}
} else {
throw ctx.getError({ msg: '参数不合法' });
}
}
* sendActiveEmail () {
const { ctx } = this;
if (ctx.request.uid > 0) {
yield ctx.service.user.sendActiveEmail(ctx.request.uid);
} else {
throw ctx.getError({ msg: '您的登录已失效' });
}
}
// 忘记密码,发送邮件
* sendEmail () {
const { ctx } = this;
const sendEmailRule = {
email: 'email'
};
// 1.校验参数
ctx.validate(sendEmailRule);
yield ctx.service.user.sendEmail(ctx.request.body.email);
}
* info () {
const { ctx } = this;
if (ctx.request.uid > 0) {
let info = yield ctx.service.user.info(ctx.request.uid);
if (ctx.query.uid > 0 && info.emailStatus !== 2) throw ctx.getError({ msg: '您的账户未激活,暂不能查看其他项目成员的信息' });
if (ctx.query.uid > 0) info = yield ctx.service.user.info(ctx.query.uid);
const user = {};
user.name = info.name;
user.userId = info.id;
user.photo = info.photo;
user.telephone = info.telephone;
user.email = info.email;
user.role = info.role;
user.security = ctx.query.uid > 0 ? '' : info.security;
user.emailStatus = info.emailStatus;
user.projectNums = info.projectCount;
ctx.body = user;
} else {
throw ctx.getError({ msg: '您的登录已失效' });
}
}
* logout () {
this.ctx.cookies.set(this.app.config.appConfig.accessTokenKey, '', {
maxAge: 0
});
}
* edit () {
const { ctx } = this;
const editRule = {
name: { type: 'string', required: true, allowEmpty: false, min: 2, max: 32 },
telephone: { type: 'string', allowEmpty: true, min: 0, max: 11 },
email: { type: 'string', allowEmpty: true },
};
// 1.校验参数
ctx.validate(editRule);
yield ctx.service.user.edit(ctx.request.body);
}
* updatePassword () {
const { ctx } = this;
const passwordRule = {
targetPassword: { type: 'string', required: true, allowEmpty: false },
password: { type: 'string', required: true, allowEmpty: false },
};
// 1.校验参数
ctx.validate(passwordRule);
ctx.request.body.uid = ctx.request.uid
yield ctx.service.user.updatePassword(ctx.request.body);
}
// 忘记密码,重置接口
* newUpdatePassword () {
const { ctx } = this;
const passwordRule = {
code: { type: 'string', required: true, allowEmpty: false },
};
ctx.validate(passwordRule, ctx.request.body);
// 解析code码
const showCode = ctx.helper.tools.decrypt(ctx.request.body.code, app.config.appConfig.desKey);
if (!ctx.helper.tools.isEmpty(showCode)) {
const params = {};
const index = showCode.indexOf('||');
params.email = showCode.substr(0, index);
params.code = showCode.substr(index + 2);
params.password = ctx.request.body.password
yield ctx.service.user.newUpdatePassword(params);
} else {
throw ctx.getError({ msg: '参数不合法' });
}
}
* forgetPassword () {
const { ctx } = this;
const forgetPasswordRule = {
email: 'email'
};
// 1.校验参数
ctx.validate(forgetPasswordRule);
yield ctx.service.user.forgetPassword(ctx.request.body);
}
* search () {
const { ctx } = this;
const searchRule = {
key: { type: 'string', required: true, max: 50 }
};
// 1.校验参数
ctx.validate(searchRule, ctx.query);
const data = yield ctx.service.user.search(ctx.query);
ctx.body = data;
}
* getTocken () {
console.log('访问ip:', this.ctx.ip)
const valid = this.ctx.helper.tools.ossConfigValid(app.config.oss)
if (!valid) throw this.ctx.getError({ msg: '您未正确配置 oss 服务的相关字段,无法使用对象存储服务' })
var expire_syncpoint = new Date().getTime() + 60 * 1000
var policyToken = {
accessKeyId: app.config.oss.accessKeyId,
accessKeySecret: app.config.oss.accessKeySecret,
host: app.config.oss.host,
expire_time: moment(expire_syncpoint).toISOString(),
upload_dir: `${app.config.oss.bucket}/`
}
console.log(policyToken)
var policy_dict = {}
policy_dict[ 'expiration' ] = policyToken.expire_time
var condition_array = []
var array_item = []
array_item.push('starts-with');
array_item.push('$key');
array_item.push(policyToken.upload_dir);
condition_array.push(array_item)
policy_dict[ 'conditions' ] = condition_array
var policy = JSON.stringify(policy_dict)
var policy_encode = new Buffer(policy).toString('base64')
console.log(policy_encode)
var sign_result = crypto.createHmac('sha1', policyToken.accessKeySecret).update(policy_encode).digest().toString('base64');
console.log(sign_result)
var token_dict = {}
token_dict[ 'accessid' ] = policyToken.accessKeyId
token_dict[ 'host' ] = policyToken.host
token_dict[ 'policy' ] = policy_encode
token_dict[ 'signature' ] = sign_result
token_dict[ 'expire' ] = expire_syncpoint
token_dict[ 'dir' ] = policyToken.upload_dir
token_dict[ 'ns' ] = (this.ctx.request.uid + 10000).toString(16) // namespace
const { ctx } = this;
ctx.body = token_dict;
}
}
return HomeController;
};
================================================
FILE: app/extend/application.js
================================================
const zlib = require('zlib')
const Redis = require('ioredis')
const REDIS = Symbol('REDIS#INSTANCE')
const request = require("request")
const throttle = require("lodash/throttle")
const nodemailer = require('nodemailer')
const nunjucks = require('nunjucks')
nunjucks.configure(__dirname + '/email/tpl', { autoescape: true })
function initMailSender (config) {
return nodemailer.createTransport({
host: config.host,
port: config.port,
secure: config.secure,
auth: {
user: config.user,
pass: config.pass
}
})
}
function strZip (str) {
str = typeof str === 'string' ? str : JSON.stringify(str)
return new Promise((resolve, reject) => {
zlib.gzip(str, (err, result) => {
if (err) reject(err)
else resolve(result)
})
})
}
function strUnzip (buf) {
return new Promise((resolve, reject) => {
zlib.gunzip(buf, (err, result) => {
if (err) reject(err)
else resolve(result.toString())
})
})
}
function initRedis (config, errorCallback) {
var mode = config instanceof Array ? 'cluster' : 'single'
var redis
if (mode == 'cluster') {
redis = new Redis.Cluster(config, {
keyPrefix: 'godspen:',
})
} else {
redis = new Redis(Object.assign({
keyPrefix: 'godspen:',
}, config))
}
redis.on('error', (err) => {
console.error(err)
typeof errorCallback === 'function' && errorCallback(err)
})
return redis
}
module.exports = {
get redis () {
if (!this[REDIS]) {
this[REDIS] = initRedis(this.config.redis, throttle((err) => {
this.DDNotify(`${err.name} \n\n ${err.message} \n\n ${err.stack} \n\n ${err.lastNodeError}`, 'redis 异常')
}, 60000))
}
return this[REDIS]
},
async getCache (key) {
if (!key) return
let cache
try {
cache = await this.redis.getBuffer(key)
if (!cache) return null
cache = await strUnzip(cache)
} catch (e) {
console.error(e)
}
return cache
},
async setCache (key, val) {
if (!key) return
let flag
try {
val = await strZip(val)
flag = await this.redis.set(key, val, 'EX', 86400)
} catch (e) {
console.error(e)
}
return flag === 'OK'
},
async delCache (key) {
if (!key) return
let flag
try {
flag = await this.redis.del(key)
} catch (e) {
console.error(e)
}
return Boolean(flag)
},
addPV (from) {
let pvkey = `pv_from_${from}` // report or detail
if (!this[pvkey] || typeof this[pvkey] !== 'number') this[pvkey] = 1
else this[pvkey] += 1
},
getPV () {
return {
pv_from_report: this.pv_from_report,
pv_from_detail: this.pv_from_detail,
}
},
DDNotify (msg, title, at) {
if (!this.config.dingding) return
var options = {
method: 'POST',
url: 'https://oapi.dingtalk.com/robot/send',
qs: { access_token: this.config.dingding},
headers: {
'cache-control': 'no-cache',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'msgtype': 'markdown',
'markdown': {
'title': title,
'text': `${title} @${at || 'all'} \n${msg}`
},
'at': {
'atMobiles': at ? [at] : [],
'isAtAll': false
}
})
}
request(options, function (error, response, body) {
if (error) console.error(error)
console.log(body)
})
},
/**
* 发送邮件给指定用户
* send({
* receivers: [ '258137678@qq.com' ],
* tplName: 'password',
* data: {
* name:'xingm',
* password:'密码',
* }
*})
* @param {array} receivers 接受邮件的邮箱地址,一个数组
* @param {string} tplName 模板名称。见tpl文件夹下面的模板名称。不用写后缀.html
* @param {object} data 模板数据
* @param {string} subject 邮件主题,可不填写
* @return {Promise} 返回一个promise
*/
sendMail: function mailSend ({ receivers, tplName, data, subject }) {
if (!mailSend.transporter) mailSend.transporter = initMailSender(this.config.mail || {})
let mailOptions = {
from: this.config.mail.user.indexOf('<') > -1 ? this.config.mail.user : `码良noreply <${this.config.mail.user}>`, // sender address
to: receivers.join(','), // list of receivers
subject: subject || '通知', // Subject line
text: '',
html: '' // html body
}
mailOptions.html = nunjucks.render(`${tplName}.html`, data)
let promise = new Promise(function (resolve, reject) {
mailSend.transporter.sendMail(mailOptions, function (error, info) {
if (error) {
reject(error)
} else {
resolve(info)
}
})
})
return promise
}
}
================================================
FILE: app/extend/context.js
================================================
class ERROR extends Error {
constructor ({ code, status, msg, error }) {
super(msg)
this.code = code || 500
this.status = status || 200
this.msg = msg || '服务器异常,请稍后重试'
this.error = error
}
}
module.exports = {
/**
* 获取一个错误对象
* @param {string} code code码
* @param {string} status 状态值
* @param {string} msg 错误消息
* @return {ERROR} 错误对象
*/
getError ({ code, status, msg, e }) {
// this 就是 ctx 对象,在其中可以调用 ctx 上的其他方法,或访问属性
console.log(e)
return new ERROR({ code, status, msg, e })
}
}
================================================
FILE: app/extend/dingd.js
================================================
/**
*
*/
'use strict'
module.exports = {
* sendNotice (data) {
try {
const projectInfo = yield this.ctx.model.Project.findOne({
where: {
id: data.projectId
}
})
console.log('ddproject', projectInfo)
let webhook = projectInfo.ddwebhook
if (webhook) {
let info = {
msgtype: 'markdown',
markdown: data.markdown,
at: data.at
}
console.log(info)
const result = yield this.ctx.curl(webhook, {
// 必须指定 method
method: 'POST',
// 通过 contentType 告诉 HttpClient 以 JSON 格式发送
contentType: 'json',
data: info,
// 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body
dataType: 'json'
})
console.log(result)
return result
}
} catch (error) {
console.log('钉钉发送消息失败', error)
}
}
}
================================================
FILE: app/extend/email/tpl/active.html
================================================
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
</style>
<div style=" margin:0; padding:0;width:598px;color:#666; font:normal 12px/24px 'SimSun'; margin:0 auto;-webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); -o-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); margin-top:30px; border:1px solid #bcbcbc;">
<div style="font:normal 18px/18px 'Arial'; padding-top:40px; line-height:26px;padding-left: 20px; height:325px; color:#000; text-align:left;">
<p style="margin:0; padding:0; margin-bottom:10px;">尊敬的<span style="font-weight:bold">{{name}}</span>,您好:</p>
<p style="margin:0; padding:0; text-indent:2em;line-height:28px;">点击链接即可激活您的码良账户</p>
<p><a href="{{url}}">{{urlcontent}}</a></p>
<p>为保障您的帐号安全,请在24小时内点击该链接,您也可以将链接复制到浏览器地址栏访问。如果您并未尝试激活邮箱,请忽略本邮件,由此给您带来的不便请谅解。</p>
<p>本邮件由码良自动发出,请勿直接回复!</p>
</div>
</div>
================================================
FILE: app/extend/email/tpl/notice.html
================================================
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
</style>
<div style=" margin:0; padding:0;width:598px;color:#666; font:normal 12px/24px 'SimSun'; margin:0 auto;-webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); -o-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); margin-top:30px; border:1px solid #bcbcbc;">
<div style="font:normal 18px/18px 'Arial'; padding-top:40px; line-height:26px;padding-left: 20px; height:325px; color:#000; text-align:left;">
<p style="margin:0; padding:0; margin-bottom:10px;">{{content}}</p>
<p>本邮件由码良自动发出,请勿直接回复!</p>
</div>
</div>
================================================
FILE: app/extend/email/tpl/password.html
================================================
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
</style>
<div style=" margin:0; padding:0;width:598px;color:#666; font:normal 12px/24px 'SimSun'; margin:0 auto;-webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); -o-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15); margin-top:30px; border:1px solid #bcbcbc;">
<div style="font:normal 18px/18px 'Arial'; padding-top:40px; line-height:26px;padding-left: 20px; height:325px; color:#000; text-align:left;">
<p style="margin:0; padding:0; margin-bottom:10px;">尊敬的<span style="font-weight:bold">{{name}}</span>,您好:</p>
<p style="margin:0; padding:0; text-indent:2em;line-height:28px;">点击链接即可重置您的码良账户密码</p>
<p><a href="{{url}}">{{urlcontent}}</a></p>
<p>为保障您的帐号安全,请在24小时内点击该链接,您也可以将链接复制到浏览器地址栏访问。如果您并未尝试重置密码,请忽略本邮件,由此给您带来的不便请谅解。</p>
<p>本邮件由码良自动发出,请勿直接回复!</p>
</div>
</div>
================================================
FILE: app/extend/helper.js
================================================
const token = require('./token');
const tools = require('./tools');
const dingd = require('./dingd');
module.exports = {
dingd,
token,
tools,
};
================================================
FILE: app/extend/noticeMessage.js
================================================
module.exports = {
GROUP_ADD_USER: {
code: 101,
title: '{{userName}}已加入项目组《{{groupName}}》',
content: '{{userName}}已加入项目组《{{groupName}}》',
},
GROUP_DELETE_USER: {
code: 102,
title: '{{userName}}已被移除项目组《{{groupName}}》',
content: '{{userName}}您已被移除项目组《{{groupName}}》',
},
GROUP_ROLE_USER: {
code: 103,
title: '{{userName}}对项目组《{{groupName}}》的权限变更为{{role}}',
content: '{{userName}}对项目组《{{groupName}}》的权限变更为{{role}}',
},
GROUP_DELETE: {
code: 104,
title: '{{userName}}已删除项目组《{{groupName}}》',
content: '{{userName}}已删除项目组《{{groupName}}》',
},
PROJECT_ADD_USER: {
code: 201,
title: '{{userName}}已加入项目《{{projectName}}》',
content: '{{userName}}已加入项目《{{projectName}}》',
},
PROJECT_DELETE_USER: {
code: 202,
title: '{{userName}}已离开项目《{{projectName}}》',
content: '{{userName}}已离开项目《{{projectName}}》',
},
PROJECT_ROLE_USER: {
code: 203,
title: '{{userName}}在项目《{{projectName}}》内的权限变更为{{role}}',
content: '{{userName}}在项目《{{projectName}}》内的权限变更为{{role}}',
},
PROJECT_DELETE: {
code: 204,
title: '{{userName}}删除项目《{{projectName}}》',
content: '{{userName}}删除项目《{{projectName}}》',
},
API_DELETE: {
code: 301,
title: '{{userName}}已删除接口【{{interfaceName}}】',
content: '{{userName}}已删除接口【{{interfaceName}}】',
},
API_PUBLISH_RElEASE: {
code: 302,
title: '{{userName}}申请发布接口【{{interfaceName}}】',
content: '{{userName}}申请发布接口【{{interfaceName}}】,申请备注:{{remark}}',
},
API_UPDATE: {
code: 303,
title: '{{userName}}已修改接口【{{interfaceName}}】',
content: '{{userName}}已修改接口【{{interfaceName}}】',
},
API_AUDIT: {
code: 304,
title: '接口【{{interfaceName}}】申请发布审核{{version}}',
content: '接口【{{interfaceName}}】申请发布审核{{version}},审核备注:{{remark}}',
},
API_UPDATE_STATUS: {
code: 305,
title: '接口【{{interfaceName}}】被{{userName}} 修改 {{facet}} 调试结果为: {{result}}',
content: '接口【{{interfaceName}}】被{{userName}} 修改 {{facet}} 调试结果为: {{result}}',
},
demo: {
title: '申请发布接口',
content: '## 申请人:{{userName}}\n' +
'> 接口名称:{{interfaceName}}\n' +
'> 接口路径:{{interfacePath}}\n' +
'> \n' +
'> ## 10点20分发布 [天气](http://www.thinkpage.cn/) \n',
},
API_DING_PUBLISH_RElEASE: {
title: '申请发布接口',
content: '## 申请信息\n\n' +
'> 申请人:{{userName}}\n\n' +
'> 接口名称:{{interfaceName}}\n\n' +
'> 接口路径:{{interfacePath}}\n\n' +
'> ## [点击查看详情]({{url}}) \n\n' +
'## 备注 \n\n' +
'> {{remark}}\n\n'
},
API_DING_API_AUDIT: {
title: '接口文档变动',
content: '## 变动信息 {{status}}\n\n' +
'> 接口名称:{{interfaceName}}\n\n' +
'> 接口路径:{{interfacePath}}\n\n' +
'> ## [点击查看详情]({{url}}) \n\n' +
'## 备注 \n\n' +
'> {{remark}}\n\n'
},
};
================================================
FILE: app/extend/token.js
================================================
const defaultsp1 = '@#$*';
const defaultsp2 = '$%^&';
const tools = require('./tools');
module.exports = {
decryptToken(token) {
const tokenConfig = {};
const accessToken = tools.decrypt(token, this.app.config.appConfig.desKey);
const index = accessToken.indexOf(defaultsp1);
const index2 = accessToken.indexOf(defaultsp2);
if (index === -1 || index2 === -1) return tokenConfig;
tokenConfig.uid = accessToken.substring(0, index);
tokenConfig.date = accessToken.substring(index + defaultsp1.length, index2);
tokenConfig.token = accessToken;
tokenConfig.password = accessToken.substring(index2 + defaultsp2.length);
return tokenConfig;
},
getToken() {
let accesToken = this.cookies.get(this.app.config.appConfig.accessTokenKey);
if (tools.isEmpty(accesToken)) {
// 从header取值
accesToken = this.headers[ this.app.config.appConfig.accessTokenKey ];
}
return accesToken;
},
/**
* 生成token,规则 = encode("uid + sp1 + date + sp2 + password", deskey),sp1和sp2为固定值,deskey为加密key
* @param {string} uid 用户id
* @param {string} password 用户密码
*/
setToken(uid, password) {
const date = new Date().getTime();
let token = uid + defaultsp1 + date + defaultsp2 + password;
token = tools.encrypt(token, this.app.config.appConfig.desKey);
this.ctx.cookies.set(this.app.config.appConfig.accessTokenKey, token, {
maxAge: this.app.config.appConfig.accessTokenKeyTime,
});
},
parseCookie(cookie) {
var cookies = {};
if (!cookie) {
return cookie;
}
var list = cookie.split(';');
for (var i = 0; i < list.length; i++) {
var pair = list[i].split('=');
cookies[pair[0].trim()] = pair[1];
}
return cookies;
},
};
================================================
FILE: app/extend/tools.js
================================================
/**
*
*/
'use strict'
const crypto = require('crypto')
const CryptoJs = require('crypto-js')
const Mock = require('mockjs')
var TsdbClient = require('opentsdb-node-client')
const babel = require("babel-core")
const urlParser = require('url')
module.exports = {
TSDB:(function(){
return new TsdbClient({
host: 'http://10.111.12.101',
port: '30583'
})
})(),
/**
* 加密
* @param {string} str 加密字符串
* @param {string} key 加密秘钥
* @return {*} 加密后的字符串
*/
encrypt (str, key) {
const cipher = crypto.createCipheriv('des-ecb', key, '')
cipher.setAutoPadding(true)
let ciph = cipher.update(str, 'utf8', 'base64')
ciph += cipher.final('base64')
return ciph
},
/**
* dec解密
* @param {string} str 加密字符串
* @param {string} key 加密秘钥
* @return {*} 解密后的字符串
*/
decrypt (str, key) {
str = decodeURIComponent(str)
const decipher = crypto.createDecipheriv('des-ecb', key, '')
decipher.setAutoPadding(true)
let txt = decipher.update(str, 'base64', 'utf8')
txt += decipher.final('utf8')
return txt
},
/**
* md5 加密
* @param {string} str 加密字符串
* @return {*} 加密后的字符串
*/
md5 (str) {
const hash = crypto.createHash('sha256')
hash.update(str)
return hash.digest('hex')
},
/**
* 校验字符串货对象为空
* @param {string} str 加密字符串
* @return {boolean} 校验结果
*/
isEmpty (str) {
if (str === '' || str === null || str === undefined) {
return true
}
return false
},
isEmptyObject (obj) {
return typeof obj == 'object' && JSON.stringify(obj) === '{}'
},
/**
* 修改版本号
* @param {string} str 原版本号
* @return {string} 新的版本号
*/
getNewVersion (str) {
if (str === '' || str === null || str === undefined) {
return str
}
let version = str.replace(/\./g, '') - 0
version = version + 1 + ''
version = version.split('').join('.')
return version
},
/**
* 对给定的json数据拆解成为可以
* @param {object} data 数据
* @return {*} 返回mock数据
*/
jsonToMock (data) {
let info = {}
let work = function (data, info) {
for (let key in data) {
let value = data[ key ]
info[ value.name ] = value.mock
// 数组处理
if (value.type == 'object') {
info[ value.name + value.mock ] = {}
work(value.child, info[ value.name + value.mock ])
} else if (value.type.indexOf('array') != -1) {
info[ value.name ] = []
let type = value.type.replace('array(', '').replace(')', '')
let arrayLength = value.mock - 0
for (let i = 0; i < arrayLength; i++) {
if (type == 'object') {
let data1 = {}
info[ value.name ].push(data1)
work(value.child, data1)
} else if (type == 'array') {
console.log('array no deal')
} else if (type == 'number') {
info[ value.name ].push(Mock.mock('@integer'))
} else if (type == 'string') {
info[ value.name ].push(Mock.mock('@string'))
} else if (type == 'boolean') {
info[ value.name ].push(Mock.mock('@boolean'))
}
}
}
}
}
work(data, info)
return Mock.mock(info)
},
/**
* 获取项目组权限
* @param {number} role 权限key
* @return {string} 权限值
*/
getGroupRole (role) {
let result = ''
switch (role) {
case 1:
result = '创建者'
break
case 2:
result = '管理者'
break
case 3:
result = '组成员'
break
default:
break
}
return result
},
getSSOInfo (session) {
/**
* RSA最大解密密文大小
*/
let MAX_DECRYPT_BLOCK = 128
/**
* 公钥解密
* @param date
* @returns {string}
*/
function publicDecrypt (date) {
// 得到私钥
let publicPem = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCClI7NJ+4YeSiVjp6cy5R6x9zGRSdrX06ZrEXy
kDvqBAus+luQvkfcpfU5mLgMeNEjyvTK5Om74fm0NDsoLZ6Y7xeDARgEFMoQ4h1M8cReDszFUUPV
uBAU3akQpGYd7wLMMY+ND7/Hn8GjDk5qyjwlBKUsG6/bJ4hlzej9xORE5wIDAQAB
-----END PUBLIC KEY-----`; // 替换你自己的路径
let publicKey = publicPem.toString()
// 经过base64编码的密文转成buf
let buf = new Buffer(date, 'base64')
// buf转byte数组
// let inputLen = bytes(buf, "base64");
let inputLen = buf.byteLength
// 密文
let bufs = []
// 开始长度
let offSet = 0
// 结束长度
let endOffSet = MAX_DECRYPT_BLOCK
// 分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
let bufTmp = buf.slice(offSet, endOffSet)
bufs.push(crypto.publicDecrypt({ key: publicKey, padding: crypto.RSA_PKCS1_PADDING }, bufTmp))
} else {
let bufTmp = buf.slice(offSet, inputLen)
bufs.push(crypto.publicDecrypt({ key: publicKey, padding: crypto.RSA_PKCS1_PADDING }, bufTmp))
}
offSet += MAX_DECRYPT_BLOCK
endOffSet += MAX_DECRYPT_BLOCK
}
let result = Buffer.concat(bufs).toString()
console.log(result)
return result
}
let tinfo = publicDecrypt(session) || ''
tinfo = tinfo.split(',')
if (tinfo.length == 3) {
return {
id: tinfo[ 0 ],
session: tinfo[ 1 ],
createTime: tinfo[ 2 ]
}
}
return {}
},
getSSOHeader (appid, sec) {
// 定义加/解密的 key(key都放这里了, 加密还有啥意义!^_^)
const initKey = sec
/**
* 定义加密函数
* @param {string} data - 需要加密的数据, 传过来前先进行 JSON.stringify(data)
* @param {string} key - 加密使用的 key
*/
const aesEncrypt = (data, key) => {
/**
* CipherOption, 加密的一些选项:
* mode: 加密模式, 可取值(CBC, CFB, CTR, CTRGladman, OFB, ECB), 都在 CryptoJS.mode 对象下
* padding: 填充方式, 可取值(Pkcs7, AnsiX923, Iso10126, Iso97971, ZeroPadding, NoPadding), 都在 CryptoJS.pad 对象下
* iv: 偏移量, mode === ECB 时, 不需要 iv
* 返回的是一个加密对象
*/
const cipher = CryptoJs.AES.encrypt(data, key, {
mode: CryptoJs.mode.ECB,
padding: CryptoJs.pad.Pkcs7,
iv: ''
})
// 将加密后的数据转换成 Base64
const base64Cipher = cipher.ciphertext.toString(CryptoJs.enc.Base64)
// 处理 Android 某些低版的BUG
return base64Cipher
}
// 获取填充后的key
const key = CryptoJs.enc.Utf8.parse(initKey)
// 定义需要加密的数据
const data = `${new Date() - 0}#${ parseInt(Math.random() * 100000)}`
console.log(data)
// 调用加密函数
const encrypted = aesEncrypt(data, key)
return `${appid}@${encrypted}`
},
/**
* 获取项目权限
* @param {number} role 权限key
* @return {string} 权限值
*/
getProjectRole (role) {
let result = ''
switch (role) {
case 1:
result = '创建者'
break
case 2:
result = '管理者'
break
case 3:
result = '开发者'
break
case 4:
result = '项目成员'
break
default:
break
}
return result
},
/**
* 获取项目权限
* @param {String} tree 节点树 json
* @return {string} child 子节点数组的key
*/
nodeTreeScriptTransform (tree, child = 'child') {
if (!tree) return ''
let parsingtree
try {
parsingtree = JSON.parse(tree)
parsingtree = scriptTransform(parsingtree)
parsingtree = JSON.stringify(parsingtree)
} catch (e) {
parsingtree = tree
console.error('节点树es6语法转换出错了', e)
}
return parsingtree
function scriptTransform (node) {
if (node.script) {
var script = toString.call(node.script) == toString.call([]) ? node.script : [{content: node.script, name: '脚本'}]
node.script = script.filter(s => s && s.content).map(s => {
s.content = wrapper(s.content)
return s
}).map(s => {
s.content = transform(s.content)
return s
}).map(s => {
s.content = deWrapper(s.content)
return s
})
}
if (node[child] && node[child].length) {
node[child] = node[child].map(scriptTransform)
}
return node
}
function transform (script) {
return babel.transform(script, {
minified: false,
babelrc: false,
presets: ['es2015', 'es2017', 'stage-0'],
}).code
}
function wrapper (script) {
return `function tmmmmmmmmmmmmmmmmp (vm){
${script}
}`
}
function deWrapper (script) {
script = script.replace(/^['"]use strict['"];((?:.|[\n\r])*)function\s*tmmmmmmmmmmmmmmmmp\s*\(vm\)\s*\{((?:.|[\n\r])*)\}$/m, '$1\n$2').replace(/[\r\n]+/g, '\n')
return script
}
},
/**
**
** 时间格式化
**/
timeFormat (time, format = 'yyyy/mm/dd') {
if (!time instanceof Date) return ''
let o = {
'y+': time.getFullYear(), // 年份
'M+': time.getMonth() + 1, // 月份
'd+': time.getDate(), // 日
'h+': time.getHours(), // 小时
'm+': time.getMinutes(), // 分
's+': time.getSeconds(), // 秒
'q+': Math.floor((time.getMonth() + 3) / 3), // 季度
'S': time.getMilliseconds() // 毫秒
}
for (let key of Object.keys(o)) {
format = format.replace(new RegExp(`(${key})`), (m, p) => {
let val = `00${o[key]}`
return val.slice(val.length - p.length)
})
}
return format
},
ossConfigValid (config = {}) {
const isInvalid = op => typeof op !== 'string' || op.trim() === '' || /<[^<>]+>/.test(op)
return !(isInvalid(config.accessKeyId) || isInvalid(config.accessKeySecret) || isInvalid(config.host) || isInvalid(config.bucket) || isInvalid(config.region))
},
isSafeUrl (url) {
if (!url || typeof url !== 'string') return false
const urlInfo = urlParser.parse(url)
const isSafeUrl = urlInfo && /^https?:$/.test(urlInfo.protocol) && !/^[\d.:]+$/.test(urlInfo.host) && urlInfo.hostname != 'localhost'
return isSafeUrl
},
}
================================================
FILE: app/middleware/login_handler.js
================================================
module.exports = () => {
return function* (next) {
// 校验登录信息
const accessToken = this.helper.token.getToken.call(this);
var clikey = this.headers['clikey']
if(clikey){
// 请求数据库校验用户uid
const user = yield this.model.UserLogin.findOne({
where: { security: clikey },
});
if (this.helper.tools.isEmpty(user)) throw this.getError({ status: 403, msg: '请求不合法' });
this.request.uid = user.userId;
} else {
if (this.helper.tools.isEmpty(accessToken)) throw this.getError({ status: 401, msg: '您的登录已失效,请重新登录' })
const tokenConfig = this.helper.token.decryptToken.call(this, accessToken);
if (this.helper.tools.isEmpty(tokenConfig.token)
|| this.helper.tools.isEmpty(tokenConfig.date)
|| this.helper.tools.isEmpty(tokenConfig.uid)
|| this.helper.tools.isEmpty(tokenConfig.password)) throw this.getError({ status: 403, msg: '请求不合法' });
// 校验过期时间
if (new Date().getTime() - tokenConfig.date > this.app.config.appConfig.accessTokenKeyTime) {
throw this.getError({ status: 401, msg: '您的登录已失效,请重新登录' });
}
// 请求数据库校验用户uid
const user = yield this.model.UserLogin.findOne({
where: { userId: tokenConfig.uid },
});
if (this.helper.tools.isEmpty(user) || user.password !== tokenConfig.password) throw this.getError({ status: 403, msg: '请求不合法' });
this.request.uid = tokenConfig.uid;
}
yield next;
};
};
================================================
FILE: app/middleware/response_handler.js
================================================
const throttle = require("lodash/throttle")
module.exports = (options, app) => {
var notify = throttle((err, {url, req}) => {
try {
req = JSON.stringify(req)
} catch (e) {
console.log(`try JSON.stringify ${req} error`, e)
}
app.DDNotify(`url: ${url} \n\n request: ${req} \n\n ${err.name} \n\n ${err.message} \n\n ${err.stack}`, '接口请求异常')
}, 30000)
return function* (next) {
try {
console.log('params:', this.href, this.request.body, this.query);
yield next;
const result = this.body;
// 包装返回参数
this.body = this.noWarp ? result : {
code: 1,
msg: 'success',
data: result,
}
} catch (err) {
// 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
console.error('RESPONSE:ERROR', err);
this.status = err.status;
this.body = {
code: err.code || 500,
msg: this.helper.tools.isEmpty(err.msg) ? err.message : err.msg,
};
// notify(err, {url: this.href, req: this.request && this.request.body})
}
};
};
================================================
FILE: app/model/category.js
================================================
'use strict';
module.exports = app => {
return app.model.define('category', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER },
desc: { field: 'desc', type: app.Sequelize.STRING },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_category',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/component.js
================================================
'use strict';
module.exports = app => {
var component = app.model.define('component', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
version: { field: 'version', type: app.Sequelize.STRING },
path: { field: 'path', type: app.Sequelize.STRING },
desc: { field: 'desc', type: app.Sequelize.STRING },
userId: { field: 'user_id', type: app.Sequelize.INTEGER },
useNumber: { field: 'usenumber', type: app.Sequelize.INTEGER },
status: { field: 'status', type: app.Sequelize.INTEGER },
isnew: { field: 'isnew', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER},
visibilitylevel: { field: 'visibilitylevel', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_component',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
return component
};
================================================
FILE: app/model/component_use.js
================================================
'use strict';
module.exports = app => {
return app.model.define('ComponentUse', {
cid: { field: 'cid', type: app.Sequelize.INTEGER },
useNumber: { field: 'usenumber', type: app.Sequelize.INTEGER },
love: { field: 'love', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_component_use',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/group.js
================================================
module.exports = app => {
return app.model.define('group', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
createUserId: { field: 'create_user_id', type: app.Sequelize.BIGINT },
description: { field: 'description', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
logo: { field: 'logo', type: app.Sequelize.STRING },
type: { field: 'type', type: app.Sequelize.INTEGER },
projectCount: { field: 'project_count', type: app.Sequelize.INTEGER },
userCount: { field: 'user_count', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_group',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/group_project.js
================================================
module.exports = app => {
return app.model.define('groupProject', {
groupId: { field: 'group_id', type: app.Sequelize.BIGINT, primaryKey: true },
projectId: { field: 'project_id', type: app.Sequelize.BIGINT, primaryKey: true, },
status: { field: 'status', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_group_and_project',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/group_user.js
================================================
module.exports = app => {
return app.model.define('groupUser', {
groupId: { field: 'group_id', type: app.Sequelize.BIGINT, primaryKey: true },
userId: { field: 'user_id', type: app.Sequelize.BIGINT, primaryKey: true },
status: { field: 'status', type: app.Sequelize.INTEGER },
role: { field: 'role', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_group_and_user',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/history_interface.js
================================================
/**
* Created with WebStorm.
* User: kevan
* Email:258137678@qq.com
* Date: 2017/5/3
* Time: 下午3:47
* To change this template use File | Settings | File Templates.
*/
'use strict'
module.exports = app => {
return app.model.define('historyInterface', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
createUserId: { field: 'create_user_id', type: app.Sequelize.BIGINT },
description: { field: 'description', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
path: { field: 'path', type: app.Sequelize.STRING },
request: { field: 'request_content', type: app.Sequelize.STRING },
response: { field: 'response_content', type: app.Sequelize.STRING },
mockResponse: { field: 'mock_response', type: app.Sequelize.STRING },
projectId: { field: 'project_id', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER },
version: { field: 'version', type: app.Sequelize.STRING },
interfaceId: { field: 'interface_id', type: app.Sequelize.INTEGER },
lastedVersion: { field: 'lasted_version', type: app.Sequelize.INTEGER },
modifyUserId: { field: 'modify_user_id', type: app.Sequelize.INTEGER },
aduitRemark: { field: 'aduit_remark', type: app.Sequelize.STRING },
endStatus: { field: 'end_status', type: app.Sequelize.INTEGER },
deprecated: { field: 'deprecated', type: app.Sequelize.INTEGER },
frontStatus: { field: 'front_status', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_history_interface',
createdAt: 'createTime',
updatedAt: 'updateTime'
}, {
classMethods: {}
})
}
================================================
FILE: app/model/interface.js
================================================
module.exports = app => {
return app.model.define('interface', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
createUserId: { field: 'create_user_id', type: app.Sequelize.BIGINT },
description: { field: 'description', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
path: { field: 'path', type: app.Sequelize.STRING },
request: { field: 'request_content', type: app.Sequelize.STRING },
response: { field: 'response_content', type: app.Sequelize.STRING },
projectId: { field: 'project_id', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER },
versionInterfaceId: { field: 'version_interface_id', type: app.Sequelize.INTEGER },
modifyUserId: { field: 'modify_user_id', type: app.Sequelize.INTEGER },
publishStatus: { field: 'publish_status', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_interface',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/interface_draf.js
================================================
module.exports = app => {
return app.model.define('interfaceDraf', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
projectId: { field: 'project_id', type: app.Sequelize.BIGINT },
apis: { field: 'apis', type: app.Sequelize.STRING },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_interface_draf',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/interface_log.js
================================================
module.exports = app => {
return app.model.define('interfaceLog', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
remark: { field: 'remark', type: app.Sequelize.STRING },
uid: { field: 'uid', type: app.Sequelize.BIGINT },
interfaceId: { field: 'interface_id', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_interface_log',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/interface_user.js
================================================
module.exports = app => {
return app.model.define('interfaceUser', {
userId: { field: 'user_id', type: app.Sequelize.BIGINT, primaryKey: true },
interfaceId: { field: 'interface_id', type: app.Sequelize.INTEGER, primaryKey: true },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_interface_user',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/pages.js
================================================
'use strict';
module.exports = app => {
return app.model.define('pages', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
key: { field: 'key', type: app.Sequelize.STRING },
name: { field: 'name', type: app.Sequelize.STRING },
image: { field: 'image', type: app.Sequelize.STRING },
desc: { field: 'desc', type: app.Sequelize.STRING },
content: { field: 'content', type: app.Sequelize.STRING },
draft: { field: 'draft', type: app.Sequelize.STRING },
projectId: { field: 'project_id', type: app.Sequelize.INTEGER },
status: { field: 'status', type: app.Sequelize.INTEGER },
featured: { field: 'featured', type: app.Sequelize.INTEGER },
visibilitylevel: { field: 'visibilitylevel', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER }, // 页面类型,默认0,普通页面;1,flutter 页面;
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
fork: { field: 'fork', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_pages',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/pages_history.js
================================================
'use strict';
module.exports = app => {
return app.model.define('pagesHistory', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true },
content: { field: 'content', type: app.Sequelize.STRING },
userId: { field: 'user_id', type: app.Sequelize.INTEGER },
pageId: { field: 'page_id', type: app.Sequelize.INTEGER },
status: { field: 'status', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_pages_history',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/project.js
================================================
module.exports = app => {
return app.model.define('project', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
key: { field: 'key', type: app.Sequelize.STRING },
createUserId: { field: 'create_user_id', type: app.Sequelize.BIGINT },
image: { field: 'image', type: app.Sequelize.STRING },
desc: { field: 'desc', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
visibilitylevel: { field: 'visibilitylevel', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_project',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/project_data.js
================================================
module.exports = app => {
return app.model.define('projectData', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
projectId: { field: 'project_id', type: app.Sequelize.BIGINT },
status: { field: 'status', type: app.Sequelize.INTEGER },
name: { field: 'name', type: app.Sequelize.STRING },
remark: { field: 'remark', type: app.Sequelize.STRING },
content: { field: 'content', type: app.Sequelize.STRING },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_project_data',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/resTagsRel.js
================================================
'use strict';
module.exports = app => {
return app.model.define('resTagsRel', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
rid: { field: 'rid', type: app.Sequelize.INTEGER },
tid: { field: 'tid', type: app.Sequelize.INTEGER },
cid: { field: 'cid', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_res_tags_rel',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/resources.js
================================================
'use strict';
module.exports = app => {
return app.model.define('resources', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
icon: { field: 'icon', type: app.Sequelize.STRING },
content: { field: 'content', type: app.Sequelize.STRING },
categoryId: { field: 'category_id', type: app.Sequelize.INTEGER },
userId: { field: 'user_id', type: app.Sequelize.INTEGER },
status: { field: 'status', type: app.Sequelize.INTEGER },
desc: { field: 'desc', type: app.Sequelize.STRING },
visibilitylevel: { field: 'visibilitylevel', type: app.Sequelize.INTEGER },
useCount: { field: 'use_count', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_resources',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/tag.js
================================================
/**
* Created with WebStorm.
* User: star
* Email:258137678@qq.com
* Date: 2017/5/3
* Time: 下午3:47
* To change this template use File | Settings | File Templates.
*/
'use strict'
module.exports = app => {
return app.model.define('tag', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
projectId: { field: 'project_id', type: app.Sequelize.INTEGER },
name: { field: 'name', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_tag',
createdAt: 'createTime',
updatedAt: 'updateTime'
}, {
classMethods: {}
})
}
================================================
FILE: app/model/tag_interface.js
================================================
module.exports = app => {
return app.model.define('tagInterface', {
tagId: { field: 'tag_id', type: app.Sequelize.BIGINT, primaryKey: true },
interfaceId: { field: 'interface_id', type: app.Sequelize.INTEGER, primaryKey: true },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_tag_interface',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/tags.js
================================================
'use strict';
module.exports = app => {
return app.model.define('tags', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
categoryId: { field: 'category_id', type: app.Sequelize.INTEGER },
status: { field: 'status', type: app.Sequelize.INTEGER },
useNumber: { field: 'usenumber', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_tags',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/template.js
================================================
'use strict';
module.exports = app => {
return app.model.define('template', {
id: { field: 'id', type: app.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { field: 'name', type: app.Sequelize.STRING },
categoryId: { field: 'category_id', type: app.Sequelize.INTEGER },
content: { field: 'content', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
desc: { field: 'desc', type: app.Sequelize.STRING },
image: { field: 'image', type: app.Sequelize.STRING },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT }
}, {
timestamps: true,
tableName: 'tb_template',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user.js
================================================
module.exports = app => {
return app.model.define('user', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
email: { field: 'email', type: app.Sequelize.STRING },
emailStatus: { field: 'email_status', type: app.Sequelize.INTEGER },
name: { field: 'name', type: app.Sequelize.STRING },
telephone: { field: 'telephone', type: app.Sequelize.STRING },
photo: { field: 'photo', type: app.Sequelize.STRING },
projectCount: { field: 'project_count', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
role: { field: 'role', type: app.Sequelize.INTEGER },
oauth: { field: 'oauth', type: app.Sequelize.STRING }
}, {
timestamps: true,
tableName: 'tb_user',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user_grade.js
================================================
module.exports = app => {
return app.model.define('userGrade', {
userId: { field: 'user_id', type: app.Sequelize.BIGINT, primaryKey: true },
projectNum: { field: 'project_num', type: app.Sequelize.INTEGER },
groupNum: { field: 'group_num', type: app.Sequelize.INTEGER },
interfaceNum: { field: 'interface_num', type: app.Sequelize.INTEGER },
favorateProjectNum: { field: 'favorate_project_num', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_user_grade',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user_login.js
================================================
module.exports = app => {
return app.model.define('userLogin', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
password: { field: 'password', type: app.Sequelize.STRING },
userId: { field: 'user_id', type: app.Sequelize.BIGINT },
email: { field: 'email', type: app.Sequelize.STRING },
status: { field: 'status', type: app.Sequelize.INTEGER },
lastIp: { field: 'last_ip', type: app.Sequelize.STRING },
lastLogin: { field: 'last_login', type: app.Sequelize.DATE },
ssoUid: { field: 'sso_uid', type: app.Sequelize.BIGINT },
security: { field: 'security', type: app.Sequelize.STRING },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_user_login',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user_login_log.js
================================================
module.exports = app => {
return app.model.define('userLoginLog', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
userId: { field: 'user_id', type: app.Sequelize.BIGINT },
ip: { field: 'ip', type: app.Sequelize.STRING },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_login_log',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user_notice.js
================================================
module.exports = app => {
return app.model.define('userNotice', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
createUserId: { field: 'create_user_id', type: app.Sequelize.BIGINT },
userId: { field: 'user_id', type: app.Sequelize.BIGINT },
content: { field: 'content', type: app.Sequelize.STRING },
title: { field: 'title', type: app.Sequelize.STRING },
readStatus: { field: 'read_status', type: app.Sequelize.INTEGER },
type: { field: 'type', type: app.Sequelize.INTEGER },
joinId: { field: 'join_id', type: app.Sequelize.BIGINT },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_user_notice',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user_notice_type.js
================================================
module.exports = app => {
return app.model.define('userNoticeType', {
userId: { field: 'user_id', type: app.Sequelize.BIGINT, primaryKey: true },
type: { field: 'type', type: app.Sequelize.INTEGER, primaryKey: true },
messageNotice: { field: 'message_notice', type: app.Sequelize.INTEGER },
emailNotice: { field: 'email_notice', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_user_notice_type',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/user_project.js
================================================
module.exports = app => {
return app.model.define('userProject', {
projectId: { field: 'project_id', type: app.Sequelize.BIGINT, primaryKey: true },
userId: { field: 'user_id', type: app.Sequelize.BIGINT, primaryKey: true, },
status: { field: 'status', type: app.Sequelize.INTEGER },
role: { field: 'role', type: app.Sequelize.INTEGER },
favor: { field: 'is_favor', type: app.Sequelize.INTEGER },
updateTime: { field: 'update_time', type: app.Sequelize.BIGINT },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_user_and_project',
createdAt: 'createTime',
updatedAt: 'updateTime',
}, {
classMethods: {},
});
};
================================================
FILE: app/model/valid_code.js
================================================
module.exports = app => {
return app.model.define('validCode', {
id: { field: 'id', type: app.Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
code: { field: 'code', type: app.Sequelize.STRING },
userId: { field: 'user_id', type: app.Sequelize.BIGINT },
email: { field: 'email', type: app.Sequelize.STRING },
expireTime: { field: 'expire_time', type: app.Sequelize.DATE },
createTime: { field: 'create_time', type: app.Sequelize.BIGINT },
}, {
timestamps: true,
tableName: 'tb_valid_code',
createdAt: 'createTime',
updatedAt: false,
}, {
classMethods: {},
});
};
================================================
FILE: app/router.js
================================================
'use strict'
module.exports = app => {
console.log(app.config)
app.router.prefix('/' + app.config.API_PATH.replace(/^\/+|\/+$/g, ''))
app.post(`/users/login`, 'user.login')
app.post(`/users/register`, 'user.register')
app.post(`/users/activeEmail`, 'user.activeEmail')
app.get(`/users/sendActiveEmail`, 'user.sendActiveEmail')
app.post(`/users/sendEmail`, 'user.sendEmail')
app.get(`/users/info`, 'user.info')
app.post(`/users/logout`, 'user.logout')
app.put(`/users/edit`, 'user.edit')
app.put(`/users/updatePassword`, 'user.updatePassword')
app.post(`/users/newUpdatePassword`, 'user.newUpdatePassword')
app.post(`/users/forgetPassword`, 'user.forgetPassword')
app.get(`/users/search`, 'user.search')
app.get(`/upload/getTocken`, 'user.getTocken')
app.get(`/kaptcha/init`, 'kaptcha.init')
// 第三方登录
app.post(`/users/oauthCode`, 'user.oauthCode')
app.post(`/users/oauthLogin`, 'user.oauthLogin')
// 组件列表
app.post(`/component/useone`, 'component.useone') // 组件使用次数增加
app.post(`/component/searchByName`, 'component.searchByName') // 我的分组列表
app.post(`/component/searchAllStatusByName`, 'component.searchAllStatusByName') // 我的分组列表
app.post(`/component/find`, 'component.find') // 我的分组列表
app.post(`/component/add`, 'component.save')
app.get(`/component/info`, 'component.info') // 获取组件信息
app.post(`/component/updata`, 'component.updata') // 更新组件信息
app.post(`/component/delete`, 'component.delete') // 更新组件信息
app.post(`/component/import`, 'component.import') // 组件导入
// 分组管理
app.delete(`/project/group`, 'group.delete')
app.put(`/project/group`, 'group.update')
app.get(`/project/group`, 'group.list') // 我的分组列表
app.get(`/project/groupinfo`, 'group.info') // 特定分组详情
app.post(`/project/group`, 'group.add')
// 分组成员管理
app.post(`/project/groupuser`, 'groupUser.add')
app.delete(`/project/groupuser`, 'groupUser.delete')
app.put(`/project/groupuser`, 'groupUser.update')
app.get(`/project/groupuser`, 'groupUser.list')
// 项目管理
app.delete(`/project/project`, 'project.delete')
app.put(`/project/project`, 'project.update')
app.get(`/project/project`, 'project.list') // 我的项目列表
app.get(`/project/projectinfo`, 'project.info') // 特定项目详情
app.post(`/project/project`, 'project.add')
app.get(`/project/groupproject`, 'project.groupProject') // 某分组项目列表
app.post(`/project/favorateproject`, 'project.favorateProject')
app.delete(`/project/favorateproject`, 'project.cancelFavorateProject')
app.get(`/project/favorateproject`, 'project.getFavorateProject')
// 项目成员管理
app.post(`/project/projectuser`, 'projectUser.add')
app.delete(`/project/projectuser`, 'projectUser.delete')
app.put(`/project/projectuser`, 'projectUser.update')
app.get(`/project/projectuser`, 'projectUser.list')
// pages
app.post(`/editor/pages/pvuv`, 'pages.pvuv')
app.post(`/editor/pages/pv`, 'pages.pv')
app.get(`/editor/pages/publiclist`, 'pages.publiclist')
app.post(`/editor/pages/publiclist`, 'pages.publiclist')
app.post(`/editor/pages/list`, 'pages.list')
app.post(`/editor/pages/save`, 'pages.save')
app.post(`/editor/pages/delete`, 'pages.delete')
app.post(`/editor/pages/info`, 'pages.info')
app.post(`/editor/pages/detail`, 'pages.detail')
app.post(`/editor/pages/editor-detail`, 'pages.detail') // 编辑起调用这个,走登陆判断
app.post(`/editor/pages/change-status`, 'pages.changeStatus')
app.post(`/editor/pages/set-home-page`, 'pages.setHomePage')
app.post(`/editor/pages/publish`, 'pages.publish')
app.get(`/editor/pages/count`, 'pages.count')
app.get(`/editor/pages/history`, 'pages.history')
app.get(`/editor/pages/history-delete`, 'pages.historyDelete')
app.post(`/editor/pages/history-publish`, 'pages.historyPublish')
app.post(`/editor/pages/history-to-draft`, 'pages.historyToDraft')
app.post(`/editor/pages/update-fork`, 'pages.updateFork')
app.post(`/editor/pages/psd-to-page`, 'pages.psdToPage')
app.post(`/editor/pages/featuring`, 'pages.featuringPages')
app.post(`/editor/pages/update-featured`, 'pages.updateFeatured')
// names
app.post(`/editor/pages/getNameBykeys`, 'pages.getNameBykeys')
// template
app.post(`/editor/template/list`, 'template.list')
app.post(`/editor/template/save`, 'template.save')
app.post(`/editor/template/delete`, 'template.delete')
app.post(`/editor/template/detail`, 'template.detail')
// category
app.post(`/editor/category/list`, 'category.list')
app.post(`/editor/category/save`, 'category.save')
app.post(`/editor/category/delete`, 'category.delete')
// resources
app.post(`/editor/resources/list`, 'resources.list')
app.get(`/editor/resources/info`, 'resources.info')
app.post(`/editor/resources/save`, 'resources.save')
app.post(`/editor/resources/delete`, 'resources.delete')
app.post(`/editor/resources/addUseCount`, 'resources.addUseCount')
// tags
app.post(`/editor/tags/list`, 'tags.list')
app.post(`/editor/tags/add`, 'tags.add')
app.post(`/editor/tags/useone`, 'tags.useone')
app.get(`/test`, 'home.index')
// proxy
// img-proxy
app.get(`/proxy/imgCors`, 'proxy.imgCorsProxy')
// img-proxy alias
app.get(`/cors-proxy`, 'proxy.imgCorsProxy')
// transparent-proxy
app.post(`/proxy/transparent`, 'proxy.transparentProxy')
// 数据统计
// es
app.get(`/statistics/report`, 'statistics.report')
app.post(`/statistics/getPUV`, 'statistics.getPUV')
app.post(`/statistics/getPages`, 'statistics.getPages')
app.post(`/statistics/getActions`, 'statistics.getActions')
app.post(`/statistics/actionTrack`, 'statistics.actionTrack')
app.post(`/statistics/getTodayOutline`, 'statistics.getTodayOutline')
app.post(`/statistics/getUtms`, 'statistics.getUtms')
app.post(`/statistics/getViewTime`, 'statistics.getViewTime')
app.post(`/statistics/getProjectReport`, 'statistics.getProjectReport')
app.post(`/statistics/getGroupReport`, 'statistics.getGroupReport')
// ossupload
app.post(`/ossupload/uploadByUrls`, 'ossupload.uploadByUrls')
app.post(`/ossupload/uploadFile`, 'ossupload.uploadFile')
// word to html
app.post(`/transform/word2html`, 'proxy.word2html')
}
================================================
FILE: app/schedule/es_delete.js
================================================
module.exports = {
schedule: {
cron: '0 0 1 * *', // 每月1号
type: 'worker', // 随机一个 worker 执行
},
cronOptions: {
tz: 'Asia/Shanghai'
},
disable: false,
async task(ctx) {
console.info('开始清理6个月前的日志')
let status = await ctx.service.statistics.deleteIndices({monthBefore: 6})
console.info('日志清理完成', status)
}
}
================================================
FILE: app/schedule/es_report.js
================================================
module.exports = {
schedule: {
interval: '1m', // 1分钟间隔
type: 'all',
},
async task(ctx) {
console.info('开始执行上报打点日志定时任务')
let status = await ctx.service.statistics.addDocs()
console.info('结束执行上报打点日志定时任务,操作', status ? '成功' : '失败')
}
}
================================================
FILE: app/schedule/pu_uv_ratio.js
================================================
const PUV_RATIO_THRESHOLD = 1.6 // 小时 pv/uv 平均1.34 最大值3.72 取1.6为告警阈值
module.exports = app => {
return {
schedule: {
// interval: '1m',
cron: '0 0 * * * *', // 每小时一次
type: 'worker',
},
disable: 0,
cronOptions: {
tz: 'Asia/Shanghai'
},
async task(ctx) {
let {sum: {pv, uv}} = await ctx.service.statistics.getPUV({pageId: '', timePeriod: [Date.now() - 3600000], utm: '', interval: 'hour', appId: 'tview'})
let ratio = pv / uv
if (ratio >= PUV_RATIO_THRESHOLD) {
app.DDNotify(
`最近一小时码良页面 \n\n 访问量 ${pv} \n\n 访问人数 ${uv} \n\n 比值 ${ratio.toFixed(2)} \n\n 注意排查是否流量异常`,
'码良访问量/访问人数比超限告警')
}
}
}
}
================================================
FILE: app/schedule/pvcount.js
================================================
module.exports = app => {
return {
schedule: {
// interval: '30s',
cron: '0 0 */1 * *', // 每天零点
type: 'all',
},
disable: true,
cronOptions: {
tz: 'Asia/Shanghai'
},
async task(ctx) {
console.info('page_count_log', app.getPV())
}
}
}
================================================
FILE: app/service/base.js
================================================
module.exports = app => {
class BaseService extends app.Service {
* getUserRole (uid, projectId) {
let role = 100;
uid = uid || (this.ctx.request && this.ctx.request.uid);
let userProject = yield this.ctx.model.UserProject.findOne({ where: { userId: uid, projectId, status: 1 } });
if (userProject) role = userProject.role;
// 查询是否有分组权限
const group = yield this.ctx.model.GroupProject.findOne({ where: { projectId, status: 1 } });
if (group) {
// 查询分组权限
const groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: uid, groupId: group.groupId, status: 1 } });
if (groupUser) role = role < groupUser.role ? role : groupUser.role;
} else {
role = 100;
}
return role;
}
* sendNotice (uids, notice, noticeType) {
if (!uids || uids.length == 0 || noticeType < 0 || !notice) return;
for (let i = 0; i < uids.length; i++) {
let uid = uids[ i ];
// 查询用户信息
const user = yield this.ctx.model.User.findOne({ where: { id: uid } });
if (!user) continue;
// 查询用户的通知设置
const userNotice = yield this.ctx.model.UserNoticeType.findOne({ where: { userId: uid, type: noticeType } });
if (!userNotice) continue;
if (userNotice.messageNotice == 1) {
// 发送站内信
notice.userId = uid;
yield this.ctx.model.UserNotice.create(notice);
}
if (userNotice.emailNotice == 1) {
// 发送邮件
this.app.sendMail({
receivers: [ user.email ],
tplName: 'notice',
data: {
content: notice.content,
},
callback () {},
subject: notice.title
});
}
}
}
}
return BaseService;
};
================================================
FILE: app/service/category.js
================================================
/**
* Created with WebStorm.
* User: kevan
* Email:258137678@qq.com
* Date: 2017/5/3
* Time: 上午9:30
* To change this template use File | Settings | File Templates.
*/
var md5 = require('md5');
'use strict'
module.exports = app => {
class Category extends app.Service {
* list (query) {
console.log('service start --list')
let _where = {
status: (query.status == undefined) ? 1 : query.status,
}
if (query.type) {
_where.type = {
$in: query.type
}
}
return yield this.ctx.model.Category.findAll({ where: _where });
}
* save (query) {
try {
var _item = {
name: query.name,
type: query.type,
desc: query.desc,
status: (query.status == undefined) ? 1 : query.status,
}
if (query.id) {
// 编辑
// 参数验证,1判断是否存在该id,2是否重名
if (!(yield this.ctx.model.Category.findOne({
where: {
status: 1,
id: query.id
}
}))) throw this.ctx.getError({ msg: `不存在id为${query.id}的类型` });
if (yield this.ctx.model.Category.findOne({
where: {
name: query.name,
status: 1,
type: query.type,
id: { $ne: query.id }
}
})) throw this.ctx.getError({ msg: `已经存在名称为${query.name}的类型` });
yield this.ctx.model.Category.update(_item, { where: { id: query.id } })
return { id: query.id }
} else {
// 参数验证 是否重名
if (yield this.ctx.model.Category.findOne({
where: {
type: query.type,
name: query.name,
status: 1
}
})) throw this.ctx.getError({ msg: `已经存在名称为${query.name}的类型` });
var newItem = yield this.ctx.model.Category.create(_item)
return { id: newItem.id }
}
} catch (e) {
throw this.ctx.getError({ msg: e.msg || `${query.id ? '更新失败' : '新增失败'}`, error: e });
}
}
* delete (id) {
var item = yield this.ctx.model.Category.findOne({ where: { id: id } })
if (item) {
yield this.ctx.model.Category.update({ status: 0 }, { where: { id: id } });
return true
}
}
}
return Category
}
================================================
FILE: app/service/component.js
================================================
const co = require('co')
const OSS = require('ali-oss')
const request = require('request')
var JSZip = require("jszip")
function setConstVar (str, {
publicpath, namespace, name, version
}) {
str = str.replace(/__OSS_BUCKET__/g, publicpath)
str = str.replace(/__NAMESPACE__/g, namespace)
str = str.replace(/__NAME__/g, name)
str = str.replace(/__VERSION__/g, version)
return str
}
function normalizeName (name = '') {
return String(name).replace(/[-_\s]+(\w)/g, (m, p) => p.toUpperCase())
}
async function genManifest (pkgfile, uid) {
const pkg = JSON.parse(await pkgfile.async('text'))
const ns = normalizeName(pkg.author || (uid + 10000).toString(16))
return {
namespace: ns,
name: normalizeName(pkg.name),
type: +pkg.type || 0,
description: pkg.description,
version: pkg.version,
visibilitylevel: Number(!pkg.private),
tags: (pkg.tags || []).filter(t => !isNaN(t)).map(t => ({id: t}))
}
}
module.exports = app => {
const ossClient = new OSS({
region: app.config.oss.region,
accessKeyId: app.config.oss.accessKeyId,
accessKeySecret: app.config.oss.accessKeySecret,
bucket: app.config.oss.bucket,
})
const OSS_HOST = app.config.oss.host
async function downloadZip (id, token) {
const downloadApi = app.config.zipDownloadApi
const options = {
'method': 'POST',
'url': downloadApi,
'headers': {
'clikey': token,
'Content-Type': 'application/json'
},
encoding: null,
body: JSON.stringify({id: String(id)})
}
return await new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if(error || response.statusCode !== 200) reject(error || new Error('资源下载错误'))
resolve(body)
})
})
}
async function ossUpload (files = [], manifest) {
const DIR = process.env.EGG_SERVER_ENV !== 'production' ? 'components-test' : 'components'
let mainFile
for (let file of files) {
const name = file.name
const filepath = `${manifest.namespace}/${manifest.name}/${manifest.version}/${name.replace(/^dist[\/]/, '')}`
let data
if (/(index|editor)\.js$/.test(name)) {
data = await file.async('text')
data = setConstVar(data, { namespace: manifest.namespace, publicpath: `${OSS_HOST}/${DIR}/`, name: manifest.name, version: manifest.version })
} else {
data = await file.async('nodebuffer')
}
const res = await upload(filepath, data)
console.log(`${res.name} 上传成功: ${res.path}`)
if (/index\.js$/.test(name)) mainFile = res.path
}
return mainFile
async function upload (filepath, data) {
if (typeof data === 'string') data = Buffer.from(data)
let result = await co(function * () {
return yield ossClient.put(`${DIR}/${filepath}`, data)
})
return {
name: result && result.name,
path: result && result.url.replace(/^http(?!s)/, 'https')
}
}
}
class Component extends app.Service {
* info (obj) {
var item = yield this.ctx.model.Component.findOne({
where: {
id: obj.id,
userId: obj.uid,
isnew: 1
}
})
if (!item || !item.dataValues) return item
console.log(item)
let tags = yield this.ctx.model.query(`select
t.id,
t.name
from tb_res_tags_rel rel
left join tb_tags t on t.id=rel.tid
where rel.rid =:id and cid=3
`, {
type: 'SELECT', replacements: obj
})
item.dataValues.tags = tags
return item
}
* list (query) {
var where = {}
if (query.like) {
if (query.name) {
where[ '$and' ] = {
'$or': [ {
name: {
$like: `%${query.name}%`
}
}, {
desc: {
$like: `%${query.name}%`
}
} ]
}
}
where.isnew = 1
if (query.type !== undefined && query.type !== null) where.type = query.type
if (query.onlyMine) {
where.userId = +query.uid
} else {
where[ '$or' ] = [ {
userId: query.uid
}, {
visibilitylevel: 1
} ]
}
} else {
query.name && (where.name = query.name)
query.version && (where.version = query.version)
}
if (query.status != 'all') {
where.status = 1
}
this.ctx.model.Component.hasOne(this.ctx.model.ComponentUse, {
foreignKey: 'cid'
})
if (query.tags && query.tags.length) {
let tagrefs = yield this.ctx.model.ResTagsRel.findAll({
where: {
tid: {'$or': query.tags.map(t => t.id || t)}
}
})
let cid = Array.from(new Set((tagrefs || []).map(t => t.rid)))
if (!cid || !cid.length) return {list: []}
where.id = {'$or': cid}
}
var list = yield this.ctx.model.Component.findAll({
where: where,
include: [ {
required: false,
model: this.ctx.model.ComponentUse
} ],
order: [
[ this.ctx.model.ComponentUse, 'usenumber', 'DESC' ]
],
limit: query.limit || 100
})
return {
list
}
}
* updata (query) {
console.log(query)
let t = yield app.model.transaction();
try {
var _item = query
yield this.ctx.model.Component.update({
desc: query.desc,
visibilitylevel: query.visibilitylevel,
}, {
where: {
name: query.name,
userId: query.userId
}
}, {
transaction: t
})
// 移除标签和资源的关系
yield this.ctx.model.query(` DELETE FROM tb_res_tags_rel WHERE rid=:id and cid=3; `, {
type: 'BULKDELETE',
transaction: t,
replacements: query
});
query.tags = query.tags || []
for (let tag of query.tags) {
yield this.ctx.service.tags.useone({
id: tag.id
})
// 绑定标签和资源的关系
yield this.ctx.model.ResTagsRel.create({
rid: query.id,
tid: tag.id,
cid: 3
}, {
transaction: t
})
}
yield t.commit();
return {
id: query.id
}
} catch (e) {
yield t.rollback();
throw this.ctx.getError({
msg: e.msg || `${query.id ? '更新失败' : '新增失败'}`,
error: e
});
}
}
* save (query) {
let t = yield app.model.transaction();
try {
var _item = query
// 如果没有用户信息,资源设为公共资源
if (!query.userId) {
_item.visibilitylevel = 1
}
_item.status = 1
_item.isnew = 1
// 查找最新的元素
var currComponent = yield this.ctx.model.Component.findOne({
where: {
isnew: 1,
name: query.name
}
})
yield this.ctx.model.Component.update({
isnew: 0
}, {
where: {
isnew: 1,
name: query.name
}
}, {
transaction: t
})
var newItem = yield this.ctx.model.Component.create(_item, {
transaction: t
})
query.id = newItem.id
if (currComponent) {
var componentUse = yield this.ctx.model.ComponentUse.findOne({
where: {
cid: currComponent.id
}
}, {
transaction: t
})
// 使用量记录,如果有记录则加一,没有的话。初始化创建一个
if (componentUse) {
yield this.ctx.model.ComponentUse.update({
cid: newItem.id
}, {
where: {
cid: currComponent.id
}
}, {
transaction: t
})
} else {
yield this.ctx.model.ComponentUse.create({
cid: newItem.id,
useNumber: currComponent.useNumber
}, {
transaction: t
})
}
// 更新tag标签
// 移除标签和资源的关系
var _query = {
id: newItem.id,
rid: currComponent.id
}
yield this.ctx.model.query(`update tb_res_tags_rel set rid=:id WHERE rid=:rid and cid=3; `, {
transaction: t, replacements: _query
});
} else {
// 首次添加时,生成标签
query.tags = query.tags || []
for (let tag of query.tags) {
yield this.ctx.service.tags.useone({
id: tag.id
})
// 绑定标签和资源的关系
yield this.ctx.model.ResTagsRel.create({
rid: query.id,
tid: tag.id,
cid: 3
}, {
transaction: t
})
}
}
yield t.commit();
return {
id: query.id
}
} catch (e) {
yield t.rollback();
throw this.ctx.getError({
msg: e.msg || `${query.id ? '更新失败' : '新增失败'}`,
error: e
});
}
}
* useone (query) {
let t = yield app.model.transaction();
try {
var component = yield this.ctx.model.Component.findOne({
where: {
id: query.id,
status: 1
}
}, {
transaction: t
})
if (component) {
var useNum = component.useNumber + 1
yield this.ctx.model.Component.update({
useNumber: useNum
}, {
where: {
id: query.id
}
}, {
transaction: t
})
var componentUse = yield this.ctx.model.ComponentUse.findOne({
where: {
cid: component.id
}
}, {
transaction: t
})
// 使用量记录,如果有记录则加一,没有的话。初始化创建一个
if (componentUse) {
yield this.ctx.model.ComponentUse.update({
useNumber: componentUse.useNumber + 1
}, {
where: {
cid: componentUse.cid
}
}, {
transaction: t
})
} else {
yield this.ctx.model.ComponentUse.create({
cid: component.id,
useNumber: 1
}, {
transaction: t
})
}
} else {
throw this.ctx.getError({
msg: '不纯在该组件'
});
}
yield t.commit();
return {
id: query.id
}
} catch (e) {
yield t.rollback();
throw this.ctx.getError({
msg: e.msg || `${query.id ? '更新失败' : '新增失败'}`,
error: e
});
}
}
* delete (obj) {
var item = yield this.ctx.model.Component.findOne({
where: {
id: obj.id,
userId: obj.uid
}
})
if (item) {
yield this.ctx.model.Component.update({
status: obj.status || 0
}, {
where: {
id: obj.id,
userId: obj.uid
}
});
return true
} else {
throw this.ctx.getError({
msg: `该资源不可删除或者不是你上传的资源`
});
}
}
async import ({id, token}, userId) {
const valid = this.ctx.helper.tools.ossConfigValid(app.config.oss)
if (!valid) throw this.ctx.getError({ msg: '您未正确配置 oss 服务的相关字段,无法使用对象存储服务' })
const that = this
const zipBuffer = await downloadZip(id, token).catch(e => (console.error(e), null))
if (!zipBuffer) return
const zip = await JSZip.loadAsync(zipBuffer)
const manifest = await genManifest(zip.files['package.json'], userId)
const exists = await co(function * () {
return yield that.list({
name: `${manifest.namespace}/${manifest.name}`,
version: manifest.version,
uid: userId,
like: false
})
})
if (exists && exists.list && exists.list.length > 0) {
throw this.ctx.getError({
msg: '组件库已存在相同名称和版本的组件,终止导入',
})
}
const files = Object.keys(zip.files).filter(k => k !== 'package.json' && k !== 'install.js').map(k => zip.files[k])
const mainFile = await ossUpload(files, manifest)
const res = await co(function * () {
return yield that.save({
userId: userId,
name: `${manifest.namespace}/${manifest.name}`,
version: manifest.version,
desc: manifest.description,
type: manifest.type,
tags: manifest.tags,
path: mainFile.replace(/^http:/, 'https:'),
visibilitylevel: manifest.visibilitylevel
})
})
return res
}
}
return Component
}
================================================
FILE: app/service/group.js
================================================
const nunjucks = require('nunjucks');
const noticeMessage = require('../extend/noticeMessage');
module.exports = app => {
class Group extends app.Service {
* delete (obj) {
// 检查用户与组的权限
let groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.uid, groupId: obj.groupId } });
if (!groupUser) throw this.ctx.getError({ msg: '无权限操作' });
if (groupUser.role !== app.config.appConfig.roleOwner || groupUser.status !== 1) {
throw this.ctx.getError({ msg: '无操作权限' });
}
// 检查分组是否存在
let group = yield this.ctx.model.Group.findOne({ where: { id: obj.groupId } });
if (!group) throw this.ctx.getError({ msg: '分组不存在' });
// 软删除分组信息
let t = yield app.model.transaction();
try {
group = {};
group.status = 0;
yield this.ctx.model.Group.update(group, { where: { id: obj.groupId }, transaction: t });
// delete all group-user relation
yield this.ctx.model.GroupUser.update({ status: 0 }, { where: { groupId: obj.groupId }, transaction: t });
// delete all prioject relation
const groupProjects = yield this.ctx.model.GroupProject.findAll({ where: { status: 1, groupId: obj.groupId } });
if (groupProjects && groupProjects.length > 0) {
// delete group all projects
let projectIds = [];
groupProjects.forEach(function(e) {
// 组合相应的项目信息
projectIds.push(e.projectId);
});
yield this.ctx.model.GroupProject.update({ status: 0 }, { where: { projectId: { in: projectIds } } });
yield this.ctx.model.Project.update({ status: 0 }, { where: { id: { in: projectIds } } });
// delete all project usre relation
yield this.ctx.model.UserProject.update({ status: 0 }, { where: { projectId: { in: projectIds } } });
// delete all interface
const interfaces = yield this.ctx.model.Interface.findAll({ where: { status: 1, projectId: { in: projectIds } } });
let interfaceIds = [];
interfaces.forEach(function(e) {
// 组合相应的项目信息
interfaceIds.push(e.id);
});
yield this.ctx.model.Interface.update({ status: 2 }, { where: { id: { in: interfaceIds } } });
// delete all interface usre relation
yield this.ctx.model.InterfaceUser.update({ status: 2 }, { where: { interfaceId: { in: interfaceIds } } });
}
// 发送消息删除组
let uids = yield this.ctx.model.query(`select user_id from tb_group_and_user where group_id=:groupId and status=1 and user_id !=:uid`, { type: 'SELECT', replacements: obj });
uids = uids.map((value) => value.user_id);
// 获取用户信息
const user = yield this.ctx.model.User.findOne({ where: { id: obj.uid } });
// 获取分组信息
const groupInfo = yield this.ctx.model.Group.findOne({ where: { id: obj.groupId } });
const title = nunjucks.renderString(noticeMessage.GROUP_DELETE.title, { userName: user.name, groupName: groupInfo.name });
const content = nunjucks.renderString(noticeMessage.GROUP_DELETE.content, { userName: user.name, groupName: groupInfo.name });
const notice = {};
notice.createUserId = obj.uid;
notice.content = content;
notice.title = title;
notice.readStatus = 1;
notice.type = noticeMessage.GROUP_DELETE.code;
notice.joinId = group.id;
yield this.ctx.service.base.sendNotice(uids, notice, app.config.appConfig.noticeTypeGroup);
yield t.commit();
} catch (e) {
yield t.rollback();
throw this.ctx.getError({ msg: '服务器异常' });
}
}
* add (group) {
// 限制分组名不能重复
const groupPro = yield this.ctx.model.Group.findAll({ where: { name: group.name, status: 1 } });
if (groupPro && groupPro.length > 0) {
throw this.ctx.getError({ msg: '该名称的分组已存在' });
}
// 校验用户创建组的最大数
const groupNums = yield this.ctx.model.GroupUser.count({ where: { userId: group.uid, status: 1 } });
const userGrade = yield this.ctx.model.UserGrade.findOne({ where: { userId: group.uid } });
if (userGrade && userGrade.groupNum <= groupNums) {
throw this.ctx.getError({ msg: '分组数目超限' });
}
// 插入分组数据
group.createUserId = group.uid;
group.status = 1;
group.type = 1;
group.projectCount = 0;
group.userCount = 1;
let t = yield app.model.transaction();
try {
// 创建组
let groupInfo = yield this.ctx.model.Group.create(group, { transaction: t });
// 创建组合用户的关系
yield this.ctx.model.GroupUser.create({
groupId: groupInfo.id,
userId: group.uid,
status: 1,
role: app.config.appConfig.roleOwner,
}, { transaction: t });
yield t.commit();
let groupPro = {};
groupPro.description = group.description;
groupPro.name = group.name;
groupPro.logo = group.logo;
groupPro.id = groupInfo.id;
groupPro.role = app.config.appConfig.roleOwner;
return groupPro;
} catch (e) {
yield t.rollback();
console.log(e);
throw this.ctx.getError({ msg: '服务器异常', error: e });
}
}
* list (obj) {
// 组合当前用户的信息
let wheres = {};
wheres.userId = obj.uid;
wheres.status = 1;
if (obj.type === 1) {
wheres.role = app.config.appConfig.roleOwner;
}
const groupUsers = yield this.ctx.model.GroupUser.findAll({ where: wheres });
if (!groupUsers) return null;
let groupIds = [];
let tempGroupUser = {};
groupUsers.forEach(function(e) {
// 组合相应的分组信息
groupIds.push(e.groupId);
tempGroupUser[e.groupId] = e;
});
const groups = yield this.ctx.model.Group.findAll({ attributes: [ 'id', 'name', 'description', 'logo', 'createTime' ], where: { status: 1, id: { in: groupIds } } });
groups.forEach(function(el) {
el.dataValues.role = tempGroupUser[el.id].role;
});
return groups;
}
* update (obj) {
// 检查用户与组的权限
let groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.uid, groupId: obj.id } });
if (!groupUser) throw this.ctx.getError({ msg: '无权限操作' });
if (groupUser.role !== app.config.appConfig.roleOwner || groupUser.status !== 1) {
throw this.ctx.getError({ msg: '无操作权限' });
}
// 检查分组是否存在
let group = yield this.ctx.model.Group.findOne({ where: { id: obj.id } });
if (!group) throw this.ctx.getError({ msg: '分组不存在' });
const groupPro = this.ctx.model.Group.findAll({ where: { name: obj.name, status: 1, id: { $ne: obj.id } } });
if (groupPro && groupPro.length > 0) {
throw this.ctx.getError({ msg: '该名称的分组已存在' });
}
// 修改分组信息
group = {};
group.description = obj.description;
group.name = obj.name;
group.logo = obj.logo;
console.log(group);
yield this.ctx.model.Group.update(group, { where: { id: obj.id } });
let groupInfoPro = {};
groupInfoPro.description = obj.description;
groupInfoPro.name = obj.name;
groupInfoPro.logo = obj.logo;
groupInfoPro.id = obj.id;
groupInfoPro.role = groupUser.role;
return groupInfoPro;
}
* info (obj) {
// 检查用户与组的权限
let groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.uid, groupId: obj.groupId } });
if (!groupUser) throw this.ctx.getError({ msg: '无权限查看' });
if (groupUser.status !== 1) {
throw this.ctx.getError({ msg: '无权限查看' });
}
const groupInfo = yield this.ctx.model.Group.findOne({ attributes: [ 'id', 'name', 'description', 'logo', 'createTime' ], where: { status: 1, id: obj.groupId } });
groupInfo.dataValues.role = groupUser.role;
return groupInfo;
}
}
return Group;
};
================================================
FILE: app/service/groupUser.js
================================================
const moment = require('moment');
const nunjucks = require('nunjucks');
const noticeMessage = require('../extend/noticeMessage');
module.exports = app => {
class GroupUser extends app.Service {
* add (obj) {
// 校验用户权限
if (obj.role != app.config.appConfig.roleOwner && obj.role != app.config.appConfig.roleMaster && obj.role != app.config.appConfig.roleDev) {
throw this.ctx.getError({ msg: '参数有误' });
}
let groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.uid, groupId: obj.groupId, status: 1 } });
if (!groupUser || (groupUser.role != app.config.appConfig.roleOwner)) {
throw this.ctx.getError({ msg: '无权限操作' });
}
const userIds = obj.userId;
for (let i = 0; i < userIds.length; i++) {
const userId = userIds[i];
if (userId == obj.uid) continue;
// 查询是否已插入
groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId, groupId: obj.groupId } });
if (!groupUser) {
// 插入,数量加1
let t = yield app.model.transaction();
try {
yield this.ctx.model.GroupUser.create({ userId, groupId: obj.groupId, role: obj.role, status: 1 }, { transaction: t });
yield this.ctx.model.query(`update tb_group set user_count=user_count+1,update_time='${moment().utcOffset(0).format('YYYY-MM-DD HH:mm:ss')}' where id=:groupId and user_count+1>0 `, { transaction: t, replacements: obj });
// 用户加入成功,发送消息通知
let uids = yield this.ctx.model.query(`select user_id from tb_group_and_user where group_id=:groupId and user_id!=:uid and status=1`, { type: 'SELECT', replacements: obj });
uids = uids.map((value) => value.user_id);
// 获取用户信息
const user = yield this.ctx.model.User.findOne({ where: { id: userId } });
// 获取分组信息
const group = yield this.ctx.model.Group.findOne({ where: { id: obj.groupId } });
const title = nunjucks.renderString(noticeMessage.GROUP_ADD_USER.title, { userName: user.name, groupName: group.name });
const content = nunjucks.renderString(noticeMessage.GROUP_ADD_USER.content, { userName: user.name, groupName: group.name });
const notice = {};
notice.createUserId = obj.uid;
notice.content = content;
notice.title = title;
notice.readStatus = 1;
notice.type = noticeMessage.GROUP_ADD_USER.code;
notice.joinId = group.id;
yield this.ctx.service.base.sendNotice(uids, notice, app.config.appConfig.noticeTypeGroup);
yield t.commit();
} catch (e) {
yield t.rollback();
throw this.ctx.getError({ msg: '服务器异常', error: e });
}
} else {
// 修改信息
yield this.ctx.model.GroupUser.update({ role: obj.role, status: 1 }, { where: { userId, groupId: obj.groupId } });
// 发送消息修改项目组内的权限
let uids = yield this.ctx.model.query(`select user_id from tb_group_and_user where group_id=:groupId and user_id!=:uid and status=1`, { type: 'SELECT', replacements: obj });
uids = uids.map((value) => value.user_id);
// 获取用户信息
const user = yield this.ctx.model.User.findOne({ where: { id: userId } });
// 获取分组信息
const group = yield this.ctx.model.Group.findOne({ where: { id: obj.groupId } });
const title = nunjucks.renderString(noticeMessage.GROUP_ROLE_USER.title, { userName: user.name, groupName: group.name, role: this.ctx.helper.tools.getGroupRole(obj.role - 0) });
const content = nunjucks.renderString(noticeMessage.GROUP_ROLE_USER.content, { userName: user.name, groupName: group.name, role: this.ctx.helper.tools.getGroupRole(obj.role - 0) });
const notice = {};
notice.createUserId = obj.uid;
notice.content = content;
notice.title = title;
notice.readStatus = 1;
notice.type = noticeMessage.GROUP_ROLE_USER.code;
notice.joinId = group.id;
yield this.ctx.service.base.sendNotice(uids, notice, app.config.appConfig.noticeTypeGroup);
}
}
}
* delete (obj) {
// 查询是否已插入
const groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.userId, groupId: obj.groupId } });
if (!groupUser) {
throw this.ctx.getError({ msg: '当前用户不存在' });
} else {
// 校验用户权限
const opGroupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.uid, groupId: obj.groupId, status: 1 } });
if ((!opGroupUser || (opGroupUser.role != app.config.appConfig.roleOwner)) && obj.uid != groupUser.userId) {
throw this.ctx.getError({ msg: '无权限操作' });
}
if (groupUser.role == app.config.appConfig.roleOwner) {
// 查询是否存在其他的owner
const userOwners = yield this.ctx.model.GroupUser.count({ where: { groupId: obj.groupId, status: 1, role: app.config.appConfig.roleOwner } });
if (userOwners < 2) throw this.ctx.getError({ msg: '当前只有一位owner用户无法删除' });
}
// 存在删除信息
let t = yield app.model.transaction();
try {
yield this.ctx.model.GroupUser.update({ status: 0 }, { where: { userId: obj.userId, groupId: obj.groupId } });
yield this.ctx.model.query(`update tb_group set user_count=user_count-1,update_time='${moment().utcOffset(0).format('YYYY-MM-DD HH:mm:ss')}' where id=:groupId and user_count-1>0 `, { transaction: t, replacements: obj });
// 发送消息删除项目组成员
let uids = yield this.ctx.model.query(`select user_id from tb_group_and_user where group_id=:groupId and status=1 and user_id!=:uid `, { type: 'SELECT', replacements: obj });
uids = uids.map((value) => value.user_id);
// 获取用户信息
const user = yield this.ctx.model.User.findOne({ where: { id: obj.userId } });
// 获取分组信息
const group = yield this.ctx.model.Group.findOne({ where: { id: obj.groupId } });
const title = nunjucks.renderString(noticeMessage.GROUP_DELETE_USER.title, { userName: user.name, groupName: group.name });
const content = nunjucks.renderString(noticeMessage.GROUP_DELETE_USER.content, { userName: user.name, groupName: group.name });
const notice = {};
notice.createUserId = obj.uid;
notice.content = content;
notice.title = title;
notice.readStatus = 1;
notice.type = noticeMessage.GROUP_DELETE_USER.code;
notice.joinId = group.id;
yield this.ctx.service.base.sendNotice(uids, notice, app.config.appConfig.noticeTypeGroup);
yield t.commit();
} catch (e) {
yield t.rollback();
throw this.ctx.getError({ msg: '服务器异常', error: e });
}
}
}
* list (obj) {
// 组合当前用户的信息
let wheres = {};
wheres.groupId = obj.groupId;
wheres.status = 1;
const groupUsers = yield this.ctx.model.GroupUser.findAll({ where: wheres });
if (!groupUsers) return null;
let userIds = [];
let tempGroupUser = {};
groupUsers.forEach(function(e) {
// 组合相应的分组信息
userIds.push(e.userId);
tempGroupUser[e.userId] = e;
});
const users = yield this.ctx.model.User.findAll({ attributes: [ 'id', 'name', 'photo' ], where: { id: { in: userIds } } });
users.forEach(function(el) {
el.dataValues.role = tempGroupUser[el.id].role;
el.dataValues.userId = el.id;
delete el.dataValues.id;
});
return users;
}
* update (obj) {
// 校验用户权限
if (obj.role != app.config.appConfig.roleOwner && obj.role != app.config.appConfig.roleMaster && obj.role != app.config.appConfig.roleDev) {
throw this.ctx.getError({ msg: '参数有误' });
}
// 查询是否已插入
const groupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.userId, groupId: obj.groupId, status: 1 } });
if (!groupUser) {
// 不存在
throw this.ctx.getError({ msg: '当前用户不存在' });
} else {
// 存在则修改
// 校验用户权限
const opGroupUser = yield this.ctx.model.GroupUser.findOne({ where: { userId: obj.uid, groupId: obj.groupId, status: 1 } });
if (!opGroupUser || (opGroupUser.role != app.config.appConfig.roleOwner)) {
throw this.ctx.getError({ msg: '无权限操作' });
}
if (groupUser.role == app.config.appConfig.roleOwner) {
if (obj.role != app.config.appConfig.roleOwner) {
// 查询是否存在其他的owner
const userOwners = yield this.ctx.model.GroupUser.count({ where: { groupId: obj.groupId, status: 1, role: app.config.appConfig.roleOwner } });
if (userOwners < 2) throw this.ctx.getError({ msg: '当前只有一位owner用户无法修改为其他权限' });
}
}
yield this.ctx.model.GroupUser.update({ role: obj.role, status: 1 }, { where: { userId: obj.userId, groupId: obj.groupId } });
// 发送消息修改项目组内的权限
let uids = yield this.ctx.model.query(`select user_id from tb_group_and_user where group_id=:groupId and status=1`, { type: 'SELECT', replacements: obj });
uids = uids.map((value) => value.user_id);
// 获取用户信息
const user = yield this.ctx.model.User.findOne({ where: { id: obj.userId } });
// 获取分组信息
const group = yield this.ctx.model.Group.findOne({ where: { id: obj.groupId } });
const title = nunjucks.renderString(noticeMessage.GROUP_ROLE_USER.title, { userName: user.name, groupName: group.name, role: this.ctx.helper.tools.getGroupRole(obj.role - 0) });
const content = nunjucks.renderString(noticeMessage.GROUP_ROLE_USER.content, { userName: user.name, groupName: group.name, role: this.ctx.helper.tools.getGroupRole(obj.role - 0) });
const notice = {};
notice.createUserId = obj.uid;
notice.content = content;
notice.title = title;
notice.readStatus = 1;
notice.type = noticeMessage.GROUP_ROLE_USER.code;
notice.joinId = group.id;
yield this.ctx.service.base.sendNotice(uids, notice, app.config.appConfig.noticeTypeGroup);
}
}
}
return GroupUser;
};
================================================
FILE: app/service/kaptcha.js
================================================
const captchapng = require('captchapng');
module.exports = app => {
class Kaptcha extends app.Service {
* getCaptcha(key) {
const p = new captchapng(80, 30, key); // width,height,numeric captcha
p.color(0, 0, 0, 0); // First color: background (red, green, blue, alpha)
p.color(80, 80, 80, 255); // Second color: paint (red, green, blue, alpha)
const img = p.getBase64();
return img
}
}
return Kaptcha;
};
================================================
FILE: app/service/mock.js
================================================
module.exports = app => {
class Mock extends app.Service {
* info (obj) {
// 查询项目
// 查询项目对应接口信息
// 生成mock数据
console.log('---------')
let shortPath = obj.path.replace(/^\//, '').replace(/\/$/, '')
console.log(shortPath)
const interfaceInfos = yield this.ctx.model.HistoryInterface.findAll({
where: {
projectId: obj.id,
path: { $in: [ shortPath, '/' + shortPath, shortPath + '/', '/' + shortPath + '/' ] },
status: 1
}
});
let interfaceInfo = null
interfaceInfos.forEach((value) => {
if (value.type == obj.type) {
interfaceInfo = value
}
})
if (interfaceInfo) {
if(interfaceInfo.mockResponse){
return JSON.parse(interfaceInfo.mockResponse)
}
if (!interfaceInfo.response) throw this.ctx.getError({ msg: '无响应值' });
return this.ctx.helper.tools.jsonToMock(JSON.parse(interfaceInfo.response));
} else {
if (interfaceInfos.length == 0) {
throw this.ctx.getError({ msg: '接口不存在' });
} else {
throw this.ctx.getError({ msg: '接口访问类型不匹配,尝试用 ' + interfaceInfos[ 0 ].type });
}
}
}
* add (obj) {
const mockInfo = yield this.ctx.model.Mock.findOne({ where: { interfaceId: obj.apiId, type: obj.type } });
if (mockInfo) {
// 修改
yield this.ctx.model.Mock.update({ mockRequest: obj.mockRequest }, {
where: {
interfaceId: obj.apiId,
type: obj.type
}
});
} else {
// 添加
yield this.ctx.model.Mock.create({ mockRequest: obj.mockRequest, interfaceId: obj.apiId, type: obj.type });
}
}
}
return Mock;
};
================================================
FILE: app/service/notice.js
================================================
const moment = require('moment');
module.exports = app => {
class ProjectUser extends app.Service {
* pullMessage (obj) {
const data = yield this.ctx.model.query(`select id,create_user_id as userId,title,content,type,join_id as joinId,read_status as readStatus,create_time as time from tb_user_notice where user_id=${obj.uid} and read_status=${obj.readStatus} order by id desc limit 20`, { type: 'SELECT' });
if (data && data.length > 0) {
for (let i = 0; i < data.length; i++) {
const temp = data[ i ];
const user = yield this.ctx.model.User.findOne({ where: { id: temp.userId } });
temp.userName = user.name;
temp.userPhoto = user.photo;
}
}
return data;
}
* listMessage (obj) {
let sql = 'select title,id,create_user_id as userId,content,read_status as readStatus,type,join_id as joinId,create_time as time from tb_user_notice where ';
if (obj.status == 1) {
sql = sql + ' read_status = 1 and ';
}
if (obj.status == 2) {
sql = sql + ' read_status = 1 and ';
}
if (obj.startId > 0) {
sql = sql + ' id < ' + obj.start + ' and ';
}
if (obj.type == 1) {
sql = sql + ' type <= 200 and ';
}
if (obj.type == 2) {
sql = sql + ' type > 200 and type <= 300 and ';
}
if (obj.type == 3) {
sql = sql + ' type > 300 and type <= 400 and ';
}
sql = sql + ' user_id=' + obj.uid + ' order by id desc limit ' + obj.count;
const data = yield this.ctx.model.query(sql, { type: 'SELECT' });
if (data && data.length > 0) {
for (let i = 0; i < data.length; i++) {
const temp = data[ i ];
const user = yield this.ctx.model.User.findOne({ where: { id: temp.userId } });
temp.userName = user.name;
temp.userPhoto = user.photo;
}
}
return data;
}
* changeReadStatus (obj) {
yield this.ctx.model.query(` update tb_user_notice set read_status=2,update_time='${moment().utcOffset(0).format('YYYY-MM-DD HH:mm:ss')}' where id=${obj.id} `);
}
* getMessageInfo (obj) {
const data = yield this.ctx.model.query(`select id,create_user_id as userId,title,content,type,join_id as joinId,read_status as readStatus,create_time as time from tb_user_notice where id=${obj.id}`, { type: 'SELECT' })
return data[0];
}
* getMessageNums (obj) {
const data = yield this.ctx.model.query(`select count(id) as total,IFNULL(sum(case when type <= 200 then 1 else 0 end),0) as groups,IFNULL(sum(case when type > 200 and type <= 300 then 1 else 0 end),0) as project,IFNULL(sum(case when type > 300 and type <= 400 then 1 else 0 end),0) as api from tb_user_notice where user_id=${obj.uid} and read_status = 1 `, { type: 'SELECT' })
return data[0];
}
* getNoticeType (obj) {
const data = yield this.ctx.model.query(`select type,message_notice as messageNotice,email_notice as emailNotice from tb_user_notice_type where user_id=${obj.uid}`, { type: 'SELECT' })
return data[0];
}
* updateNoticeType (obj) {
let sql = ' update tb_user_notice_type set ';
if (obj.noticeType == 'message') {
sql = sql + 'message_notice = ' + obj.noticeType + ' , update_time=' + moment().utcOffset(0).format('YYYY-MM-DD HH:mm:ss');
} else if (obj.noticeType == 'email') {
sql = sql + 'email_notice = ' + obj.noticeType + ' , update_time=' + moment().utcOffset(0).format('YYYY-MM-DD HH:mm:ss');
}
sql = sql + 'where user_id=' + obj.uid + ' and type=' + obj.categoryType;
yield this.ctx.model.query(sql);
}
}
return ProjectUser;
};
================================================
FILE: app/service/pages.js
================================================
/**
* Created with WebStorm.
* User: kevan
* Email:258137678@qq.com
* Date: 2017/5/3
* Time: 上午9:30
* To change this template use File | Settings | File Templates.
*/
var md5 = require('md5')
'use strict'
module.exports = app => {
class Pages extends app.Service {
* save (query) {
try {
var _item = {
name: query.name,
projectId: query.projectId,
draft: query.content || '',
desc: query.desc,
image: query.image,
key: query.key,
type: query.type || 0,
fork: query.fork,
visibilitylevel: query.visibilitylevel || 0 // 默认公共页面
}
if (query.id) {
// 编辑
// 参数验证,1判断是否存在该id,2是否重名
var pageInfo = yield this.ctx.model.Pages.findOne({
where: {
status: 1,
id: query.id
}
})
var keyPage = yield this.ctx.model.Pages.findOne({
where: {
key: _item.key,
status: { $ne: 0 }
}
})
if (keyPage && keyPage.id != query.id) {
throw this.ctx.getError({ msg: `新输入的页面的key:${query.key}已经存在,重新输入` })
}
if (pageInfo) {
yield this.ctx.model.Pages.update(_item, { where: { id: query.id } })
} else {
throw this.ctx.getError({ msg: `不存在id为${query.id}的页面信息` })
}
} else {
// 新建页面 fork使用数为0
_item.fork = 0
// 页面不存在。生成页面信息
_item.status = 1
// 生成唯一的页面 key
_item.key = this.ctx.helper.tools.md5(_item.projectId + '_' + _item.name + (new Date().getTime()))
// 截取10位,然后随机做大小写切换
_item.key = _item.key.substring(0, 10).replace(/./gi, function (a) {
if (parseInt(Math.random() * 10) % 3 == 0) {
return a.toUpperCase()
} else {
return a.toLowerCase()
}
})
// 参数验证 是否重名
let oldPage = yield this.ctx.model.Pages.findOne({
where: {
projectId: query.projectId,
key: _item.key,
status: { $ne: 0 }
}
})
if (oldPage) throw this.ctx.getError({ msg: `已经存在key为:${_item.key}的页面信息,重新提交` })
if (query.publishNow) {
_item.content = _item.draft
_item.draft = ''
}
var newItem = yield this.ctx.model.Pages.create(_item)
query.id = newItem.id
}
// 移除标签和资源的关系
yield this.ctx.model.query(` DELETE FROM tb_res_tags_rel WHERE rid=:id and cid=1; `, {
type: 'BULKDELETE', replacements: query
});
// 对标签关系进行添加
query.tags = query.tags || []
for (let tag of query.tags) {
yield this.ctx.service.tags.useone({
id: tag.id
})
// 绑定标签和资源的关系
yield this.ctx.model.ResTagsRel.create({
rid: query.id,
tid: tag.id,
cid: 1
})
}
return { id: query.id }
} catch (e) {
console.log(e)
throw this.ctx.getError({ msg: e.msg || `${query.id ? '更新失败' : '新增失败'}`, error: e })
}
}
* list (query) {
console.log('service start --list', query, query.status == undefined)
if (!query.projectId) return []
// 检查用户与项目的权限
const role = yield this.ctx.service.base.getUserRole(query.uid, query.projectId);
if (role == 100) {
throw this.ctx.getError({ msg: '无操作权限' });
}
let projectIds = (query.projectId instanceof Array ? query.projectId : [query.projectId]).filter(id => id && id > 0)
if (!projectIds.length) return []
let _where = {
projectId: {
'$or': projectIds
},
status: (query.status == undefined) ? { $ne: 0 } : query.status
}
let list = yield this.ctx.model.Pages.findAll({
where: _where,
order: 'create_time DESC'
})
if (list && list.length) {
list = list.map(item => {
var it = item.dataValues || {}
if (it.id) {
try {
let content = it.draft || it.content
it.psdList = JSON.parse(content).psdList || []
} catch (error) {
it.psdList = []
}
it.isPublish = !it.draft
delete it.content
delete it.draft
}
return it
})
}
return list
}
* publiclist (query) {
console.log('service start --list', query, query.status == undefined)
var sqlQuery = ''
if (query.name) {
query.name = `%${query.name}%`
sqlQuery += ` and pages.name like :name`;
}
if (query.tags && query.tags.length != 0) {
sqlQuery += ` and t.id in (${query.tags.map(val => val.id).join(',')})`
// query.tagsStr = query.tags.map(val => val.id).join(',')
// sqlQuery += ` and t.id in (:tagsStr)`
}
let list = yield this.ctx.model.query(`SELECT
distinct pages.id,
pages.key,
pages.name,
pages.fork,
pages.image,
pages.desc,
pages.draft,
pages.type,
pages.project_id AS projectId,
pages.status,
pages.visibilitylevel,
pages.update_time AS updateTime,
pages.create_time AS createTime
FROM tb_pages AS pages
left join tb_res_tags_rel rel on pages.id=rel.rid
left join tb_tags t on rel.tid=t.id
WHERE ${!query.featured ? '' : 'pages.featured = 1 AND'} pages.visibilitylevel = 1 AND pages.status = 1 ${sqlQuery}
`,{type:'SELECT', replacements: query})
// if (list && list.length) {
// list = list.map(item => {
// var it = item.dataValues || {}
// if (it.id) {
// it.isPublish = !it.draft
// delete it.draft
// }
// return it
// })
// }
// console.log(list)
return list || []
}
/**
* 查询页面内容
* 1:公共页面直接提供查询
* 2:非公共页面只有当改页面所在的项目属于该用户才可见,否则提示权限不够
* @param query
* @return {*}
*/
* info (query) {
let page = yield this.ctx.model.Pages.findOne({
where: {
id: query.id
}
})
// 查询当前用户的角色
if (page) {
const role = yield this.ctx.service.base.getUserRole(query.uid, page.projectId)
page.dataValues.role = role
// 查询标签
let tags = yield this.ctx.model.query(`select
t.id,
t.name
from tb_res_tags_rel rel
left join tb_tags t on t.id=rel.tid
where rel.rid =:id and cid=1
`, {
type: 'SELECT', replacements: query
})
page.dataValues.tags = tags
}
return page
}
* detail (query) {
let info = (yield this.ctx.model.Pages.findOne({
where: {
key: query.pageKey,
status: { $ne: 0 }
}
})) || {}
info = info.dataValues || {}
if (info.id) {
if (['edit', 'preview', 'copy'].includes(query.scene)) {
info.content = info.draft || info.content
}
gitextract_t7mniftx/
├── .dockerignore
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .gitmodules
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app/
│ ├── controller/
│ │ ├── category.js
│ │ ├── component.js
│ │ ├── group.js
│ │ ├── groupUser.js
│ │ ├── home.js
│ │ ├── interface.js
│ │ ├── kaptcha.js
│ │ ├── mock.js
│ │ ├── notice.js
│ │ ├── ossupload.js
│ │ ├── pages.js
│ │ ├── project.js
│ │ ├── projectUser.js
│ │ ├── proxy.js
│ │ ├── resources.js
│ │ ├── statistics.js
│ │ ├── tags.js
│ │ ├── template.js
│ │ ├── test.js
│ │ └── user.js
│ ├── extend/
│ │ ├── application.js
│ │ ├── context.js
│ │ ├── dingd.js
│ │ ├── email/
│ │ │ └── tpl/
│ │ │ ├── active.html
│ │ │ ├── notice.html
│ │ │ └── password.html
│ │ ├── helper.js
│ │ ├── noticeMessage.js
│ │ ├── token.js
│ │ └── tools.js
│ ├── middleware/
│ │ ├── login_handler.js
│ │ └── response_handler.js
│ ├── model/
│ │ ├── category.js
│ │ ├── component.js
│ │ ├── component_use.js
│ │ ├── group.js
│ │ ├── group_project.js
│ │ ├── group_user.js
│ │ ├── history_interface.js
│ │ ├── interface.js
│ │ ├── interface_draf.js
│ │ ├── interface_log.js
│ │ ├── interface_user.js
│ │ ├── pages.js
│ │ ├── pages_history.js
│ │ ├── project.js
│ │ ├── project_data.js
│ │ ├── resTagsRel.js
│ │ ├── resources.js
│ │ ├── tag.js
│ │ ├── tag_interface.js
│ │ ├── tags.js
│ │ ├── template.js
│ │ ├── user.js
│ │ ├── user_grade.js
│ │ ├── user_login.js
│ │ ├── user_login_log.js
│ │ ├── user_notice.js
│ │ ├── user_notice_type.js
│ │ ├── user_project.js
│ │ └── valid_code.js
│ ├── router.js
│ ├── schedule/
│ │ ├── es_delete.js
│ │ ├── es_report.js
│ │ ├── pu_uv_ratio.js
│ │ └── pvcount.js
│ └── service/
│ ├── base.js
│ ├── category.js
│ ├── component.js
│ ├── group.js
│ ├── groupUser.js
│ ├── kaptcha.js
│ ├── mock.js
│ ├── notice.js
│ ├── pages.js
│ ├── project.js
│ ├── projectUser.js
│ ├── resources.js
│ ├── statistics.js
│ ├── tags.js
│ ├── template.js
│ └── user.js
├── app.js
├── config/
│ ├── config.app.js
│ ├── config.default.js
│ ├── config.dev.js
│ ├── config.production.js
│ └── plugin.js
├── config-parser.js
├── docker-entrypoint.sh
├── index.js
├── package.json
├── process.json
├── sql/
│ └── init.sql
└── sub-build/
└── .gitkeep
SYMBOL INDEX (370 symbols across 48 files)
FILE: app/controller/category.js
class CategoryController (line 4) | class CategoryController extends app.Controller {
method list (line 5) | * list () {
method save (line 15) | * save () {
method delete (line 46) | * delete () {
FILE: app/controller/component.js
class ComponentController (line 4) | class ComponentController extends app.Controller {
method find (line 5) | * find () {
method searchByName (line 28) | * searchByName () {
method searchAllStatusByName (line 42) | * searchAllStatusByName () {
method info (line 57) | * info () {
method updata (line 72) | * updata () {
method save (line 92) | * save () {
method useone (line 127) | * useone () {
method delete (line 143) | * delete () {
method import (line 159) | async import () {
FILE: app/controller/group.js
class controller (line 4) | class controller extends app.Controller {
method add (line 6) | * add () {
method delete (line 24) | * delete () {
method list (line 39) | * list () {
method update (line 57) | * update () {
method info (line 71) | * info () {
FILE: app/controller/groupUser.js
class controller (line 4) | class controller extends app.Controller {
method add (line 6) | * add () {
method delete (line 23) | * delete () {
method list (line 39) | * list () {
method update (line 56) | * update () {
FILE: app/controller/home.js
class HomeController (line 4) | class HomeController extends app.Controller {
method index (line 5) | * index() {
FILE: app/controller/interface.js
class HomeController (line 4) | class HomeController extends app.Controller {
method add (line 5) | * add () {
method delete (line 23) | * delete () {
method update (line 37) | * update () {
method updataInterfaceMock (line 56) | * updataInterfaceMock () {
method apiUpload (line 75) | * apiUpload () {
method getApiByProjectId (line 93) | * getApiByProjectId () {
method batchUpdate (line 108) | * batchUpdate () {
method getDetailDrafById (line 123) | * getDetailDrafById () {
method draftList (line 137) | * draftList () {
method deprecated (line 155) | * deprecated (obj) {
method list (line 167) | * list () {
method getInterfaceInfo (line 181) | * getInterfaceInfo () {
method addApiTag (line 197) | * addApiTag () {
method requestRelease (line 213) | * requestRelease () {
method getApproveList (line 227) | * getApproveList () {
method deleteApiTag (line 236) | * deleteApiTag () {
method auditApi (line 251) | * auditApi () {
method addTag (line 266) | * addTag () {
method updateTag (line 282) | * updateTag () {
method deleteTag (line 298) | * deleteTag () {
method getTags (line 312) | * getTags () {
method getPersons (line 327) | * getPersons () {
method addPerson (line 342) | * addPerson () {
method deletePerson (line 357) | * deletePerson () {
method addData (line 372) | * addData () {
method updateData (line 388) | * updateData () {
method deleteData (line 405) | * deleteData () {
method getDatas (line 419) | * getDatas () {
method getHistoryList (line 434) | * getHistoryList () {
method updateHistoryStatus (line 448) | * updateHistoryStatus () {
FILE: app/controller/kaptcha.js
class HomeController (line 3) | class HomeController extends app.Controller {
method init (line 4) | * init (ctx) {
FILE: app/controller/mock.js
class controller (line 4) | class controller extends app.Controller {
method infoOption (line 5) | * infoOption () {
method info (line 12) | * info () {
method insertOrUpdate (line 27) | * insertOrUpdate () {
FILE: app/controller/notice.js
class controller (line 4) | class controller extends app.Controller {
method pullMessage (line 7) | * pullMessage () {
method listMessage (line 17) | * listMessage () {
method changeReadStatus (line 35) | * changeReadStatus () {
method getMessageInfo (line 42) | * getMessageInfo () {
method getMessageNums (line 49) | * getMessageNums () {
method getNoticeType (line 57) | * getNoticeType () {
method updateNoticeType (line 65) | * updateNoticeType () {
FILE: app/controller/ossupload.js
constant OSS (line 1) | const OSS = require('ali-oss')
class OssUploadController (line 14) | class OssUploadController extends app.Controller {
method uploadByUrls (line 15) | async uploadByUrls () {
method uploadFile (line 77) | async uploadFile () {
FILE: app/controller/pages.js
constant PSD (line 3) | const PSD = require('psd')
constant OSS (line 9) | const OSS = require('ali-oss')
class PagesController (line 12) | class PagesController extends app.Controller {
method pv (line 17) | * pv() {
method pvuv (line 32) | * pvuv() {
method save (line 97) | * save() {
method list (line 126) | * list() {
method publiclist (line 148) | * publiclist() {
method info (line 157) | * info() {
method detail (line 174) | async detail() {
method changeStatus (line 234) | *changeStatus() {
method setHomePage (line 255) | * setHomePage() {
method delete (line 271) | * delete() {
method publish (line 289) | * publish() {
method count (line 326) | * count() {
method history (line 336) | * history() {
method getNameBykeys (line 353) | * getNameBykeys() {
method historyDelete (line 369) | * historyDelete() {
method historyPublish (line 385) | * historyPublish() {
method historyToDraft (line 426) | * historyToDraft() {
method updateFork (line 466) | * updateFork() {
method psdToPage (line 480) | async psdToPage() {
method upload (line 565) | async upload(filedata, fileName) {
method featuringPages (line 580) | async featuringPages () {
method updateFeatured (line 592) | async updateFeatured () {
FILE: app/controller/project.js
class controller (line 4) | class controller extends app.Controller {
method add (line 6) | * add () {
method delete (line 20) | * delete () {
method list (line 35) | * list () {
method update (line 50) | * update () {
method info (line 65) | * info () {
method groupProject (line 78) | * groupProject () {
method favorateProject (line 94) | * favorateProject () {
method cancelFavorateProject (line 107) | * cancelFavorateProject () {
method getFavorateProject (line 120) | * getFavorateProject () {
FILE: app/controller/projectUser.js
class controller (line 4) | class controller extends app.Controller {
method add (line 6) | * add () {
method delete (line 23) | * delete () {
method list (line 39) | * list () {
method update (line 56) | * update () {
FILE: app/controller/proxy.js
class ProxyController (line 9) | class ProxyController extends Controller {
method imgCorsProxy (line 10) | async imgCorsProxy () {
method transparentProxy (line 42) | async transparentProxy () {
method word2html (line 81) | async word2html () {
FILE: app/controller/resources.js
class ResourcesController (line 4) | class ResourcesController extends app.Controller {
method info (line 5) | * info () {
method list (line 20) | * list () {
method save (line 54) | * save () {
method delete (line 90) | * delete () {
method addUseCount (line 106) | * addUseCount () {
FILE: app/controller/statistics.js
class StatisticsController (line 3) | class StatisticsController extends Controller {
method report (line 5) | async report () {
method getPUV (line 48) | async getPUV () {
method getPages (line 78) | async getPages () {
method getUtms (line 100) | async getUtms () {
method getViewTime (line 126) | async getViewTime () {
method getActions (line 158) | async getActions () {
method actionTrack (line 184) | async actionTrack () {
method getTodayOutline (line 214) | async getTodayOutline () {
method getProjectReport (line 227) | async getProjectReport () {
method getGroupReport (line 254) | async getGroupReport () {
FILE: app/controller/tags.js
class TagsController (line 3) | class TagsController extends app.Controller {
method list (line 4) | * list () {
method add (line 26) | * add () {
method useone (line 50) | * useone () {
FILE: app/controller/template.js
class TemplateController (line 4) | class TemplateController extends app.Controller {
method list (line 5) | * list () {
method detail (line 34) | * detail () {
method save (line 48) | * save () {
method delete (line 90) | * delete () {
FILE: app/controller/test.js
class HomeController (line 4) | class HomeController extends app.Controller {
method index (line 5) | * index() {
FILE: app/controller/user.js
function getAdminUrl (line 8) | function getAdminUrl (ctx, path) {
class HomeController (line 25) | class HomeController extends app.Controller {
method login (line 26) | * login () {
method oauthCode (line 45) | * oauthCode () {
method oauthLogin (line 70) | async oauthLogin () {
method register (line 122) | * register () {
method activeEmail (line 140) | * activeEmail () {
method sendActiveEmail (line 165) | * sendActiveEmail () {
method sendEmail (line 175) | * sendEmail () {
method info (line 185) | * info () {
method logout (line 207) | * logout () {
method edit (line 213) | * edit () {
method updatePassword (line 227) | * updatePassword () {
method newUpdatePassword (line 241) | * newUpdatePassword () {
method forgetPassword (line 261) | * forgetPassword () {
method search (line 272) | * search () {
method getTocken (line 284) | * getTocken () {
FILE: app/extend/application.js
constant REDIS (line 3) | const REDIS = Symbol('REDIS#INSTANCE')
function initMailSender (line 11) | function initMailSender (config) {
function strZip (line 23) | function strZip (str) {
function strUnzip (line 33) | function strUnzip (buf) {
function initRedis (line 42) | function initRedis (config, errorCallback) {
method redis (line 62) | get redis () {
method getCache (line 70) | async getCache (key) {
method setCache (line 82) | async setCache (key, val) {
method delCache (line 93) | async delCache (key) {
method addPV (line 103) | addPV (from) {
method getPV (line 108) | getPV () {
method DDNotify (line 114) | DDNotify (msg, title, at) {
FILE: app/extend/context.js
class ERROR (line 1) | class ERROR extends Error {
method constructor (line 2) | constructor ({ code, status, msg, error }) {
method getError (line 19) | getError ({ code, status, msg, e }) {
FILE: app/extend/dingd.js
method sendNotice (line 6) | * sendNotice (data) {
FILE: app/extend/token.js
method decryptToken (line 5) | decryptToken(token) {
method getToken (line 17) | getToken() {
method setToken (line 30) | setToken(uid, password) {
method parseCookie (line 38) | parseCookie(cookie) {
FILE: app/extend/tools.js
method encrypt (line 25) | encrypt (str, key) {
method decrypt (line 38) | decrypt (str, key) {
method md5 (line 51) | md5 (str) {
method isEmpty (line 61) | isEmpty (str) {
method isEmptyObject (line 67) | isEmptyObject (obj) {
method getNewVersion (line 75) | getNewVersion (str) {
method jsonToMock (line 89) | jsonToMock (data) {
method getGroupRole (line 130) | getGroupRole (role) {
method getSSOInfo (line 147) | getSSOInfo (session) {
method getSSOHeader (line 209) | getSSOHeader (appid, sec) {
method getProjectRole (line 251) | getProjectRole (role) {
method nodeTreeScriptTransform (line 276) | nodeTreeScriptTransform (tree, child = 'child') {
method timeFormat (line 329) | timeFormat (time, format = 'yyyy/mm/dd') {
method ossConfigValid (line 349) | ossConfigValid (config = {}) {
method isSafeUrl (line 353) | isSafeUrl (url) {
FILE: app/schedule/es_delete.js
method task (line 10) | async task(ctx) {
FILE: app/schedule/es_report.js
method task (line 6) | async task(ctx) {
FILE: app/schedule/pu_uv_ratio.js
constant PUV_RATIO_THRESHOLD (line 1) | const PUV_RATIO_THRESHOLD = 1.6 // 小时 pv/uv 平均1.34 最大值3.72 取1.6为告警阈值
method task (line 14) | async task(ctx) {
FILE: app/schedule/pvcount.js
method task (line 12) | async task(ctx) {
FILE: app/service/base.js
class BaseService (line 2) | class BaseService extends app.Service {
method getUserRole (line 3) | * getUserRole (uid, projectId) {
method sendNotice (line 19) | * sendNotice (uids, notice, noticeType) {
FILE: app/service/category.js
class Category (line 12) | class Category extends app.Service {
method list (line 13) | * list (query) {
method save (line 26) | * save (query) {
method delete (line 70) | * delete (id) {
FILE: app/service/component.js
constant OSS (line 2) | const OSS = require('ali-oss')
function setConstVar (line 6) | function setConstVar (str, {
function normalizeName (line 16) | function normalizeName (name = '') {
function genManifest (line 20) | async function genManifest (pkgfile, uid) {
function downloadZip (line 43) | async function downloadZip (id, token) {
function ossUpload (line 63) | async function ossUpload (files = [], manifest) {
class Component (line 94) | class Component extends app.Service {
method info (line 95) | * info (obj) {
method list (line 118) | * list (query) {
method updata (line 183) | * updata (query) {
method save (line 233) | * save (query) {
method useone (line 333) | * useone (query) {
method delete (line 399) | * delete (obj) {
method import (line 423) | async import ({id, token}, userId) {
FILE: app/service/group.js
class Group (line 4) | class Group extends app.Service {
method delete (line 5) | * delete (obj) {
method add (line 73) | * add (group) {
method list (line 117) | * list (obj) {
method update (line 141) | * update (obj) {
method info (line 171) | * info (obj) {
FILE: app/service/groupUser.js
class GroupUser (line 5) | class GroupUser extends app.Service {
method add (line 7) | * add (obj) {
method delete (line 76) | * delete (obj) {
method list (line 122) | * list (obj) {
method update (line 145) | * update (obj) {
FILE: app/service/kaptcha.js
class Kaptcha (line 5) | class Kaptcha extends app.Service {
method getCaptcha (line 6) | * getCaptcha(key) {
FILE: app/service/mock.js
class Mock (line 2) | class Mock extends app.Service {
method info (line 3) | * info (obj) {
method add (line 40) | * add (obj) {
FILE: app/service/notice.js
class ProjectUser (line 3) | class ProjectUser extends app.Service {
method pullMessage (line 5) | * pullMessage (obj) {
method listMessage (line 18) | * listMessage (obj) {
method changeReadStatus (line 51) | * changeReadStatus (obj) {
method getMessageInfo (line 55) | * getMessageInfo (obj) {
method getMessageNums (line 60) | * getMessageNums (obj) {
method getNoticeType (line 65) | * getNoticeType (obj) {
method updateNoticeType (line 70) | * updateNoticeType (obj) {
FILE: app/service/pages.js
class Pages (line 12) | class Pages extends app.Service {
method save (line 14) | * save (query) {
method list (line 105) | * list (query) {
method publiclist (line 145) | * publiclist (query) {
method info (line 198) | * info (query) {
method detail (line 224) | * detail (query) {
method setHomePage (line 254) | * setHomePage (query) {
method changeStatus (line 277) | * changeStatus (query) {
method delete (line 292) | * delete (query) {
method publish (line 305) | * publish (query) {
method count (line 317) | * count () {
method history (line 321) | * history (query) {
method getNameBykeys (line 340) | * getNameBykeys ({ ids }) {
method deleteHistory (line 350) | * deleteHistory (query) {
method historyPublish (line 359) | * historyPublish (query) {
method historyToDraft (line 371) | * historyToDraft (query) {
method updateFork (line 376) | * updateFork (query) {
method featuringPages (line 385) | async featuringPages ({ monthBefore = 3}) {
method updateFeatured (line 407) | async updateFeatured ({ value = 2, id, uid, key = '' }) {
FILE: app/service/project.js
class Project (line 4) | class Project extends app.Service {
method delete (line 5) | * delete (obj) {
method add (line 45) | * add (obj) {
method list (line 95) | * list (obj) {
method update (line 210) | * update (obj) {
method info (line 233) | * info (obj) {
method groupProjects (line 263) | * groupProjects (obj) {
method favorateProject (line 331) | * favorateProject (obj) {
method cancelFavorateProject (line 355) | * cancelFavorateProject (obj) {
method getFavorateProject (line 367) | * getFavorateProject (obj) {
FILE: app/service/projectUser.js
class ProjectUser (line 4) | class ProjectUser extends app.Service {
method add (line 6) | * add (obj) {
method delete (line 68) | * delete (obj) {
method list (line 110) | * list (obj) {
method update (line 133) | * update (obj) {
FILE: app/service/resources.js
class Resources (line 12) | class Resources extends app.Service {
method info (line 13) | * info (obj) {
method list (line 30) | * list (query) {
method save (line 81) | * save (query) {
method delete (line 158) | * delete (obj) {
method addUseCount (line 168) | * addUseCount (obj) {
FILE: app/service/statistics.js
class Statistics (line 20) | class Statistics extends app.Service {
method report (line 22) | report (doc) {
method addDoc (line 30) | async addDoc (doc) {
method addDocs (line 49) | async addDocs (docs) {
method search (line 85) | async search ({filter, timePeriod = [], size = 10, aggs, mustNot} = {}) {
method getPUV (line 124) | async getPUV ({pageId, pageIds, timePeriod, utm, interval = 'day', app...
method getPages (line 192) | async getPages ({appId, timePeriod, size = 20}) {
method getActions (line 236) | async getActions ({appId, pageId, utm, timePeriod, size = 20}) {
method getViewTime (line 277) | async getViewTime ({appId, pageId, utm, timePeriod, interval, avgonly}) {
method getUtms (line 338) | async getUtms ({appId, pageId, timePeriod, size = 20}) {
method getTodayOutline (line 375) | async getTodayOutline ({appId = 'tview'}) {
method actionTrack (line 488) | async actionTrack ({pageId, action, utm, timePeriod, size = 0, interva...
method createIndex (line 556) | async createIndex (indices) {
method deleteIndices (line 587) | async deleteIndices ({monthBefore = 6} = {}) {
FILE: app/service/tags.js
class Tags (line 12) | class Tags extends app.Service {
method list (line 14) | * list (query) {
method add (line 30) | * add (query) {
method useone (line 48) | * useone (query) {
FILE: app/service/template.js
class Template (line 12) | class Template extends app.Service {
method list (line 13) | * list (query) {
method detail (line 23) | * detail (query) {
method save (line 26) | * save (query) {
method delete (line 53) | * delete (id) {
FILE: app/service/user.js
function getAdminUrl (line 4) | function getAdminUrl (ctx, path) {
class User (line 21) | class User extends app.Service {
method login (line 22) | * login (obj) {
method oauthLogin (line 51) | * oauthLogin (obj) {
method register (line 120) | * register (obj) {
method activeEmail (line 199) | * activeEmail (obj) {
method sendActiveEmail (line 222) | * sendActiveEmail (uid) {
method sendEmail (line 255) | * sendEmail (email) {
method info (line 288) | * info (uid) {
method edit (line 302) | * edit (obj) {
method updatePassword (line 309) | * updatePassword (obj) {
method newUpdatePassword (line 325) | * newUpdatePassword (obj) {
method forgetPassword (line 349) | * forgetPassword (obj) {
method search (line 375) | * search (obj) {
method getUserRole (line 391) | async getUserRole ({uid = null}) {
FILE: config-parser.js
constant CLIENT_KEYS (line 4) | const CLIENT_KEYS = ['VIEW_PATH','ADMIN_PATH','EDITOR_PATH','API_PATH','...
constant REQUIRED_KEYS (line 5) | const REQUIRED_KEYS = ['sequelize', 'oss', 'es', 'redis', 'mail']
constant SEQUELIZE_REQUIRED (line 6) | const SEQUELIZE_REQUIRED = ['database', 'host', 'username', 'password']
constant OSS_REQUIRED (line 7) | const OSS_REQUIRED = ['accessKeyId', 'accessKeySecret', 'host', 'bucket']
constant ES_REQUIRED (line 8) | const ES_REQUIRED = ['host']
constant REDIS_REQUIRED (line 9) | const REDIS_REQUIRED = ['host', 'port']
constant MAIL_REQUIRED (line 10) | const MAIL_REQUIRED = ['host', 'port', 'user', 'pass']
constant CLIENT_CONFIG_DIR (line 11) | const CLIENT_CONFIG_DIR = process.env.CLIENT_CONFIG_DIR || './sub/gods-p...
constant SERVER_CONFIG_DIR (line 12) | const SERVER_CONFIG_DIR = process.env.SERVER_CONFIG_DIR || './config'
function parseConfig (line 14) | function parseConfig () {
function validConfig (line 25) | function validConfig (config) {
function rewriteConfig (line 76) | function rewriteConfig (config) {
FILE: config/config.default.js
method ignore (line 30) | ignore(ctx) {
method ignore (line 43) | ignore(ctx) {
FILE: sql/init.sql
type `tb_category` (line 33) | CREATE TABLE `tb_category` (
type `tb_company` (line 62) | CREATE TABLE `tb_company` (
type `tb_company_and_user` (line 89) | CREATE TABLE `tb_company_and_user` (
type `tb_component` (line 116) | CREATE TABLE `tb_component` (
type `tb_component_use` (line 151) | CREATE TABLE `tb_component_use` (
type `tb_favorate_project` (line 178) | CREATE TABLE `tb_favorate_project` (
type `tb_group` (line 205) | CREATE TABLE `tb_group` (
type `tb_group_and_project` (line 237) | CREATE TABLE `tb_group_and_project` (
type `tb_group_and_user` (line 264) | CREATE TABLE `tb_group_and_user` (
type `tb_kaptcha` (line 292) | CREATE TABLE `tb_kaptcha` (
type `tb_login_log` (line 319) | CREATE TABLE `tb_login_log` (
type `tb_login_token` (line 346) | CREATE TABLE `tb_login_token` (
type `tb_mock` (line 374) | CREATE TABLE `tb_mock` (
type `tb_pages` (line 400) | CREATE TABLE `tb_pages` (
type `tb_pages_history` (line 438) | CREATE TABLE `tb_pages_history` (
type `tb_project` (line 466) | CREATE TABLE `tb_project` (
type `tb_res_tags_rel` (line 498) | CREATE TABLE `tb_res_tags_rel` (
type `tb_resources` (line 525) | CREATE TABLE `tb_resources` (
type `tb_role` (line 558) | CREATE TABLE `tb_role` (
type `tb_tags` (line 586) | CREATE TABLE `tb_tags` (
type `tb_template` (line 605) | CREATE TABLE `tb_template` (
type `tb_user` (line 635) | CREATE TABLE `tb_user` (
type `tb_user_and_project` (line 669) | CREATE TABLE `tb_user_and_project` (
type `tb_user_grade` (line 698) | CREATE TABLE `tb_user_grade` (
type `tb_user_login` (line 726) | CREATE TABLE `tb_user_login` (
type `tb_user_notice` (line 758) | CREATE TABLE `tb_user_notice` (
type `tb_user_notice_type` (line 789) | CREATE TABLE `tb_user_notice_type` (
type `tb_valid_code` (line 816) | CREATE TABLE `tb_valid_code` (
Condensed preview — 105 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (334K chars).
[
{
"path": ".dockerignore",
"chars": 231,
"preview": "logs/\nnpm-debug.log\nnode_modules/\ncoverage/\n.idea/\nrun/\n.DS_Store\n*.swp\n.vscode/\nconfig/config.dev.js\nconfig/config.dock"
},
{
"path": ".eslintignore",
"chars": 9,
"preview": "coverage\n"
},
{
"path": ".eslintrc.js",
"chars": 627,
"preview": "module.exports = {\n root: true,\n parserOptions: {\n sourceType: 'module'\n },\n // https://github.com/feross/standar"
},
{
"path": ".gitignore",
"chars": 198,
"preview": "logs/\nnpm-debug.log\nnode_modules/\ncoverage/\n.idea/\nrun/\n.DS_Store\n*.swp\n.vscode/\nconfig/config.docker.js\n.travis.yml\napp"
},
{
"path": ".gitmodules",
"chars": 210,
"preview": "[submodule \"sub/gods-pen-admin\"]\n\tpath = sub/gods-pen-admin\n\turl = https://github.com/ymm-tech/gods-pen-admin.git\n[submo"
},
{
"path": "CHANGELOG.md",
"chars": 975,
"preview": "## V1.0.0\n- 新增flutter 布局 去除动画等\n- 页面类型\n - 后台设置/更新类型\n - 编辑器组件分类型显示\n - 编辑器样式设置面板分类型展示\n- 组件类型\n - 创建时可选择组件类型\n - 被正确筛选展示在"
},
{
"path": "Dockerfile",
"chars": 381,
"preview": "FROM node:10.13\nCOPY . /app\nWORKDIR /app\nRUN npm install pm2@3.5.0 -g --registry=https://registry.npm.taobao.org\nRUN mak"
},
{
"path": "LICENSE",
"chars": 1076,
"preview": "MIT License\n\nCopyright (c) 2020 Full Truck Alliance\n\nPermission is hereby granted, free of charge, to any person obtaini"
},
{
"path": "Makefile",
"chars": 994,
"preview": "# 路径配置\nadmin = sub/gods-pen-admin\neditor = sub/gods-pen\n\n# 安装依赖\ninstall-lib: install-server install-admin install-editor"
},
{
"path": "README.md",
"chars": 1733,
"preview": "\n# 码良服务端\n\n\n\n \n<p align=\"center\"><a href=\"https://godspen.ymm56.com/\" target=\"_blank\" rel=\"noopener noreferrer\"><img widt"
},
{
"path": "app/controller/category.js",
"chars": 1259,
"preview": "'use strict'\n\nmodule.exports = app => {\n class CategoryController extends app.Controller {\n * list () {\n const "
},
{
"path": "app/controller/component.js",
"chars": 3893,
"preview": "'use strict'\n\nmodule.exports = app => {\n class ComponentController extends app.Controller {\n * find () {\n const"
},
{
"path": "app/controller/group.js",
"chars": 2111,
"preview": "'use strict';\n\nmodule.exports = app => {\n class controller extends app.Controller {\n\n * add () {\n const { ctx }"
},
{
"path": "app/controller/groupUser.js",
"chars": 1755,
"preview": "'use strict';\n\nmodule.exports = app => {\n class controller extends app.Controller {\n\n * add () {\n const { ctx }"
},
{
"path": "app/controller/home.js",
"chars": 230,
"preview": "'use strict';\n\nmodule.exports = app => {\n class HomeController extends app.Controller {\n * index() {\n this.ctx."
},
{
"path": "app/controller/interface.js",
"chars": 12017,
"preview": "'use strict'\n\nmodule.exports = app => {\n class HomeController extends app.Controller {\n * add () {\n const { ctx"
},
{
"path": "app/controller/kaptcha.js",
"chars": 400,
"preview": "\nmodule.exports = app => {\n class HomeController extends app.Controller {\n * init (ctx) {\n const info = {\n "
},
{
"path": "app/controller/mock.js",
"chars": 1046,
"preview": "'use strict';\n\nmodule.exports = app => {\n class controller extends app.Controller {\n * infoOption () {\n const {"
},
{
"path": "app/controller/notice.js",
"chars": 1743,
"preview": "'use strict';\n\nmodule.exports = app => {\n class controller extends app.Controller {\n\n // 拉取最新20条未读消息\n * pullMessa"
},
{
"path": "app/controller/ossupload.js",
"chars": 3825,
"preview": "const OSS = require('ali-oss')\nconst co = require('co')\nconst sendToWormhole = require('stream-wormhole');\nconst mime = "
},
{
"path": "app/controller/pages.js",
"chars": 16541,
"preview": "'use strict'\nconst co = require('co')\nconst PSD = require('psd')\nconst fs = require('fs')\nconst path = require('path')\nc"
},
{
"path": "app/controller/project.js",
"chars": 3410,
"preview": "'use strict';\n\nmodule.exports = app => {\n class controller extends app.Controller {\n\n * add () {\n const { ctx }"
},
{
"path": "app/controller/projectUser.js",
"chars": 1783,
"preview": "'use strict';\n\nmodule.exports = app => {\n class controller extends app.Controller {\n\n * add () {\n const { ctx }"
},
{
"path": "app/controller/proxy.js",
"chars": 3808,
"preview": "const fs = require('fs')\nconst Controller = require('egg').Controller\nconst ua = require('random-ua')\nconst mime = requi"
},
{
"path": "app/controller/resources.js",
"chars": 2525,
"preview": "'use strict'\n\nmodule.exports = app => {\n class ResourcesController extends app.Controller {\n * info () {\n const"
},
{
"path": "app/controller/statistics.js",
"chars": 6736,
"preview": "const Controller = require('egg').Controller\nconst co = require('co')\nclass StatisticsController extends Controller {\n\n "
},
{
"path": "app/controller/tags.js",
"chars": 1366,
"preview": "'use strict'\nmodule.exports = app => {\n class TagsController extends app.Controller {\n * list () {\n const {\n "
},
{
"path": "app/controller/template.js",
"chars": 2185,
"preview": "'use strict'\n\nmodule.exports = app => {\n class TemplateController extends app.Controller {\n * list () {\n const "
},
{
"path": "app/controller/test.js",
"chars": 176,
"preview": "'use strict';\n\nmodule.exports = app => {\n class HomeController extends app.Controller {\n * index() {\n this.ctx."
},
{
"path": "app/controller/user.js",
"chars": 10158,
"preview": "'use strict';\nvar OSS = require('ali-oss');\nvar moment = require('moment')\nvar crypto = require('crypto')\nconst co = req"
},
{
"path": "app/extend/application.js",
"chars": 4616,
"preview": "const zlib = require('zlib')\nconst Redis = require('ioredis')\nconst REDIS = Symbol('REDIS#INSTANCE')\nconst request = req"
},
{
"path": "app/extend/context.js",
"chars": 540,
"preview": "class ERROR extends Error {\n constructor ({ code, status, msg, error }) {\n super(msg)\n this.code = code || 500\n "
},
{
"path": "app/extend/dingd.js",
"chars": 889,
"preview": "/**\n *\n */\n'use strict'\nmodule.exports = {\n * sendNotice (data) {\n try {\n const projectInfo = yield this.ctx.mo"
},
{
"path": "app/extend/email/tpl/active.html",
"chars": 939,
"preview": "<style type=\"text/css\">\n\thtml, body {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t}\n</style>\n<div style=\" margin:0; padding:0;width:598p"
},
{
"path": "app/extend/email/tpl/notice.html",
"chars": 680,
"preview": "<style type=\"text/css\">\n\thtml, body {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t}\n</style>\n<div style=\" margin:0; padding:0;width:598p"
},
{
"path": "app/extend/email/tpl/password.html",
"chars": 941,
"preview": "<style type=\"text/css\">\n\thtml, body {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t}\n</style>\n<div style=\" margin:0; padding:0;width:598p"
},
{
"path": "app/extend/helper.js",
"chars": 152,
"preview": "\nconst token = require('./token');\nconst tools = require('./tools');\nconst dingd = require('./dingd');\nmodule.exports = "
},
{
"path": "app/extend/noticeMessage.js",
"chars": 2762,
"preview": "module.exports = {\n GROUP_ADD_USER: {\n code: 101,\n title: '{{userName}}已加入项目组《{{groupName}}》',\n content: '{{us"
},
{
"path": "app/extend/token.js",
"chars": 1747,
"preview": "const defaultsp1 = '@#$*';\nconst defaultsp2 = '$%^&';\nconst tools = require('./tools');\nmodule.exports = {\n decryptToke"
},
{
"path": "app/extend/tools.js",
"chars": 9910,
"preview": "/**\n *\n */\n'use strict'\nconst crypto = require('crypto')\nconst CryptoJs = require('crypto-js')\nconst Mock = require('moc"
},
{
"path": "app/middleware/login_handler.js",
"chars": 1457,
"preview": "module.exports = () => {\n return function* (next) {\n // 校验登录信息\n const accessToken = this.helper.token.getToken.ca"
},
{
"path": "app/middleware/response_handler.js",
"chars": 1044,
"preview": "const throttle = require(\"lodash/throttle\")\n\nmodule.exports = (options, app) => {\n var notify = throttle((err, {url, re"
},
{
"path": "app/model/category.js",
"chars": 701,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('category', {\n id: { field: 'id', type: app.Sequeli"
},
{
"path": "app/model/component.js",
"chars": 1121,
"preview": "'use strict';\nmodule.exports = app => {\n var component = app.model.define('component', {\n id: { field: 'id', type: a"
},
{
"path": "app/model/component_use.js",
"chars": 565,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('ComponentUse', {\n cid: { field: 'cid', type: app.S"
},
{
"path": "app/model/group.js",
"chars": 971,
"preview": "module.exports = app => {\n return app.model.define('group', {\n id: { field: 'id', type: app.Sequelize.BIGINT, primar"
},
{
"path": "app/model/group_project.js",
"chars": 605,
"preview": "module.exports = app => {\n return app.model.define('groupProject', {\n groupId: { field: 'group_id', type: app.Sequel"
},
{
"path": "app/model/group_user.js",
"chars": 650,
"preview": "module.exports = app => {\n return app.model.define('groupUser', {\n groupId: { field: 'group_id', type: app.Sequelize"
},
{
"path": "app/model/history_interface.js",
"chars": 1892,
"preview": "/**\n * Created with WebStorm.\n * User: kevan\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 下午3:47\n * To change th"
},
{
"path": "app/model/interface.js",
"chars": 1289,
"preview": "module.exports = app => {\n return app.model.define('interface', {\n id: { field: 'id', type: app.Sequelize.BIGINT, pr"
},
{
"path": "app/model/interface_draf.js",
"chars": 588,
"preview": "module.exports = app => {\n return app.model.define('interfaceDraf', {\n id: { field: 'id', type: app.Sequelize.BIGINT"
},
{
"path": "app/model/interface_log.js",
"chars": 709,
"preview": "module.exports = app => {\n return app.model.define('interfaceLog', {\n id: { field: 'id', type: app.Sequelize.BIGINT,"
},
{
"path": "app/model/interface_user.js",
"chars": 543,
"preview": "module.exports = app => {\n return app.model.define('interfaceUser', {\n userId: { field: 'user_id', type: app.Sequeli"
},
{
"path": "app/model/pages.js",
"chars": 1235,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('pages', {\n id: { field: 'id', type: app.Sequelize."
},
{
"path": "app/model/pages_history.js",
"chars": 706,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('pagesHistory', {\n id: { field: 'id', type: app.Seq"
},
{
"path": "app/model/project.js",
"chars": 896,
"preview": "module.exports = app => {\n return app.model.define('project', {\n id: { field: 'id', type: app.Sequelize.BIGINT, prim"
},
{
"path": "app/model/project_data.js",
"chars": 771,
"preview": "module.exports = app => {\n return app.model.define('projectData', {\n id: { field: 'id', type: app.Sequelize.BIGINT, "
},
{
"path": "app/model/resTagsRel.js",
"chars": 641,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('resTagsRel', {\n id: { field: 'id', type: app.Seque"
},
{
"path": "app/model/resources.js",
"chars": 1046,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('resources', {\n id: { field: 'id', type: app.Sequel"
},
{
"path": "app/model/tag.js",
"chars": 812,
"preview": "/**\n * Created with WebStorm.\n * User: star\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 下午3:47\n * To change thi"
},
{
"path": "app/model/tag_interface.js",
"chars": 539,
"preview": "module.exports = app => {\n return app.model.define('tagInterface', {\n tagId: { field: 'tag_id', type: app.Sequelize."
},
{
"path": "app/model/tags.js",
"chars": 717,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('tags', {\n id: { field: 'id', type: app.Sequelize.I"
},
{
"path": "app/model/template.js",
"chars": 836,
"preview": "'use strict';\nmodule.exports = app => {\n return app.model.define('template', {\n id: { field: 'id', type: app.Sequeli"
},
{
"path": "app/model/user.js",
"chars": 951,
"preview": "module.exports = app => {\n return app.model.define('user', {\n id: { field: 'id', type: app.Sequelize.BIGINT, primary"
},
{
"path": "app/model/user_grade.js",
"chars": 745,
"preview": "module.exports = app => {\n return app.model.define('userGrade', {\n userId: { field: 'user_id', type: app.Sequelize.B"
},
{
"path": "app/model/user_login.js",
"chars": 959,
"preview": "module.exports = app => {\n return app.model.define('userLogin', {\n id: { field: 'id', type: app.Sequelize.BIGINT, pr"
},
{
"path": "app/model/user_login_log.js",
"chars": 573,
"preview": "module.exports = app => {\n return app.model.define('userLoginLog', {\n id: { field: 'id', type: app.Sequelize.BIGINT,"
},
{
"path": "app/model/user_notice.js",
"chars": 908,
"preview": "module.exports = app => {\n return app.model.define('userNotice', {\n id: { field: 'id', type: app.Sequelize.BIGINT, p"
},
{
"path": "app/model/user_notice_type.js",
"chars": 681,
"preview": "module.exports = app => {\n return app.model.define('userNoticeType', {\n userId: { field: 'user_id', type: app.Sequel"
},
{
"path": "app/model/user_project.js",
"chars": 722,
"preview": "module.exports = app => {\n return app.model.define('userProject', {\n projectId: { field: 'project_id', type: app.Seq"
},
{
"path": "app/model/valid_code.js",
"chars": 625,
"preview": "module.exports = app => {\n return app.model.define('validCode', {\n id: { field: 'id', type: app.Sequelize.BIGINT, pr"
},
{
"path": "app/router.js",
"chars": 6116,
"preview": "'use strict'\nmodule.exports = app => {\n console.log(app.config)\n\n app.router.prefix('/' + app.config.API_PATH.replace("
},
{
"path": "app/schedule/es_delete.js",
"chars": 341,
"preview": "module.exports = {\n schedule: {\n cron: '0 0 1 * *', // 每月1号\n type: 'worker', // 随机一个 worker 执行\n },\n cronOptions"
},
{
"path": "app/schedule/es_report.js",
"chars": 260,
"preview": "module.exports = {\n schedule: {\n interval: '1m', // 1分钟间隔\n type: 'all',\n },\n async task(ctx) {\n console.info"
},
{
"path": "app/schedule/pu_uv_ratio.js",
"chars": 700,
"preview": "const PUV_RATIO_THRESHOLD = 1.6 // 小时 pv/uv 平均1.34 最大值3.72 取1.6为告警阈值\n\nmodule.exports = app => {\n return {\n schedule:"
},
{
"path": "app/schedule/pvcount.js",
"chars": 295,
"preview": "module.exports = app => {\n return {\n schedule: {\n // interval: '30s',\n cron: '0 0 */1 * *', // 每天零点\n "
},
{
"path": "app/service/base.js",
"chars": 1807,
"preview": "module.exports = app => {\n class BaseService extends app.Service {\n * getUserRole (uid, projectId) {\n let role "
},
{
"path": "app/service/category.js",
"chars": 2346,
"preview": "/**\n * Created with WebStorm.\n * User: kevan\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 上午9:30\n * To change th"
},
{
"path": "app/service/component.js",
"chars": 12834,
"preview": "const co = require('co')\nconst OSS = require('ali-oss')\nconst request = require('request')\nvar JSZip = require(\"jszip\")\n"
},
{
"path": "app/service/group.js",
"chars": 7910,
"preview": "const nunjucks = require('nunjucks');\nconst noticeMessage = require('../extend/noticeMessage');\nmodule.exports = app => "
},
{
"path": "app/service/groupUser.js",
"chars": 10199,
"preview": "const moment = require('moment');\nconst nunjucks = require('nunjucks');\nconst noticeMessage = require('../extend/noticeM"
},
{
"path": "app/service/kaptcha.js",
"chars": 452,
"preview": "\nconst captchapng = require('captchapng');\n\nmodule.exports = app => {\n class Kaptcha extends app.Service {\n * getCap"
},
{
"path": "app/service/mock.js",
"chars": 1761,
"preview": "module.exports = app => {\n class Mock extends app.Service {\n * info (obj) {\n // 查询项目\n // 查询项目对应接口信息\n "
},
{
"path": "app/service/notice.js",
"chars": 3708,
"preview": "const moment = require('moment');\nmodule.exports = app => {\n class ProjectUser extends app.Service {\n\n * pullMessage"
},
{
"path": "app/service/pages.js",
"chars": 12648,
"preview": "/**\n * Created with WebStorm.\n * User: kevan\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 上午9:30\n * To change th"
},
{
"path": "app/service/project.js",
"chars": 15063,
"preview": "const nunjucks = require('nunjucks');\nconst noticeMessage = require('../extend/noticeMessage');\nmodule.exports = app => "
},
{
"path": "app/service/projectUser.js",
"chars": 8579,
"preview": "const nunjucks = require('nunjucks');\nconst noticeMessage = require('../extend/noticeMessage');\nmodule.exports = app => "
},
{
"path": "app/service/resources.js",
"chars": 5760,
"preview": "/**\n * Created with WebStorm.\n * User: kevan\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 上午9:30\n * To change th"
},
{
"path": "app/service/statistics.js",
"chars": 18411,
"preview": "const es = require('elasticsearch')\nconst throttle = require(\"lodash/throttle\")\n\nmodule.exports = app => {\n const ES_SE"
},
{
"path": "app/service/tags.js",
"chars": 1523,
"preview": "/**\n * Created with WebStorm.\n * User: kevan\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 上午9:30\n * To change th"
},
{
"path": "app/service/template.js",
"chars": 2282,
"preview": "/**\n * Created with WebStorm.\n * User: kevan\n * Email:258137678@qq.com\n * Date: 2017/5/3\n * Time: 上午9:30\n * To change th"
},
{
"path": "app/service/user.js",
"chars": 14725,
"preview": "\nmodule.exports = app => {\n\n function getAdminUrl (ctx, path) {\n if (/https?:\\/\\//.test(app.config.ADMIN_PATH)) retu"
},
{
"path": "app.js",
"chars": 57,
"preview": "module.exports = app => {\n console.log('hello world')\n}\n"
},
{
"path": "config/config.app.js",
"chars": 546,
"preview": "module.exports = {\n accessTokenKey: 'maliangtoken', // cookies的key信息\n accessTokenKeyTime: 86400 * 1000, // cookies的key"
},
{
"path": "config/config.default.js",
"chars": 2434,
"preview": "const appConfig = require('./config.app')\n\nmodule.exports = appInfo => {\n let config = {}\n config.appConfig = appConfi"
},
{
"path": "config/config.dev.js",
"chars": 820,
"preview": "/*\n*\n* 开发环境配置\n* 下面是码良依赖的各种服务,请务必逐一配置\n* \n*/\n\nmodule.exports = {\n sequelize: {\n dialect: 'mysql',\n port: '3306',\n "
},
{
"path": "config/config.production.js",
"chars": 755,
"preview": "/*\n*\n* 生产环境配置\n* 下面是码良依赖的各种服务,请务必逐一配置\n*\n*/\n\nmodule.exports = {\n sequelize: {\n dialect: 'mysql',\n port: '3306',\n "
},
{
"path": "config/plugin.js",
"chars": 202,
"preview": "exports.static = true\n\nexports.cors = {\n enable: true,\n package: 'egg-cors',\n};\nexports.validate = {\n package: 'egg-v"
},
{
"path": "config-parser.js",
"chars": 3319,
"preview": "const fs = require('fs')\nconst path = require('path')\nconst yaml = require('js-yaml')\nconst CLIENT_KEYS = ['VIEW_PATH','"
},
{
"path": "docker-entrypoint.sh",
"chars": 236,
"preview": "#!/bin/sh\n\n#if [ -d '/app/static-html' -a \"`ls -A /app/static-html`\" != \"\" ]; then\nif [ $1 != 'pm2-runtime' -a $1 != 'pm"
},
{
"path": "index.js",
"chars": 102,
"preview": "'use strict'\n\nrequire('egg').startCluster({\n baseDir: __dirname,\n port: process.env.PORT || 7051\n})\n"
},
{
"path": "package.json",
"chars": 1767,
"preview": "{\n \"name\": \"gods-pen-server\",\n \"version\": \"1.0.0\",\n \"description\": \"码良服务端\",\n \"private\": false,\n \"dependencies\": {\n "
},
{
"path": "process.json",
"chars": 389,
"preview": "{\n \"script\": \"index.js\",\n \"name\":\"godspen\",\n \"log_date_format\": \"YYYY-MM-DD HH:mm:ss\",\n \"env\": {\n \"NODE_ENV\": \"de"
},
{
"path": "sql/init.sql",
"chars": 32295,
"preview": "-- MySQL dump 10.13 Distrib 5.7.27, for Linux (x86_64)\n--\n-- Host: localhost Database: godspen_db\n-- ---------------"
},
{
"path": "sub-build/.gitkeep",
"chars": 0,
"preview": ""
}
]
About this extraction
This page contains the full source code of the ymm-tech/gods-pen-server GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 105 files (296.6 KB), approximately 84.1k tokens, and a symbol index with 370 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.