feat: 权限控制加强、swagger 导入加强

pull/663/head
bigfengyu 5 years ago
parent 87ed661dea
commit df84f7bae2

@ -12,7 +12,7 @@ router.get('/export/postman', async ctx => {
const repoId = +ctx.query.id
if (
!(await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY,
ACCESS_TYPE.REPOSITORY_GET,
ctx.session.id,
repoId
))
@ -41,7 +41,7 @@ router.get('/export/markdown', async ctx => {
const repoId = +ctx.query.id
if (
!(await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY,
ACCESS_TYPE.REPOSITORY_GET,
ctx.session.id,
repoId
))
@ -59,7 +59,7 @@ router.get('/export/docx', async ctx => {
const repoId = +ctx.query.id
if (
!(await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY,
ACCESS_TYPE.REPOSITORY_GET,
ctx.session.id,
repoId
))

@ -113,7 +113,7 @@ router.get('/organization/joined', isLoggedIn, async (ctx) => {
})
router.get('/organization/get', async (ctx) => {
const organizationId = +ctx.query.id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, organizationId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_GET, ctx.session.id, organizationId)) {
ctx.body = COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -144,7 +144,7 @@ router.post('/organization/create', isLoggedIn, async (ctx) => {
router.post('/organization/update', isLoggedIn, async (ctx, next) => {
let body = Object.assign({}, ctx.request.body)
const organizationId = +body.id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, organizationId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_SET, ctx.session.id, organizationId)) {
ctx.body = COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -188,7 +188,7 @@ router.post('/organization/update', isLoggedIn, async (ctx, next) => {
router.post('/organization/transfer', isLoggedIn, async (ctx) => {
let { id, ownerId } = ctx.request.body
const organizationId = +id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, organizationId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_SET, ctx.session.id, organizationId)) {
ctx.body = COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -201,7 +201,7 @@ router.post('/organization/transfer', isLoggedIn, async (ctx) => {
router.get('/organization/remove', isLoggedIn, async (ctx, next) => {
let { id } = ctx.query
const organizationId = +id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, organizationId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_SET, ctx.session.id, organizationId)) {
ctx.body = COMMON_ERROR_RES.ACCESS_DENY
return
}

@ -47,7 +47,7 @@ router.get('/repository/list', async (ctx) => {
let { name, user, organization } = ctx.query
if (+organization > 0) {
const access = await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, organization)
const access = await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_GET, ctx.session.id, organization)
if (access === false) {
ctx.body = {
@ -74,10 +74,11 @@ router.get('/repository/list', async (ctx) => {
include: [
QueryInclude.Creator,
QueryInclude.Owner,
QueryInclude.Locker,
],
} as any)
let pagination = new Pagination(total, ctx.query.cursor || 1, ctx.query.limit || 100)
QueryInclude.Locker
]
})
let limit = Math.min(ctx.query.limit ?? 10, 100)
let pagination = new Pagination(total, ctx.query.cursor || 1, limit)
let repositories = await Repository.findAll({
where,
attributes: { exclude: [] },
@ -91,11 +92,22 @@ router.get('/repository/list', async (ctx) => {
],
offset: pagination.start,
limit: pagination.limit,
order: [['updatedAt', 'DESC']],
} as any)
order: [['updatedAt', 'DESC']]
})
let repoData = await Promise.all(repositories.map(async (repo) => {
const canUserEdit = await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY_SET,
ctx.session.id,
repo.id,
)
return {
...repo.toJSON(),
canUserEdit
}
}))
ctx.body = {
isOk: true,
data: repositories,
data: repoData,
pagination: pagination,
}
})
@ -113,8 +125,7 @@ router.get('/repository/owned', isLoggedIn, async (ctx) => {
}
let auth: User = await User.findByPk(ctx.query.user || ctx.session.id)
// let total = await auth.countOwnedRepositories({ where })
// let pagination = new Pagination(total, ctx.query.cursor || 1, ctx.query.limit || 100)
let repositories = await auth.$get('ownedRepositories', {
where,
include: [
@ -125,12 +136,16 @@ router.get('/repository/owned', isLoggedIn, async (ctx) => {
QueryInclude.Organization,
QueryInclude.Collaborators,
],
// offset: pagination.start,
// limit: pagination.limit,
order: [['updatedAt', 'DESC']],
order: [['updatedAt', 'DESC']]
})
let repoData = repositories.map(repo => {
return {
...repo.toJSON(),
canUserEdit: true
}
})
ctx.body = {
data: repositories,
data: repoData,
pagination: undefined,
}
})
@ -148,8 +163,6 @@ router.get('/repository/joined', isLoggedIn, async (ctx) => {
}
let auth = await User.findByPk(ctx.query.user || ctx.session.id)
// let total = await auth.countJoinedRepositories({ where })
// let pagination = new Pagination(total, ctx.query.cursor || 1, ctx.query.limit || 100)
let repositories = await auth.$get('joinedRepositories', {
where,
attributes: { exclude: [] },
@ -161,19 +174,23 @@ router.get('/repository/joined', isLoggedIn, async (ctx) => {
QueryInclude.Organization,
QueryInclude.Collaborators,
],
// offset: pagination.start,
// limit: pagination.limit,
order: [['updatedAt', 'DESC']],
order: [['updatedAt', 'DESC']]
})
let repoData = repositories.map(repo => {
return {
...repo.toJSON(),
canUserEdit: true,
}
})
ctx.body = {
data: repositories,
pagination: undefined,
data: repoData,
pagination: undefined
}
})
router.get('/repository/get', async (ctx) => {
const access = await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY,
ACCESS_TYPE.REPOSITORY_GET,
ctx.session.id,
ctx.query.id,
ctx.query.token
@ -186,54 +203,52 @@ router.get('/repository/get', async (ctx) => {
return
}
const excludeProperty = ctx.query.excludeProperty || false
const cacheKey = excludeProperty ? CACHE_KEY.REPOSITORY_GET_EXCLUDE_PROPERTY : CACHE_KEY.REPOSITORY_GET
const tryCache = await RedisService.getCache(cacheKey, ctx.query.id)
let repository: Partial<Repository>
if (tryCache && !excludeProperty) {
repository = JSON.parse(tryCache)
} else {
// 分开查询减少
let [repositoryOmitModules, repositoryModules] = await Promise.all([
Repository.findByPk(ctx.query.id, {
attributes: { exclude: [] },
include: [
QueryInclude.Creator,
QueryInclude.Owner,
QueryInclude.Locker,
QueryInclude.Members,
QueryInclude.Organization,
QueryInclude.Collaborators,
],
}),
Repository.findByPk(ctx.query.id, {
attributes: { exclude: [] },
include: [
excludeProperty
? QueryInclude.RepositoryHierarchyExcludeProperty
: QueryInclude.RepositoryHierarchy,
],
order: [
[{ model: Module, as: 'modules' }, 'priority', 'asc'],
[
{ model: Module, as: 'modules' },
{ model: Interface, as: 'interfaces' },
'priority',
'asc',
],
const canUserEdit = await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY_SET,
ctx.session.id,
ctx.query.id,
ctx.query.token,
)
let repository: Partial<Repository> & {
canUserEdit: boolean
}
// 分开查询减少查询时间
let [repositoryOmitModules, repositoryModules] = await Promise.all([
Repository.findByPk(ctx.query.id, {
attributes: { exclude: [] },
include: [
QueryInclude.Creator,
QueryInclude.Owner,
QueryInclude.Locker,
QueryInclude.Members,
QueryInclude.Organization,
QueryInclude.Collaborators,
],
}),
Repository.findByPk(ctx.query.id, {
attributes: { exclude: [] },
include: [
excludeProperty
? QueryInclude.RepositoryHierarchyExcludeProperty
: QueryInclude.RepositoryHierarchy,
],
order: [
[{ model: Module, as: 'modules' }, 'priority', 'asc'],
[
{ model: Module, as: 'modules' },
{ model: Interface, as: 'interfaces' },
'priority',
'asc',
],
}),
])
repository = {
...repositoryOmitModules.toJSON(),
...repositoryModules.toJSON()
}
await RedisService.setCache(
cacheKey,
JSON.stringify(repository),
ctx.query.id
)
],
}),
])
repository = {
...repositoryOmitModules.toJSON(),
...repositoryModules.toJSON(),
canUserEdit
}
ctx.body = {
data: repository,
}
@ -281,7 +296,7 @@ router.post('/repository/create', isLoggedIn, async (ctx, next) => {
router.post('/repository/update', isLoggedIn, async (ctx, next) => {
const body = Object.assign({}, ctx.request.body)
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, ctx.session.id, body.id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, body.id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -354,7 +369,7 @@ router.post('/repository/update', isLoggedIn, async (ctx, next) => {
router.post('/repository/transfer', isLoggedIn, async (ctx) => {
let { id, ownerId, organizationId } = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, organizationId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_SET, ctx.session.id, organizationId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -372,7 +387,7 @@ router.post('/repository/transfer', isLoggedIn, async (ctx) => {
router.get('/repository/remove', isLoggedIn, async (ctx, next) => {
const id = +ctx.query.id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, ctx.session.id, id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -397,7 +412,7 @@ router.get('/repository/remove', isLoggedIn, async (ctx, next) => {
// TOEO 锁定/解锁仓库 待测试
router.post('/repository/lock', isLoggedIn, async (ctx) => {
const id = +ctx.request.body.id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, ctx.session.id, id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -475,7 +490,7 @@ router.post('/module/create', isLoggedIn, async (ctx, next) => {
router.post('/module/update', isLoggedIn, async (ctx, next) => {
const { id, name, description } = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.MODULE, ctx.session.id, +id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.MODULE_SET, ctx.session.id, +id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -521,7 +536,7 @@ router.post('/module/move', isLoggedIn, async ctx => {
router.get('/module/remove', isLoggedIn, async (ctx, next) => {
let { id } = ctx.query
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.MODULE, ctx.session.id, +id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.MODULE_SET, ctx.session.id, +id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -570,7 +585,7 @@ router.get('/interface/count', async (ctx) => {
router.get('/interface/list', async (ctx) => {
let where: any = {}
let { repositoryId, moduleId, name } = ctx.query
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, ctx.session.id, +repositoryId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_GET, ctx.session.id, +repositoryId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -594,7 +609,7 @@ router.get('/repository/defaultVal/get/:id', async (ctx) => {
router.post('/repository/defaultVal/update/:id', async (ctx) => {
const repositoryId: number = ctx.params.id
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, ctx.session.id, repositoryId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, repositoryId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -643,7 +658,7 @@ router.get('/interface/get', async (ctx) => {
if (
!(await AccessUtils.canUserAccess(
ACCESS_TYPE.REPOSITORY,
ACCESS_TYPE.REPOSITORY_GET,
ctx.session.id,
itf.repositoryId
))
@ -699,7 +714,7 @@ router.post('/interface/create', isLoggedIn, async (ctx, next) => {
router.post('/interface/update', isLoggedIn, async (ctx, next) => {
let body = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE, ctx.session.id, +body.id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, +body.id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -744,7 +759,7 @@ router.post('/interface/move', isLoggedIn, async ctx => {
router.get('/interface/remove', async (ctx, next) => {
let { id } = ctx.query
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE, ctx.session.id, +id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, +id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -783,7 +798,7 @@ router.post('/interface/lock', async (ctx, next) => {
}
let { id } = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE, ctx.session.id, +id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, +id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -820,7 +835,7 @@ router.post('/interface/unlock', async (ctx) => {
}
let { id } = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE, ctx.session.id, +id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, +id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -919,7 +934,7 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => {
properties = Array.isArray(properties) ? properties : [properties]
let itf = await Interface.findByPk(itfId)
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE, ctx.session.id, itfId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, itfId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -1014,7 +1029,7 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => {
router.get('/property/remove', isLoggedIn, async (ctx) => {
let { id } = ctx.query
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.PROPERTY, ctx.session.id, id)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.PROPERTY_SET, ctx.session.id, id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -1027,7 +1042,7 @@ router.get('/property/remove', isLoggedIn, async (ctx) => {
router.post('/repository/import', isLoggedIn, async (ctx) => {
const { docUrl, orgId } = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, orgId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_SET, ctx.session.id, orgId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -1044,7 +1059,7 @@ router.post('/repository/import', isLoggedIn, async (ctx) => {
router.post('/repository/importswagger', isLoggedIn, async (ctx) => {
const { orgId, repositoryId, swagger, version = 1, mode = 'manual'} = ctx.request.body
// 权限判断
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION, ctx.session.id, orgId)) {
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, repositoryId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
@ -1063,7 +1078,7 @@ router.post('/repository/importswagger', isLoggedIn, async (ctx) => {
router.post('/repository/importJSON', isLoggedIn , async ctx => {
const { data } = ctx.request.body
if (!(await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, ctx.session.id, data.id))) {
if (!(await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, data.id))) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}

@ -2,27 +2,61 @@ import OrganizationService from '../../service/organization'
import RepositoryService from '../../service/repository'
import { Module, Interface, Property } from '../../models'
export enum ACCESS_TYPE { ORGANIZATION, REPOSITORY, MODULE, INTERFACE, PROPERTY, USER, ADMIN }
export enum ACCESS_TYPE {
ORGANIZATION_GET,
ORGANIZATION_SET,
REPOSITORY_GET,
REPOSITORY_SET,
MODULE_GET,
MODULE_SET,
INTERFACE_GET,
INTERFACE_SET,
PROPERTY_GET,
PROPERTY_SET,
USER,
ADMIN,
}
const inTestMode = process.env.TEST_MODE === 'true'
export class AccessUtils {
public static async canUserAccess(accessType: ACCESS_TYPE, curUserId: number, entityId: number, token?: string): Promise<boolean> {
public static async canUserAccess(
accessType: ACCESS_TYPE,
curUserId: number,
entityId: number,
token?: string,
): Promise<boolean> {
// 测试模式无权限
if (inTestMode) {
return true
}
if (accessType === ACCESS_TYPE.ORGANIZATION) {
return await OrganizationService.canUserAccessOrganization(curUserId, entityId)
} else if (accessType === ACCESS_TYPE.REPOSITORY) {
return await RepositoryService.canUserAccessRepository(curUserId, entityId, token)
} else if (accessType === ACCESS_TYPE.MODULE) {
// 无 session 时拒绝写操作
if (!curUserId) {
return false
}
if (
accessType === ACCESS_TYPE.ORGANIZATION_GET ||
accessType === ACCESS_TYPE.ORGANIZATION_SET
) {
return OrganizationService.canUserAccessOrganization(curUserId, entityId)
} else if (
accessType === ACCESS_TYPE.REPOSITORY_GET ||
accessType === ACCESS_TYPE.REPOSITORY_SET
) {
return RepositoryService.canUserAccessRepository(curUserId, entityId, token)
} else if (accessType === ACCESS_TYPE.MODULE_GET || accessType === ACCESS_TYPE.MODULE_SET) {
const mod = await Module.findByPk(entityId)
return await RepositoryService.canUserAccessRepository(curUserId, mod.repositoryId, token)
} else if (accessType === ACCESS_TYPE.INTERFACE) {
return RepositoryService.canUserAccessRepository(curUserId, mod.repositoryId, token)
} else if (
accessType === ACCESS_TYPE.INTERFACE_GET ||
accessType === ACCESS_TYPE.INTERFACE_SET
) {
const itf = await Interface.findByPk(entityId)
return await RepositoryService.canUserAccessRepository(curUserId, itf.repositoryId, token)
} else if (accessType === ACCESS_TYPE.PROPERTY) {
return RepositoryService.canUserAccessRepository(curUserId, itf.repositoryId, token)
} else if (accessType === ACCESS_TYPE.PROPERTY_GET || accessType === ACCESS_TYPE.PROPERTY_SET) {
const p = await Property.findByPk(entityId)
return await RepositoryService.canUserAccessRepository(curUserId, p.repositoryId, token)
return RepositoryService.canUserAccessRepository(curUserId, p.repositoryId, token)
}
return false
}
@ -33,4 +67,4 @@ export class AccessUtils {
}
return curUserId === 1
}
}
}

@ -8,7 +8,7 @@ const start = () => {
let open = false
console.log('----------------------------------------')
app.listen(port, () => {
console.log(`rap2-dolores is running as ${url}`)
console.log(`rap2-delos is running as ${url}`)
if (!open) return
try {
execSync(`osascript openChrome.applescript ${url}`, { cwd: __dirname, stdio: 'ignore' })

@ -2,26 +2,158 @@ import * as nodemailer from 'nodemailer'
import config from '../config'
export default class MailService {
public static async sendMail(mailOptions: nodemailer.SendMailOptions) {
const transporter = nodemailer.createTransport(config.mail)
public static send(to: string, subject: string, html: string) {
return new Promise((resolve, reject) => {
transporter.verify(function(error) {
if (error) {
reject(error)
} else {
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
reject(error)
} else {
resolve(info.response)
}
})
}
})
})
}
nodemailer.createTestAccount((_err, _account) => {
const transporter = nodemailer.createTransport(config.mail)
public static send(to: string | string[], subject: string, html: string) {
const transporter = nodemailer.createTransport(config.mail)
// setup email data with unicode symbols
const mailOptions = {
from: `"RAP2 Notifier" <${config.mailSender}>`, // sender address
to,
subject,
html,
}
const mailOptions = {
from: `"RAP2 Notifier" <${config.mailSender}>`,
to,
subject,
html,
}
// send mail with defined transport object
transporter.sendMail(mailOptions)
return new Promise((resolve, reject) => {
transporter.verify(function(error) {
if (error) {
reject(error)
} else {
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
reject(error)
} else {
resolve(info.response)
}
})
}
})
})
}
public static mailFindpwdTemp = `<head>
public static mailNoticeTemp = `<head>
<base target="_blank" />
</head>
<body>
<div id="content" bgcolor="#f0f0f0" style="background-color:#f0f0f0;padding:10px">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td valign="top">
<table class="full-width" align="center" width="700" border="0" cellpadding="0" cellspacing="0"
bgcolor="#ffffff" style="background-color:#ffffff; width:700px;">
<tr>
<td style="vertical-align:top;">
<table class="full-width" align="center" width="700" border="0" cellpadding="0" cellspacing="0"
style="width:700px;">
<tr>
<td valign="top">
<table class="full-width" width="549" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
<td width="32" valign="middle" style="padding-top:30px; padding-bottom:32px; width:32px;"><img width="140" height="34" src="http://img.alicdn.com/tfs/TB1vtTllHH1gK0jSZFwXXc7aXXa-140-34.png"></td>
<td valign="middle"></td>
<td width="10"></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td valign="top" style="padding-top:10px ;">
<table class="full-width" align="center" width="700" border="0" cellpadding="0" cellspacing="0"
bgcolor="#ffffff" style="background-color:#ffffff; width:700px;">
<tr>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
<td class="mobile-headline"
style="color:#000000; font-size:24px; line-height:26px;">{=TITLE=}</td>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
</tr>
<tr>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
<td
style="color:#555555; font-size:15px; line-height:20px; padding-top:30px;">
<ul style="margin-bottom:0px; margin-top:0px;">
{=CONTENT=}
</ul>
</td>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
</tr>
</table>
<table class="full-width" align="center" width="700" border="0" cellpadding="0" cellspacing="0"
bgcolor="#F7F8F9" style="background-color:#F7F8F9; width:700px;">
<tr>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
<td
style="color:#555555; font-size:15px; line-height:20px; padding-top:30px; padding-bottom:30px;">
<strong>,</strong>
</td>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td valign="top">
<table class="full-width" align="center" width="700" border="0" cellpadding="0" cellspacing="0"
bgcolor="#3f51b5" style="background-color:#3f51b5; width:700px;">
<tr>
<td width="22">&nbsp;</td>
<td align="center"
style="color:#ffffff; font-size:11px; line-height:14px; padding-top:20px;text-align:center;">
<strong><a href="http://rap2.alibaba-inc.com/"
style="color:#ffffff; font-weight:bold; text-decoration:none;">RAP2</a> | <a
href="https://github.com/thx/rap2-delos"
style="color:#ffffff; font-weight:bold; text-decoration:none;">GitHub</a></strong>
</td>
<td width="22">&nbsp;</td>
</tr>
<tr>
<td width="22" colspan="3">&nbsp;</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<style type="text/css">
body {
font-size: 14px;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
line-height: 1.666;
padding: 0;
margin: 0;
overflow: auto;
white-space: normal;
word-wrap: break-word;
min-height: 100px
}
</style>
</body>`
public static mailFindpwdTemp = `<head>
<base target="_blank" />
</head>
<body>
@ -84,6 +216,7 @@ public static mailFindpwdTemp = `<head>
style="color:#555555; font-size:15px; line-height:20px; padding-top:30px; padding-bottom:30px;">
<strong></strong>RAP2使
<br><br>使
<br><br><a href="http://rap2.taobao.org/account/unsubscribe" style="color:#1473E6; font-weight:bold; text-decoration:none;">退</a>
</td>
<td class="mobile-spacer" width="30" style="width:30px;">&nbsp;</td>
</tr>
@ -130,4 +263,4 @@ public static mailFindpwdTemp = `<head>
}
</style>
</body>`
}
}

@ -1,25 +1,30 @@
import { Repository, Module, Interface, Property, User, QueryInclude } from '../models'
import { SCOPES } from "../models/bo/property"
import * as md5 from 'md5'
import * as querystring from 'querystring'
import * as rp from 'request-promise'
const isMd5 = require('is-md5')
import Tree from '../routes/utils/tree'
import * as JSON5 from 'json5'
import * as querystring from 'querystring'
import * as rp from 'request-promise'
import { Op } from 'sequelize'
import RedisService, { CACHE_KEY } from "./redis"
import MailService from './mail'
import * as md5 from 'md5'
const isMd5 = require('is-md5')
import * as _ from 'lodash'
const SWAGGER_VERSION = {
1: '2.0'
}
/**
* swagger json
* @param list
*/
const arrayToTree = (list) => {
const parseChildren = (list, parent) => {
list.forEach((item) => {
if (item.parent === parent.id) {
item.depth = parent.depth + 1
item.parentName = parent.name
item.children = item.children || []
parent.children.push(item)
parseChildren(list, item)
@ -36,6 +41,50 @@ const arrayToTree = (list) => {
})
}
/**
* swagger json
* @param tree
*/
const treeToArray = (tree: any) => {
const parseChildren = (parent: any, result: any) => {
if (!parent.children) { return result }
parent.children.forEach((item: any) => {
result.push(item)
parseChildren(item, result)
delete item.children
})
return result
}
return parseChildren(tree, [])
}
/**
* -
* @param list
*/
const arrayToTreeProperties = (list: any) => {
const parseChildren = (list: any, parent: any) => {
list.forEach((item: any) => {
if (item.parentId === parent.id) {
item.depth = parent.depth + 1
item.children = item.children || []
parent.children.push(item)
parseChildren(list, item)
}
})
return parent
}
return parseChildren(list, {
id: -1,
name: 'root',
children: [],
depth: -1,
})
}
/**
*
*/
const REQUEST_TYPE_POS = {
path: 2,
query: 2,
@ -44,13 +93,21 @@ const REQUEST_TYPE_POS = {
body: 3
}
let checkSwaggerResult = []
let changeTip = '' // 变更信息
/**
* Swagger JSON
* @param parameters
* @param parent
* @param result swagger
* @param definitions swagger $ref definitions
* @param parent id
* @param parentName name
* @param depth parameters list
* @param result swagger -- swagger
* @param definitions swagger $ref definitions swaggerdefinitions,
* @param scope --
* @param apiInfo --
*/
const parse = (parameters, parent, result, definitions) => {
const parse = (parameters, parent, parentName, depth, result, definitions, scope, apiInfo) => {
for (let key = 0, len = parameters.length; key < len; key++) {
const param = parameters[key]
@ -58,6 +115,8 @@ const parse = (parameters, parent, result, definitions) => {
// 非对象或者数组的基础类型
result.push({
...param, parent,
parentName,
depth,
id: `${parent}-${key}`
})
} else {
@ -68,17 +127,20 @@ const parse = (parameters, parent, result, definitions) => {
result.push({
...param, parent,
parentName,
depth,
id: `${parent}-${key}`,
type: paramType
})
let refName
if (!param.items) {
// 对象
refName = param.$ref.split('#/definitions/')[1]
delete result.find(item => item.id === `${parent}-${key}`)['$ref']
}
if (param.items) {
// 数组
refName = param.items.$ref.split('#/definitions/')[1]
delete result.find(item => item.id === `${parent}-${key}`).items
}
@ -87,20 +149,194 @@ const parse = (parameters, parent, result, definitions) => {
if (ref && ref.properties) {
const properties = ref.properties
const list = []
for (const key in properties) {
list.push({
name: key,
...properties[key],
in: param.in, // response 无所谓不使用但是request 使用
required: (ref.required || []).indexOf(key) >= 0
})
// swagger文档中对definition定义属性又引用自身的情况处理-死循环
if (properties[key].$ref) {
if (properties[key].$ref.split('#/definitions/')[1] === refName) {
// delete properties[key].$ref
list.push({
name: key,
parentName: param.name,
depth: depth + 1,
...properties[key],
$ref: null,
type: 'object',
in: param.in,
required: (ref.required || []).indexOf(key) >= 0,
description: `【递归父级属性】${properties[key].description || ''}`
})
} else {
list.push({
name: key,
parentName: param.name,
depth: depth + 1,
...properties[key],
in: param.in,
required: (ref.required || []).indexOf(key) >= 0,
})
}
} else if ((properties[key].items || {}).$ref) {
if (properties[key].items.$ref.split('#/definitions/')[1] === refName) {
// delete properties[key].items.$ref
list.push({
name: key,
parentName: param.name,
depth: depth + 1,
...properties[key],
type: 'array',
$ref: null,
in: param.in,
required: (ref.required || []).indexOf(key) >= 0,
description: `【递归父级属性】${properties[key].description || ''}`
})
} else {
list.push({
name: key,
parentName: param.name,
depth: depth + 1,
...properties[key],
in: param.in,
required: (ref.required || []).indexOf(key) >= 0,
})
}
} else {
list.push({
name: key,
parentName: param.name,
depth: depth + 1,
...properties[key],
in: param.in, // response 无所谓不使用但是request 使用
required: (ref.required || []).indexOf(key) >= 0,
})
}
}
parse(list, `${parent}-${key}`, result, definitions)
parse(list, `${parent}-${key}`, param.name, depth + 1, result, definitions, scope, apiInfo)
}
}
}
}
const transformRapParams = (p) => {
let rule = '', description = '', value = p.default || ''
// 类型转化处理
let type = (p.type || 'string')
if (type === 'integer') type = 'number'
if (type === 'file') type = 'string'
type = type[0].toUpperCase() + type.slice(1)
// 规则属性说明处理
if (p.type === 'string' && p.minLength && p.maxLength ) {
rule = `${p.minLength}-${p.maxLength}`
description = `${description}|长度限制: ${p.minLength}-${p.maxLength}`
} else if (p.type === 'string' && p.minLength && !p.maxLength) {
rule = `${p.minLength}`
description = `${description}|长度限制:最小值: ${p.minLength}`
} else if (p.type === 'string' && !p.minLength && p.maxLength) {
rule = `${p.required ? '1' : '0'}-${p.maxLength}`
description = `${description}|长度限制:最大值: ${p.maxLength}`
}
if (p.type === 'string' && p.enum && p.enum.length > 0) {
description = `${description}|枚举值: ${p.enum.join()}`
}
if ((p.type === 'integer' || p.type === 'number') && p.minimum && p.maxinum) {
rule = `${p.minimum}-${p.maxinum}`
description = `${description}|数据范围: ${p.minimum}-${p.maxinum}`
}
if ((p.type === 'integer' || p.type === 'number') && p.minimum && !p.maxinum) {
rule = `${p.minimum}`
description = `${description}|数据范围: 最小值:${p.minimum}`
}
if ((p.type === 'integer' || p.type === 'number') && !p.minimum && p.maxinum) {
rule = `${p.required ? '1' : '0'}-${p.maxinum}`
description = `${description}|数据范围: 最大值:${p.maxinum}`
}
// 默认值转化处理
value = p.default || ''
if (!p.default && p.type === 'string') value = '@ctitle'
if (!p.default && (p.type === 'number' || p.type === 'integer')) value = '@integer(0, 100000)'
if (p.type === 'boolean') { value = (p.default === true || p.default === false) ? p.default.toString() : 'false'}
if (p.enum && (p.enum || []).length > 0) value = p.enum[0]
if (p.type === 'string' && p.format === 'date-time') value = '@datetime'
if (p.type === 'string' && p.format === 'date') value = '@date'
if (p.type === 'array' && p.default) {
value = typeof(p.default) === 'object' ? JSON.stringify(p.default) : p.default.toString()
}
if (/^function/.test(value)) type = 'Function' // @mock=function(){} => Function
if (/^\$order/.test(value)) { // $order => Array|+1
type = 'Array'
rule = '+1'
let orderArgs = /\$order\((.+)\)/.exec(value)
if (orderArgs) value = `[${orderArgs[1]}]`
}
return {
type, rule, description: description.length > 0 ? description.substring(1) : '', value
}
}
const propertiesUpdateService = async (properties, itfId) => {
properties = Array.isArray(properties) ? properties : [properties]
let itf = await Interface.findByPk(itfId)
let existingProperties = properties.filter((item: any) => !item.memory)
let result = await Property.destroy({
where: {
id: { [Op.notIn]: existingProperties.map((item: any) => item.id) },
interfaceId: itfId
}
})
// 更新已存在的属性
for (let item of existingProperties) {
let affected = await Property.update(item, {
where: { id: item.id }
})
result += affected[0]
}
// 插入新增加的属性
let newProperties = properties.filter((item: any) => item.memory)
let memoryIdsMap: any = {}
for (let item of newProperties) {
let created = await Property.create(Object.assign({}, item, {
id: undefined,
parentId: -1,
priority: item.priority || Date.now()
}))
memoryIdsMap[item.id] = created.id
item.id = created.id
result += 1
}
// 同步 parentId
for (let item of newProperties) {
let parentId = memoryIdsMap[item.parentId] || item.parentId
await Property.update({ parentId }, {
where: { id: item.id }
})
}
itf = await Interface.findByPk(itfId, {
include: (QueryInclude.RepositoryHierarchy as any).include[0].include,
})
return {
data: {
result,
properties: itf.properties,
}
}
}
const sendMailTemplate = (changeTip) => {
let html = MailService.mailNoticeTemp
.replace('{=TITLE=}', '您相关的接口存在如下变更:(请注意代码是否要调整)')
.replace('{=CONTENT=}', (changeTip.split('<br/>') || []).map(one => {
return one ? `<li style="margin-bottom: 20px;">${one}</li>` : ''
}).join(''))
return html
}
export default class MigrateService {
public static async importRepoFromRAP1ProjectData(orgId: number, curUserId: number, projectData: any): Promise<boolean> {
if (!projectData || !projectData.id || !projectData.name) return false
@ -160,7 +396,6 @@ export default class MigrateService {
let description = []
if (p.name) description.push(p.name)
if (p.remark && remarkWithoutMock) description.push(remarkWithoutMock)
const pCreated = await Property.create({
scope,
name,
@ -226,12 +461,13 @@ export default class MigrateService {
}
/** 请求参对象->数组->标准树形对象 @param swagger @param parameters */
public static async swaggerToModelRequest(swagger: SwaggerData, parameters: Array<any>, method: string): Promise<any> {
const { definitions } = swagger
public static async swaggerToModelRequest(swagger: SwaggerData, parameters: Array<any>, method: string, apiInfo: any): Promise<any> {
let { definitions } = swagger
const result = []
definitions = JSON.parse(JSON.stringify(definitions)) // 防止接口之间数据处理相互影响
if (method === 'get' || method === 'GET') {
parse(parameters, 'root', result, definitions)
parse(parameters, 'root', 'root', 0, result, definitions, 'request', apiInfo)
} else if (method === 'post' || method === 'POST') {
let list = [] // 外层处理参数数据结果
const bodyObj = parameters.find(item => item.in === 'body') // body unique
@ -263,7 +499,7 @@ export default class MigrateService {
}
}
}
parse(list, 'root', result, definitions)
parse(list, 'root', 'root', 0, result, definitions, 'request', apiInfo)
}
const tree = arrayToTree(JSON.parse(JSON.stringify(result)))
@ -277,8 +513,10 @@ export default class MigrateService {
* @param swagger
* @param response
*/
public static async swaggerToModelRespnse (swagger: SwaggerData, response: object): Promise<any> {
const { definitions } = swagger
public static async swaggerToModelRespnse (swagger: SwaggerData, response: object, apiInfo: any): Promise<any> {
let { definitions = {} } = swagger
definitions = JSON.parse(JSON.stringify(definitions)) // 防止接口之间数据处理相互影响
const successObj = response['200']
if (!successObj) return []
@ -295,85 +533,62 @@ export default class MigrateService {
const properties = ref.properties
for (const key in properties) {
// 公共返回参数描述信息设置
let description = ''
if (!properties[key].description && key === 'errorCode') {
description = '错误码'
}
if (!properties[key].description && key === 'errorMessage') {
description = '错误描述'
}
if (!properties[key].description && key === 'success') {
description = '请求业务结果'
}
parameters.push({
name: key,
...properties[key],
in: 'body',
required: (ref.required || []).indexOf(key) >= 0
required: key === 'success' ? true : (ref.required || []).indexOf(key) >= 0,
default: key === 'success' ? true : (properties[key].default || false),
description: properties[key].description || description,
})
}
}
const result = []
parse(parameters, 'root', result, definitions)
parse(parameters, 'root', 'root', 0, result, definitions, 'response', apiInfo)
const tree = arrayToTree(JSON.parse(JSON.stringify(result)))
return tree
}
public static async importRepoFromSwaggerProjectData(repositoryId: number, curUserId: number, swagger: SwaggerData): Promise<boolean> {
public static async importRepoFromSwaggerProjectData(repositoryId: number, curUserId: number, swagger: SwaggerData): Promise<boolean> {
checkSwaggerResult = []
if (!swagger.paths || !swagger.swagger || !swagger.host) return false
let mCounter = 1 // 模块优先级顺序
let iCounter = 1 // 接口优先级顺序
let pCounter = 1 // 参数优先级顺序
/**
*
* @param p
* @param scope
* @param interfaceId
* @param moduleId
* @param parentId
*/
async function processParam(p: SwaggerParameter, scope: SCOPES, interfaceId: number, moduleId: number, parentId?: number, ) {
const name = p.name
let description = ''
// 规则转化处理
let rule = ''
if (p.type === 'string' && p.minLength && p.maxLength ) {
rule = `${p.minLength}-${p.maxLength}`
} else if (p.type === 'string' && p.minLength && !p.maxLength) {
rule = `${p.minLength}`
} else if (p.type === 'string' && !p.minLength && p.maxLength) {
rule = `${p.required ? '1' : '0'}-${p.maxLength}`
}
if (p.type === 'string' && p.enum && p.enum.length > 0) {
description = `${description} 枚举值: ${p.enum.join()}`
}
if (p.type === 'integer' && p.minimum && p.maxinum) {
rule = `${p.minimum}-${p.maxinum}`
}
if (p.type === 'integer' && p.minimum && !p.maxinum) {
rule = `${p.minimum}`
}
if (p.type === 'integer' && !p.minimum && p.maxinum) {
rule = `${p.required ? '1' : '0'}-${p.maxinum}`
}
// 类型转化处理
let type = (p.type || 'string')
if (type === 'integer') type = 'number'
type = type[0].toUpperCase() + type.slice(1) // foo => Foo 首字母转化为大写
// 默认值转化处理
let value = p.default || ''
if (p.type === 'boolean') {
value = (p.default === true || p.default === false) ? p.default.toString() : ''
}
if (p.type === 'array' && p.default) {
value = typeof(p.default) === 'object' ? JSON.stringify(p.default) : p.default.toString()
}
if (/^function/.test(value)) type = 'Function' // @mock=function(){} => Function
if (/^\$order/.test(value)) { // $order => Array|+1
type = 'Array'
rule = '+1'
let orderArgs = /\$order\((.+)\)/.exec(value)
if (orderArgs) value = `[${orderArgs[1]}]`
}
const { rule, value, type, description } = transformRapParams(p)
const joinDescription = `${p.description || ''}${((p.description || '') && (description || '')) ? '|' : ''}${description || ''}`
const pCreated = await Property.create({
scope,
name,
name: p.name,
rule,
value,
type,
required: p.required,
description: `${p.description || ''} ${description ? `|${description}` : ''}`,
description: joinDescription,
priority: pCounter++,
interfaceId: interfaceId,
creatorId: curUserId,
@ -392,7 +607,7 @@ export default class MigrateService {
let { tags = [], paths = {}, host = '' } = swagger
let pathTag: SwaggerTag[] = []
// 处理root tag中没有的情况
// 获取所有的TAG: 处理ROOT TAG中没有的情况
for (const action in paths) {
const apiObj = paths[action][Object.keys(paths[action])[0]]
const index = pathTag.findIndex((it: SwaggerTag) => {
@ -402,7 +617,11 @@ export default class MigrateService {
}
tags = pathTag
if (checkSwaggerResult.length > 0) return false
for (const tag of tags) {
if (checkSwaggerResult.length > 0) break
let repository: Partial<Repository>
let [repositoryModules] = await Promise.all([
Repository.findByPk(repositoryId, {
@ -436,12 +655,12 @@ export default class MigrateService {
} else {
mod = repository.modules[findIndex]
}
for (const action in paths) {
const apiObj = paths[action][Object.keys(paths[action])[0]]
const method = Object.keys(paths[action])[0]
const actionTags0 = apiObj.tags[0]
const url = action
const summary = apiObj.summary
if (actionTags0 === tag.name) {
// 判断接口是否存在在该模块中,如果不存在则创建接口,存在则更新接口信息
@ -464,8 +683,10 @@ export default class MigrateService {
...repositoryModules.toJSON()
}
const request = await this.swaggerToModelRequest(swagger, apiObj.parameters || {}, method)
const response = await this.swaggerToModelRespnse(swagger, apiObj.responses || {})
const request = await this.swaggerToModelRequest(swagger, apiObj.parameters || {}, method, { url, summary })
const response = await this.swaggerToModelRespnse(swagger, apiObj.responses || {}, { url, summary })
// 处理完每个接口请求参数后,如果-遇到第一个存在接口不符合规范就全部返回
if (checkSwaggerResult.length > 0) break
// 判断对应模块是否存在该接口
const index = repository.modules.findIndex(item => {
@ -504,18 +725,177 @@ export default class MigrateService {
method: method.toUpperCase()
}, { where: { id: findApi.id } })
await Property.destroy({ where: { interfaceId: findApi.id } })
// 获取已经存在的接口的属性信息并处理深度和parentName
let A_ExistsPropertiesOld = JSON.parse(JSON.stringify(findApi.properties))
A_ExistsPropertiesOld = JSON.parse(JSON.stringify(treeToArray((arrayToTreeProperties(A_ExistsPropertiesOld)))))
let A_ExistsProperties = A_ExistsPropertiesOld.map(property => {
return {
...property,
parentName: (A_ExistsPropertiesOld.find(item => item.id === property.parentId) || {}).name || 'root',
}
})
for (const p of (request.children || [])) {
await processParam(p, SCOPES.REQUEST, findApi.id, mod.id)
const B_SwaggerProperties_Request = treeToArray(request)
const B_SwaggerProperties_Response = treeToArray(response)
let PropertyId = 0, PriorityId = 0
let maxDepth_Request = 0, maxDepth_Response = 0, maxDepth_A_ExistsProperties = 0
// 计算B的最大深度-- request
B_SwaggerProperties_Request.map(item => {
if (item.depth > maxDepth_Request) {
maxDepth_Request = item.depth
}
return item
})
// 计算B的最大深度-- response
B_SwaggerProperties_Response.map(item => {
if (item.depth > maxDepth_Response) {
maxDepth_Response = item.depth
}
return item
})
// 计算A的最大深度
A_ExistsProperties.map(item => {
if (item.depth > maxDepth_A_ExistsProperties) {
maxDepth_A_ExistsProperties = item.depth
}
return item
})
const properties = []
/**
*
* @param BFilterByDepth
* @param depth
* @param scope
*/
const updateProperties = (BFilterByDepth, depth, scope) => {
for (const key in BFilterByDepth) {
const bValue = BFilterByDepth[key]
const index = A_ExistsProperties.findIndex(item => (item.name === bValue.name && item.depth === bValue.depth && item.parentName === bValue.parentName && item.scope === scope))
const { type, description, rule, value} = transformRapParams(bValue)
const joinDescription = `${bValue.description || ''}${((bValue.description || '') && (description || '')) ? '|' : ''}${description || ''}`
if (index >= 0) {
// 属性存在 ---修改:类型;是否必填;属性说明;不修改规则和默认值(前端可能正在mock)
// 如何判断有更新
if (type !== A_ExistsProperties[index].type) {
// 类型变更了
changeTip = `${changeTip}<br/>接口名称:${apiObj.summary} [更新属性:${A_ExistsProperties[index].name}类型由“${A_ExistsProperties[index].type}”变更为“${type}]”`
}
if (!!bValue.required !== A_ExistsProperties[index].required) {
// 是否必填变更
changeTip = `${changeTip}<br/>接口名称:${apiObj.summary} [更新属性:${A_ExistsProperties[index].name}是否必填由“${A_ExistsProperties[index].required}”变更为“${bValue.required}]”`
}
if (joinDescription !== A_ExistsProperties[index].description && bValue.name !== 'success' && bValue.name !== 'errorCode' && bValue.name !== 'errorMessage') {
// 描述信息变更
changeTip = `${changeTip}<br/>接口名称:${apiObj.summary} [更新属性:${A_ExistsProperties[index].name}属性简介由“${A_ExistsProperties[index].description || '无'}”变更为“${joinDescription}”]`
}
properties.push({
...A_ExistsProperties[index],
rule: (!A_ExistsProperties[index].rule && !A_ExistsProperties[index].value) ? rule : A_ExistsProperties[index].rule,
value: (!A_ExistsProperties[index].rule && !A_ExistsProperties[index].value) ? value : A_ExistsProperties[index].value,
type,
required: !!bValue.required, // 是否必填更改
description: `${joinDescription}`,
})
} else {
changeTip = `${changeTip}<br/>接口名称:${apiObj.summary} [属性添加:${bValue.name};类型:${type} ;简介: ${bValue.description || ''}${(bValue.description || '') ? '|' : ''}${description || ''} ]`
// 属性不存在
if (depth === 0) {
properties.push({
id: `memory-${++PropertyId}`,
scope,
type,
pos: REQUEST_TYPE_POS[bValue.in],
name: bValue.name,
rule,
value,
description: `${joinDescription}`,
parentId: -1 ,
priority: `${++PriorityId}`,
interfaceId: findApi.id,
moduleId: mod.id,
repositoryId,
memory: true,
depth: bValue.depth,
changeType: 'add',
})
} else {
// 找到父级属性信息
const parent = properties.find(it => (it.depth === bValue.depth - 1 && it.name === bValue.parentName && it.scope === scope))
properties.push({
id: `memory-${++PropertyId}`,
scope,
type,
pos: REQUEST_TYPE_POS[bValue.in],
name: bValue.name,
rule,
value,
description: `${joinDescription}`,
parentId: parent.id,
priority: `${++PriorityId}`,
interfaceId: findApi.id,
moduleId: mod.id,
repositoryId,
memory: true,
depth: bValue.depth,
changeType: 'add',
})
}
}
}
}
for (const p of (response.children || [])) {
await processParam(p, SCOPES.RESPONSE, findApi.id, mod.id)
/** 删除属性计算 */
const deleteProperties = (AFilterByDepth, scope) => {
for (const key in AFilterByDepth) {
const aValue = AFilterByDepth[key]
let index = -1
if (scope === 'request') {
index = B_SwaggerProperties_Request.findIndex(item => (item.name === aValue.name && item.depth === aValue.depth && item.parentName === aValue.parentName))
} else if (scope === 'response') {
index = B_SwaggerProperties_Response.findIndex(item => (item.name === aValue.name && item.depth === aValue.depth && item.parentName === aValue.parentName))
}
const { type, description } = transformRapParams(aValue)
if (index < 0) {
// A 存在B不存在
changeTip = `${changeTip} <br/> 接口名称:${apiObj.summary} [属性删除:${aValue.name};类型:${type} ;简介: ${aValue.description || ''} ${description ? `${aValue.description ? '|' : '' }${description}` : ''} ]`
}
}
}
for (let depth = 0; depth <= maxDepth_A_ExistsProperties; depth++) {
const AFilterByDepth = A_ExistsProperties.filter(item => item.depth === depth && item.scope === 'request')
deleteProperties(AFilterByDepth, 'request')
}
for (let depth = 0; depth <= maxDepth_A_ExistsProperties; depth++) {
const AFilterByDepth = A_ExistsProperties.filter(item => item.depth === depth && item.scope === 'response')
deleteProperties(AFilterByDepth, 'response')
}
for (let depth = 0; depth <= maxDepth_Request; depth++) {
const BFilterByDepth = B_SwaggerProperties_Request.filter(item => item.depth === depth)
updateProperties(BFilterByDepth, depth, 'request')
}
for (let depth = 0; depth <= maxDepth_Response; depth++) {
const BFilterByDepth = B_SwaggerProperties_Response.filter(item => item.depth === depth)
updateProperties(BFilterByDepth, depth, 'response')
}
await propertiesUpdateService(properties, findApi.id)
}
}
}
}
if (checkSwaggerResult.length > 0) return false
return true
}
@ -527,10 +907,21 @@ export default class MigrateService {
if (swagger.swagger === SWAGGER_VERSION[version]) {
let result
let mailRepositoryName = '', mailRepositoryId = 0, mailRepositoryMembers = []
if (mode === 'manual') {
const repos = await Repository.findByPk(repositoryId)
const repos = await Repository.findByPk(repositoryId, {
attributes: { exclude: [] },
include: [
QueryInclude.Creator,
QueryInclude.Owner,
QueryInclude.Members,
QueryInclude.Organization,
QueryInclude.Collaborators,
],
})
const { creatorId, members, collaborators, ownerId, name } = repos
const body = {
creatorId: creatorId,
organizationId: orgId,
@ -543,7 +934,12 @@ export default class MigrateService {
description: `[host=${host}]${info.title || ''}`,
}
result = await Repository.update(body, { where: { id: repositoryId } })
mailRepositoryName = name
mailRepositoryMembers = members
mailRepositoryId = repositoryId
} else if (mode === 'auto') {
// 团队下直接导入功能作废,此处不用执行
result = await Repository.create({
id: 0,
name: info.title || 'swagger导入仓库',
@ -562,13 +958,46 @@ export default class MigrateService {
if (result[0] || result.id) {
const bol = await this.importRepoFromSwaggerProjectData(mode === 'manual' ? repositoryId : result.id, curUserId, swagger)
await RedisService.delCache(CACHE_KEY.REPOSITORY_GET, result.id)
return { result: bol, code: 'success' }
if (!bol) {
return {result: checkSwaggerResult, code: 'checkSwagger'}
} else {
await RedisService.delCache(CACHE_KEY.REPOSITORY_GET, result.id)
if (changeTip.length > 0) {
const to = mailRepositoryMembers.map(item => {
return `"${item.fullname}" ${item.email},`
})
MailService.send(
to,
`仓库:${mailRepositoryName}(${mailRepositoryId})接口更新同步`,
sendMailTemplate(changeTip)
).then(() => {})
.catch(() => {})
// 钉钉消息发送
// const dingMsg = {
// msgtype: 'action_card',
// action_card: {
// title: `仓库:${mailRepositoryName}(${mailRepositoryId})接口更新同步`,
// markdown: "支持markdown格式的正文内容",
// single_title: "查看仓库更新", // swagger 批量导入跳转至仓库, 如果后期只要接口更新就通知相关人的话,需要设置具体接口链接
// single_url: `https://rap2.alibaba-inc.com/repository/editor?id=${repositoryId}`
// }
// }
// DingPushService.dingPush(mailRepositoryMembers.map(item => item.empId).join(), dingMsg)
// .catch((err) => { console.log(err) })
}
changeTip = ''
return { result: bol, code: 'success' }
}
}
} else {
return { result: true, code: 'version'}
}
} catch (err) {
console.log(err)
return { result: false, code: 'error'}
}
}
@ -746,6 +1175,8 @@ interface OldParameter {
remark: string
dataType: string
parameterList: OldParameter[]
parentName: string,
depth: number
}
interface SwaggerParameter {

@ -7,7 +7,7 @@ export default class OrganizationService {
SELECT COUNT(id) AS num FROM (
SELECT o.id, o.name
FROM Organizations o
WHERE visibility = ${1} OR creatorId = ${userId} OR ownerId = ${userId}
WHERE ownerId = ${userId}
UNION
SELECT o.id, o.name
FROM Organizations o
@ -31,7 +31,7 @@ export default class OrganizationService {
SELECT id FROM (
SELECT o.id, o.name
FROM Organizations o
WHERE visibility = ${1} OR creatorId = ${curUserId} OR ownerId = ${curUserId}
WHERE visibility = ${1} OR ownerId = ${curUserId}
UNION
SELECT o.id, o.name
FROM Organizations o
@ -54,7 +54,7 @@ export default class OrganizationService {
SELECT count(*) AS num FROM (
SELECT o.id, o.name
FROM Organizations o
WHERE visibility = ${1} OR creatorId = ${curUserId} OR ownerId = ${curUserId}
WHERE visibility = ${1} OR ownerId = ${curUserId}
UNION
SELECT o.id, o.name
FROM Organizations o

@ -13,7 +13,7 @@ export default class RepositoryService {
const repo = await Repository.findByPk(repositoryId)
if (token && repo.token === token) return true
if (!repo) return false
if (repo.creatorId === userId || repo.ownerId === userId) return true
if (repo.ownerId === userId) return true
const memberExistsNum = await RepositoriesMembers.count({
where: {
userId,
@ -31,16 +31,16 @@ export default class RepositoryService {
destModuleId: number,
) {
return (
AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE, userId, itfId) &&
AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, userId, destRepoId) &&
AccessUtils.canUserAccess(ACCESS_TYPE.MODULE, userId, destModuleId)
AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_GET, userId, itfId) &&
AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, userId, destRepoId) &&
AccessUtils.canUserAccess(ACCESS_TYPE.MODULE_SET, userId, destModuleId)
)
}
public static async canUserMoveModule(userId: number, modId: number, destRepoId: number) {
return (
AccessUtils.canUserAccess(ACCESS_TYPE.MODULE, userId, modId) &&
AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY, userId, destRepoId)
AccessUtils.canUserAccess(ACCESS_TYPE.MODULE_GET, userId, modId) &&
AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, userId, destRepoId)
)
}

Loading…
Cancel
Save