fix #58 add TypeScript to delos!
parent
8dcdfb03dc
commit
90314eba58
@ -1,5 +0,0 @@
|
||||
// local or development or production
|
||||
module.exports =
|
||||
(process.env.NODE_ENV === 'local' && require('./config.local')) ||
|
||||
(process.env.NODE_ENV === 'development' && require('./config.dev')) ||
|
||||
require('./config.prod')
|
@ -0,0 +1,9 @@
|
||||
import { IConfigOptions } from "../types";
|
||||
|
||||
// local or development or production
|
||||
let configObj:IConfigOptions =
|
||||
(process.env.NODE_ENV === 'local' && require('./config.local')).default ||
|
||||
(process.env.NODE_ENV === 'development' && require('./config.dev')).default ||
|
||||
require('./config.prod').default
|
||||
|
||||
export default configObj
|
@ -1,12 +1,6 @@
|
||||
// process.env.NODE_ENV = 'production'
|
||||
import * as cluster from 'cluster'
|
||||
import * as path from 'path'
|
||||
|
||||
// http://gitlab.alibaba-inc.com/thx/rap2-dolores/commit/2fd70fdcaa9d179e9cf95e530e37f31f8488f432
|
||||
// https://nodejs.org/api/cluster.html
|
||||
// https://github.com/node-modules/graceful/blob/master/example/express_with_cluster/dispatch.js
|
||||
// http://gitlab.alibaba-inc.com/mm/fb/blob/master/dispatch.js
|
||||
|
||||
let cluster = require('cluster')
|
||||
let path = require('path')
|
||||
let now = () => new Date().toISOString().replace(/T/, ' ').replace(/Z/, '')
|
||||
|
||||
cluster.setupMaster({
|
@ -1,5 +1,5 @@
|
||||
const Sequelize = require('sequelize')
|
||||
module.exports = {
|
||||
export let Helper = {
|
||||
id: { type: Sequelize.BIGINT(11).UNSIGNED, primaryKey: true, allowNull: false, autoIncrement: true, comment: '唯一标识' },
|
||||
include: [],
|
||||
exclude: {
|
@ -0,0 +1,11 @@
|
||||
import { User } from '.';
|
||||
|
||||
export { QueryInclude } from './queryInclude'
|
||||
export { Interface } from './interface'
|
||||
export { Logger } from './logger'
|
||||
export { Module } from './module'
|
||||
export { Notification } from './notification'
|
||||
export { Organization } from './organization'
|
||||
export { Property } from './property'
|
||||
export { Repository } from './repository'
|
||||
export { User } from './user'
|
@ -1,16 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const sequelize = require('./sequelize')
|
||||
const { id } = require('./helper')
|
||||
const methods = ['GET', 'POST', 'PUT', 'DELETE']
|
||||
module.exports = sequelize.define('interface', {
|
||||
id,
|
||||
name: { type: Sequelize.STRING(256), allowNull: false, comment: '接口名称' },
|
||||
url: { type: Sequelize.STRING(256), allowNull: false, comment: '接口地址' },
|
||||
method: { type: Sequelize.ENUM(...methods), allowNull: false, comment: '接口类型' },
|
||||
description: { type: Sequelize.TEXT, allowNull: true, comment: '接口描述' },
|
||||
// DONE 2.2 支持接口排序
|
||||
priority: { type: Sequelize.BIGINT(11).UNSIGNED, allowNull: false, defaultValue: 1, comment: '接口优先级' }
|
||||
}, {
|
||||
paranoid: true,
|
||||
comment: '接口'
|
||||
})
|
@ -0,0 +1,70 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-typescript'
|
||||
import { User, Module, Repository, Property } from './index';
|
||||
|
||||
enum methods { GET= 'GET', POST= 'POST', PUT= 'PUT', DELETE= 'DELETE' }
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Interface extends Model<Interface> {
|
||||
|
||||
public static METHODS= methods
|
||||
|
||||
public request?:object
|
||||
public response?:object
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(256))
|
||||
name: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(256))
|
||||
url: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Column({ type: DataType.ENUM(methods.GET, methods.POST, methods.PUT, methods.DELETE), comment: 'API method' })
|
||||
method: string
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
description
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(1)
|
||||
@Column({ type: DataType.BIGINT(11).UNSIGNED, comment: 'Priority used for ordering' })
|
||||
priority: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
creatorId: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
lockerId: number
|
||||
|
||||
@ForeignKey(() => Module)
|
||||
@Column
|
||||
moduleId: number
|
||||
|
||||
@ForeignKey(() => Repository)
|
||||
@Column
|
||||
repositoryId: number
|
||||
|
||||
@BelongsTo(() => User, 'creatorId')
|
||||
creator: User
|
||||
|
||||
@BelongsTo(() => User, 'lockerId')
|
||||
locker: User
|
||||
|
||||
@BelongsTo(() => Module, 'moduleId')
|
||||
module: Module
|
||||
|
||||
@BelongsTo(() => Repository, 'repositoryId')
|
||||
repository: Repository
|
||||
|
||||
@HasMany(() => Property, 'interfaceId')
|
||||
properties: Property[]
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-typescript'
|
||||
import { User, Repository, Organization, Module, Interface } from './index'
|
||||
|
||||
enum types {
|
||||
CREATE = 'create', UPDATE = 'update', DELETE = 'delete',
|
||||
LOCK = 'lock', UNLOCK = 'unlock', JOIN = 'join', EXIT = 'exit'
|
||||
}
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Logger extends Model<Logger> {
|
||||
public static TYPES = types
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column({
|
||||
type: DataType.ENUM(types.CREATE, types.UPDATE, types.DELETE, types.LOCK, types.UNLOCK, types.JOIN, types.EXIT),
|
||||
comment: 'operation type'
|
||||
})
|
||||
type: string
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
creatorId: number
|
||||
|
||||
@AllowNull(false)
|
||||
@ForeignKey(() => User)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
userId: number
|
||||
|
||||
@ForeignKey(() => Organization)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
organizationId: number
|
||||
|
||||
@ForeignKey(() => Repository)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
repositoryId: number
|
||||
|
||||
@ForeignKey(() => Module)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
moduleId: number
|
||||
|
||||
@ForeignKey(() => Interface)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
interfaceId: number
|
||||
|
||||
@BelongsTo(() => User, 'creatorId')
|
||||
creator: User
|
||||
|
||||
@BelongsTo(() => User, 'userId')
|
||||
user: User
|
||||
|
||||
@BelongsTo(() => Repository, 'repositoryId')
|
||||
repository: Repository
|
||||
|
||||
@BelongsTo(() => Organization, 'organizationId')
|
||||
organization: Organization
|
||||
|
||||
@BelongsTo(() => Module, 'moduleId')
|
||||
module: Module
|
||||
|
||||
@BelongsTo(() => Interface, 'interfaceId')
|
||||
interface: Interface
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const sequelize = require('./sequelize')
|
||||
const { id } = require('./helper')
|
||||
module.exports = sequelize.define('module', {
|
||||
id,
|
||||
name: { type: Sequelize.STRING(256), allowNull: false, comment: '模块名称' },
|
||||
description: { type: Sequelize.TEXT, comment: '模块描述' },
|
||||
priority: { type: Sequelize.BIGINT(11).UNSIGNED, allowNull: false, defaultValue: 1, comment: '接口优先级' }
|
||||
}, {
|
||||
paranoid: true,
|
||||
comment: '模块'
|
||||
})
|
@ -0,0 +1,42 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-typescript'
|
||||
import {User, Repository, Interface} from './index'
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Module extends Model<Module> {
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(256))
|
||||
name: string
|
||||
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.TEXT)
|
||||
description: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(1)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
priority: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
creatorId: number
|
||||
|
||||
@ForeignKey(() => Repository)
|
||||
@Column
|
||||
repositoryId: number
|
||||
|
||||
@BelongsTo(() => User, 'creatorId')
|
||||
creator: User
|
||||
|
||||
@BelongsTo(() => Repository, 'repositoryId')
|
||||
repository: Repository
|
||||
|
||||
@HasMany(() => Interface, 'moduleId')
|
||||
interfaces: Interface[]
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const sequelize = require('./sequelize')
|
||||
const { id } = require('./helper')
|
||||
module.exports = sequelize.define('notification', {
|
||||
id,
|
||||
fromId: { type: Sequelize.BIGINT(11), allowNull: true, comment: '发送者' },
|
||||
toId: { type: Sequelize.BIGINT(11), allowNull: false, comment: '接受者' },
|
||||
type: { type: Sequelize.STRING(128), allowNull: false, comment: '消息类型' },
|
||||
param1: { type: Sequelize.STRING(128), allowNull: true, comment: '参数1' },
|
||||
param2: { type: Sequelize.STRING(128), allowNull: true, comment: '参数2' },
|
||||
param3: { type: Sequelize.STRING(128), allowNull: true, comment: '参数3' },
|
||||
readed: { type: Sequelize.BOOLEAN, allowNull: false, defautValue: false, comment: '是否已读' }
|
||||
}, {
|
||||
paranoid: true,
|
||||
comment: '消息'
|
||||
})
|
@ -0,0 +1,35 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default } from 'sequelize-typescript'
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Notification extends Model<Notification> {
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@Column({ type: DataType.BIGINT(11).UNSIGNED, comment: 'sender' })
|
||||
fromId: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column({ type: DataType.BIGINT(11).UNSIGNED, comment: 'receiver' })
|
||||
toId: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column({ type: DataType.STRING(128), comment: 'msg type' })
|
||||
type: string
|
||||
|
||||
@Column(DataType.STRING(128))
|
||||
param1: string
|
||||
|
||||
@Column(DataType.STRING(128))
|
||||
param2: string
|
||||
|
||||
@Column(DataType.STRING(128))
|
||||
param3: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(false)
|
||||
@Column
|
||||
readed: boolean
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const sequelize = require('./sequelize')
|
||||
const { id } = require('./helper')
|
||||
module.exports = sequelize.define('organization', {
|
||||
id,
|
||||
name: { type: Sequelize.STRING(256), allowNull: false, comment: '团队名称' },
|
||||
description: { type: Sequelize.TEXT, allowNull: true, comment: '团队描述' },
|
||||
logo: { type: Sequelize.STRING(256), allowNull: true, comment: '团队标志' },
|
||||
visibility: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true, comment: '是否公开' }
|
||||
}, {
|
||||
paranoid: true,
|
||||
comment: '团队'
|
||||
})
|
@ -0,0 +1,46 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, BelongsToMany, ForeignKey } from 'sequelize-typescript'
|
||||
import { User, Repository } from './index'
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Organization extends Model<Organization> {
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(256))
|
||||
name: string
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
description: string
|
||||
|
||||
@Column(DataType.STRING(256))
|
||||
logo: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(true)
|
||||
@Column({ comment: 'true:public, false:private' })
|
||||
visibility: boolean
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
creatorId: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
ownerId: number
|
||||
|
||||
@BelongsTo(() => User, 'creatorId')
|
||||
creator: User
|
||||
|
||||
@BelongsTo(() => User, 'ownerId')
|
||||
owner: User
|
||||
|
||||
@BelongsToMany(() => User, 'organizations_members', 'organizationId', 'userId')
|
||||
members: User[]
|
||||
|
||||
@HasMany(() => Repository, 'organizationId')
|
||||
repositories: Repository[]
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const sequelize = require('./sequelize')
|
||||
const { id } = require('./helper')
|
||||
const SCOPES = ['request', 'response']
|
||||
const TYPES = ['String', 'Number', 'Boolean', 'Object', 'Array', 'Function', 'RegExp']
|
||||
module.exports = sequelize.define('property', {
|
||||
id,
|
||||
scope: { type: Sequelize.ENUM(...SCOPES), allowNull: false, defaultValue: 'response', comment: '属性归属' },
|
||||
name: { type: Sequelize.STRING(256), allowNull: false, comment: '属性名称' },
|
||||
type: { type: Sequelize.ENUM(...TYPES), allowNull: false, comment: '属性值类型' },
|
||||
rule: { type: Sequelize.STRING(128), allowNull: true, comment: '属性值生成规则' },
|
||||
value: { type: Sequelize.TEXT, allowNull: true, comment: '属性值' },
|
||||
description: { type: Sequelize.TEXT, allowNull: true, comment: '属性描述' },
|
||||
parentId: { type: Sequelize.BIGINT(11), allowNull: false, defaultValue: -1, comment: '父属性' },
|
||||
priority: { type: Sequelize.BIGINT(11).UNSIGNED, allowNull: false, defaultValue: 1, comment: '接口优先级' }
|
||||
}, {
|
||||
paranoid: true,
|
||||
comment: '属性'
|
||||
})
|
@ -0,0 +1,86 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-TYPEScript'
|
||||
import { User, Interface, Module, Repository } from './index'
|
||||
|
||||
enum SCOPES { REQUEST = 'request', RESPONSE = 'response' }
|
||||
enum TYPES { STRING = 'String', NUMBER = 'Number', BOOLEAN = 'Boolean', OBJECT = 'Object', ARRAY = 'Array', FUNCTION = 'Function', REGEXP = 'RegExp' }
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Property extends Model<Property> {
|
||||
public static TYPES = TYPES
|
||||
public static SCOPES = SCOPES
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
static attributes: any;
|
||||
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(SCOPES.RESPONSE)
|
||||
@Column({
|
||||
type: DataType.ENUM(SCOPES.REQUEST, SCOPES.RESPONSE),
|
||||
comment: 'property owner'
|
||||
})
|
||||
scope: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Column({
|
||||
type: DataType.ENUM(TYPES.STRING, TYPES.NUMBER, TYPES.BOOLEAN, TYPES.OBJECT, TYPES.ARRAY, TYPES.FUNCTION, TYPES.REGEXP),
|
||||
comment: 'property type'
|
||||
})
|
||||
type: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(256))
|
||||
name: string
|
||||
|
||||
@Column({ type: DataType.STRING(128), comment: 'property generation rules' })
|
||||
rule: string
|
||||
|
||||
@Column({ type: DataType.TEXT, comment: 'value of this property'})
|
||||
value: string
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
description: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(-1)
|
||||
@Column({ type: DataType.BIGINT(11), comment: 'parent property ID' })
|
||||
parentId: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(1)
|
||||
@Column(DataType.BIGINT(11).UNSIGNED)
|
||||
priority: number
|
||||
|
||||
@ForeignKey(() => Interface)
|
||||
@Column
|
||||
interfaceId: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
creatorId: number
|
||||
|
||||
@ForeignKey(() => Module)
|
||||
@Column
|
||||
moduleId: number
|
||||
|
||||
@ForeignKey(() => Repository)
|
||||
@Column
|
||||
repositoryId: number
|
||||
|
||||
@BelongsTo(() => User, 'creatorId')
|
||||
creator: User
|
||||
|
||||
@BelongsTo(() => Interface, 'interfaceId')
|
||||
interface: Interface
|
||||
|
||||
@BelongsTo(() => Module, 'moduleId')
|
||||
module: Module
|
||||
|
||||
@BelongsTo(() => Repository, 'repositoryId')
|
||||
repository: Repository
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const sequelize = require('./sequelize')
|
||||
const { id } = require('./helper')
|
||||
module.exports = sequelize.define('repository', {
|
||||
id,
|
||||
name: { type: Sequelize.STRING(256), allowNull: false, comment: '仓库名称' },
|
||||
description: { type: Sequelize.TEXT, allowNull: true, comment: '仓库描述' },
|
||||
logo: { type: Sequelize.STRING(256), allowNull: true, comment: '仓库标志' },
|
||||
visibility: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true, comment: '是否公开' }
|
||||
}, {
|
||||
paranoid: true,
|
||||
comment: '仓库'
|
||||
})
|
@ -0,0 +1,67 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, BelongsToMany, ForeignKey } from 'sequelize-typescript'
|
||||
import { User, Organization, Module, Interface } from './index'
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class Repository extends Model<Repository> {
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(256))
|
||||
name: string
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
description: string
|
||||
|
||||
@Column(DataType.STRING(256))
|
||||
logo: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(true)
|
||||
@Column({ comment: 'true:public, false:private' })
|
||||
visibility: boolean
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
ownerId: number
|
||||
|
||||
@ForeignKey(() => Organization)
|
||||
@Column
|
||||
organizationId: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
creatorId: number
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
lockerId: number
|
||||
|
||||
@BelongsTo(() => User, 'creatorId')
|
||||
creator: User
|
||||
|
||||
@BelongsTo(() => User, 'ownerId')
|
||||
owner: User
|
||||
|
||||
@BelongsTo(() => Organization, 'organizationId')
|
||||
organization: Organization
|
||||
|
||||
@BelongsTo(() => User, 'lockerId')
|
||||
locker: User
|
||||
|
||||
@BelongsToMany(() => User, 'repositories_members', 'repositoryId', 'userId')
|
||||
members: User[]
|
||||
|
||||
@HasMany(() => Module, 'repositoryId')
|
||||
modules: Module[]
|
||||
|
||||
@HasMany(() => Module, 'repositoryId')
|
||||
interfaces: Interface[]
|
||||
|
||||
@BelongsToMany(() => Repository, 'repositories_collaborators', 'repositoryId', 'collaboratorId')
|
||||
collaborators: Repository[]
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
import { Sequelize } from 'sequelize-typescript'
|
||||
import { Interface, Logger, Module, Notification, Organization, Property, User, Repository } from './index'
|
||||
import config from '../config'
|
||||
|
||||
const chalk = require('chalk')
|
||||
const now = () => new Date().toISOString().replace(/T/, ' ').replace(/Z/, '')
|
||||
const logging = process.env.NODE_ENV === 'development'
|
||||
? (sql) => {
|
||||
sql = sql.replace('Executing (default): ', '')
|
||||
console.log(`${chalk.bold('SQL')} ${now()} ${chalk.gray(sql)}`)
|
||||
}
|
||||
: console.log
|
||||
|
||||
const sequelize = new Sequelize({
|
||||
database: config.db.database,
|
||||
dialect: config.db.dialect,
|
||||
username: config.db.username,
|
||||
password: config.db.password,
|
||||
host: config.db.host,
|
||||
port: config.db.port,
|
||||
pool: config.db.pool,
|
||||
logging: config.db.logging ? logging : false
|
||||
})
|
||||
|
||||
sequelize.addModels([Interface, Logger, Module, Notification, Organization, Property, Repository, User])
|
||||
sequelize.authenticate()
|
||||
.then((/* err */) => {
|
||||
|
||||
// initialize hooks
|
||||
Organization.hook('afterCreate', async(instance, options) => {
|
||||
await Logger.create({
|
||||
userId: instance.creatorId,
|
||||
type: 'create',
|
||||
organizationId: instance.id
|
||||
})
|
||||
})
|
||||
console.log('----------------------------------------')
|
||||
console.log('DATABASE √')
|
||||
console.log(' HOST %s', config.db.host)
|
||||
console.log(' PORT %s', config.db.port)
|
||||
console.log(' DATABASE %s', config.db.database)
|
||||
console.log('----------------------------------------')
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Unable to connect to the database:', err)
|
||||
})
|
||||
|
||||
module.exports = sequelize
|
@ -0,0 +1,36 @@
|
||||
import { Sequelize, Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, Unique, ForeignKey, BelongsToMany } from 'sequelize-typescript'
|
||||
import { Organization, Repository } from './index'
|
||||
|
||||
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
|
||||
export class User extends Model<User> {
|
||||
|
||||
@AutoIncrement
|
||||
@PrimaryKey
|
||||
@Column
|
||||
id: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column(DataType.STRING(32))
|
||||
fullname: string
|
||||
|
||||
@Column(DataType.STRING(32))
|
||||
password: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Unique
|
||||
@Column(DataType.STRING(128))
|
||||
email: string
|
||||
|
||||
@HasMany(() => Organization, 'ownerId')
|
||||
ownedOrganizations: Organization[]
|
||||
|
||||
@BelongsToMany(() => Organization, 'organizations_members', 'organizationId', 'userId')
|
||||
joinedOrganizations: Organization[]
|
||||
|
||||
@HasMany(() => Repository, 'ownerId')
|
||||
ownedRepositories: Repository[]
|
||||
|
||||
@BelongsToMany(() => Repository, 'repositories_members', 'userId', 'repositoryId')
|
||||
joinedRepositories: Repository[]
|
||||
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
const vm = require('vm')
|
||||
const _ = require('underscore')
|
||||
const Mock = require('mockjs')
|
||||
const { RE_KEY } = require('mockjs/src/mock/constant')
|
||||
|
||||
const Tree = {}
|
||||
|
||||
Tree.ArrayToTree = (list) => {
|
||||
let result = {
|
||||
name: 'root',
|
||||
children: [],
|
||||
depth: 0
|
||||
}
|
||||
|
||||
let mapped = {}
|
||||
list.forEach(item => { mapped[item.id] = item })
|
||||
|
||||
function _parseChildren (parentId, children, depth) {
|
||||
for (let id in mapped) {
|
||||
let item = mapped[id]
|
||||
if (typeof parentId === 'function' ? parentId(item.parentId) : item.parentId === parentId) {
|
||||
children.push(item)
|
||||
item.depth = depth + 1
|
||||
item.children = _parseChildren(item.id, [], item.depth)
|
||||
}
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
_parseChildren(
|
||||
(parentId) => {
|
||||
// 忽略 parentId 为 0 的根属性(历史遗留),现为 -1
|
||||
if (parentId === -1) return true
|
||||
},
|
||||
result.children,
|
||||
result.depth
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO 2.x 和前端重复了
|
||||
Tree.TreeToTemplate = (tree) => {
|
||||
function parse (item, result) {
|
||||
let rule = item.rule ? ('|' + item.rule) : ''
|
||||
let value = item.value
|
||||
switch (item.type) {
|
||||
case 'String':
|
||||
result[item.name + rule] = item.value
|
||||
break
|
||||
case 'Number':
|
||||
if (value === '') value = 1
|
||||
let parsed = parseFloat(value)
|
||||
if (!isNaN(parsed)) value = parsed
|
||||
result[item.name + rule] = value
|
||||
break
|
||||
case 'Boolean':
|
||||
if (value === 'true') value = true
|
||||
if (value === 'false') value = false
|
||||
if (value === '0') value = false
|
||||
value = !!value
|
||||
result[item.name + rule] = value
|
||||
break
|
||||
case 'Function':
|
||||
case 'RegExp':
|
||||
try {
|
||||
result[item.name + rule] = eval('(' + item.value + ')') // eslint-disable-line no-eval
|
||||
} catch (e) {
|
||||
console.warn(`TreeToTemplate ${e.message}: ${item.type} { ${item.name}${rule}: ${item.value} }`) // TODO 2.2 怎么消除异常值?
|
||||
result[item.name + rule] = item.value
|
||||
}
|
||||
break
|
||||
case 'Object':
|
||||
if (item.value) {
|
||||
try {
|
||||
result[item.name + rule] = eval(`(${item.value})`) // eslint-disable-line no-eval
|
||||
} catch (e) {
|
||||
result[item.name + rule] = item.value
|
||||
}
|
||||
} else {
|
||||
result[item.name + rule] = {}
|
||||
item.children.forEach((child) => {
|
||||
parse(child, result[item.name + rule])
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'Array':
|
||||
if (item.value) {
|
||||
try {
|
||||
result[item.name + rule] = eval(`(${item.value})`) // eslint-disable-line no-eval
|
||||
} catch (e) {
|
||||
result[item.name + rule] = item.value
|
||||
}
|
||||
} else {
|
||||
result[item.name + rule] = item.children.length ? [{}] : []
|
||||
item.children.forEach((child) => {
|
||||
parse(child, result[item.name + rule][0])
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
let result = {}
|
||||
tree.children.forEach((child) => {
|
||||
parse(child, result)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
Tree.TemplateToData = (template) => {
|
||||
// 数据模板 template 中可能含有攻击代码,例如死循环,所以在沙箱中生成最终数据
|
||||
// https://nodejs.org/dist/latest-v7.x/docs/api/vm.html
|
||||
const sandbox = { Mock, template, data: {} }
|
||||
const script = new vm.Script('data = Mock.mock(template)')
|
||||
const context = new vm.createContext(sandbox) // eslint-disable-line new-cap
|
||||
try {
|
||||
script.runInContext(context, { timeout: 1000 }) // 每次 Mock.mock() 最多执行 1s
|
||||
// DONE 2.1 __root__
|
||||
let data = sandbox.data
|
||||
let keys = Object.keys(data)
|
||||
if (keys.length === 1 && keys[0] === '__root__') data = data.__root__
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
Tree.ArrayToTreeToTemplate = (list) => {
|
||||
let tree = Tree.ArrayToTree(list)
|
||||
let template = Tree.TreeToTemplate(tree)
|
||||
return template
|
||||
}
|
||||
|
||||
Tree.ArrayToTreeToTemplateToData = (list, extra) => {
|
||||
let tree = Tree.ArrayToTree(list)
|
||||
let template = Tree.TreeToTemplate(tree)
|
||||
let data
|
||||
|
||||
if (extra) {
|
||||
// DONE 2.2 支持引用请求参数
|
||||
let keys = Object.keys(template).map(item => item.replace(RE_KEY, '$1'))
|
||||
let extraKeys = _.difference(Object.keys(extra), keys)
|
||||
let scopedData = Tree.TemplateToData(
|
||||
Object.assign({}, _.pick(extra, extraKeys), template)
|
||||
)
|
||||
data = _.pick(scopedData, keys)
|
||||
} else {
|
||||
data = Tree.TemplateToData(template)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
Tree.ArrayToTreeToTemplateToJSONSchema = (list) => {
|
||||
let tree = Tree.ArrayToTree(list)
|
||||
let template = Tree.TreeToTemplate(tree)
|
||||
let schema = Mock.toJSONSchema(template)
|
||||
return schema
|
||||
}
|
||||
|
||||
// TODO 2.2 执行 JSON.stringify() 序列化时会丢失正则和函数。需要转为字符串或者函数。
|
||||
// X Function.protytype.toJSON = Function.protytype.toString
|
||||
// X RegExp.protytype.toJSON = RegExp.protytype.toString
|
||||
Tree.stringifyWithFunctonAndRegExp = (json) => {
|
||||
return JSON.stringify(json, (k, v) => {
|
||||
if (typeof v === 'function') return v.toString()
|
||||
if (v !== undefined && v !== null && v.exec) return v.toString()
|
||||
else return v
|
||||
}, 2)
|
||||
}
|
||||
|
||||
module.exports = Tree
|
@ -0,0 +1,174 @@
|
||||
const vm = require('vm')
|
||||
const _ = require('underscore')
|
||||
const Mock = require('mockjs')
|
||||
const { RE_KEY } = require('mockjs/src/mock/constant')
|
||||
|
||||
|
||||
export default class Tree {
|
||||
|
||||
public static ArrayToTree (list) {
|
||||
let result = {
|
||||
name: 'root',
|
||||
children: [],
|
||||
depth: 0
|
||||
}
|
||||
|
||||
let mapped = {}
|
||||
list.forEach(item => { mapped[item.id] = item })
|
||||
|
||||
function _parseChildren (parentId, children, depth) {
|
||||
for (let id in mapped) {
|
||||
let item = mapped[id]
|
||||
if (typeof parentId === 'function' ? parentId(item.parentId) : item.parentId === parentId) {
|
||||
children.push(item)
|
||||
item.depth = depth + 1
|
||||
item.children = _parseChildren(item.id, [], item.depth)
|
||||
}
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
_parseChildren(
|
||||
(parentId) => {
|
||||
// 忽略 parentId 为 0 的根属性(历史遗留),现为 -1
|
||||
if (parentId === -1) return true
|
||||
},
|
||||
result.children,
|
||||
result.depth
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO 2.x 和前端重复了
|
||||
public static TreeToTemplate (tree) {
|
||||
function parse (item, result) {
|
||||
let rule = item.rule ? ('|' + item.rule) : ''
|
||||
let value = item.value
|
||||
switch (item.type) {
|
||||
case 'String':
|
||||
result[item.name + rule] = item.value
|
||||
break
|
||||
case 'Number':
|
||||
if (value === '') value = 1
|
||||
let parsed = parseFloat(value)
|
||||
if (!isNaN(parsed)) value = parsed
|
||||
result[item.name + rule] = value
|
||||
break
|
||||
case 'Boolean':
|
||||
if (value === 'true') value = true
|
||||
if (value === 'false') value = false
|
||||
if (value === '0') value = false
|
||||
value = !!value
|
||||
result[item.name + rule] = value
|
||||
break
|
||||
case 'Function':
|
||||
case 'RegExp':
|
||||
try {
|
||||
result[item.name + rule] = eval('(' + item.value + ')') // eslint-disable-line no-eval
|
||||
} catch (e) {
|
||||
console.warn(`TreeToTemplate ${e.message}: ${item.type} { ${item.name}${rule}: ${item.value} }`) // TODO 2.2 怎么消除异常值?
|
||||
result[item.name + rule] = item.value
|
||||
}
|
||||
break
|
||||
case 'Object':
|
||||
if (item.value) {
|
||||
try {
|
||||
result[item.name + rule] = eval(`(${item.value})`) // eslint-disable-line no-eval
|
||||
} catch (e) {
|
||||
result[item.name + rule] = item.value
|
||||
}
|
||||
} else {
|
||||
result[item.name + rule] = {}
|
||||
item.children.forEach((child) => {
|
||||
parse(child, result[item.name + rule])
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'Array':
|
||||
if (item.value) {
|
||||
try {
|
||||
result[item.name + rule] = eval(`(${item.value})`) // eslint-disable-line no-eval
|
||||
} catch (e) {
|
||||
result[item.name + rule] = item.value
|
||||
}
|
||||
} else {
|
||||
result[item.name + rule] = item.children.length ? [{}] : []
|
||||
item.children.forEach((child) => {
|
||||
parse(child, result[item.name + rule][0])
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
let result = {}
|
||||
tree.children.forEach((child) => {
|
||||
parse(child, result)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
public static TemplateToData (template) {
|
||||
// 数据模板 template 中可能含有攻击代码,例如死循环,所以在沙箱中生成最终数据
|
||||
// https://nodejs.org/dist/latest-v7.x/docs/api/vm.html
|
||||
const sandbox = { Mock, template, data: {} }
|
||||
const script = new vm.Script('data = Mock.mock(template)')
|
||||
const context = new vm.createContext(sandbox) // eslint-disable-line new-cap
|
||||
try {
|
||||
script.runInContext(context, { timeout: 1000 }) // 每次 Mock.mock() 最多执行 1s
|
||||
// DONE 2.1 __root__
|
||||
let data:any = sandbox.data
|
||||
let keys = Object.keys(data)
|
||||
if (keys.length === 1 && keys[0] === '__root__') data = data.__root__
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
public static ArrayToTreeToTemplate(list) {
|
||||
let tree = Tree.ArrayToTree(list)
|
||||
let template = Tree.TreeToTemplate(tree)
|
||||
return template
|
||||
}
|
||||
|
||||
public static ArrayToTreeToTemplateToData(list, extra?:any) {
|
||||
let tree = Tree.ArrayToTree(list)
|
||||
let template = Tree.TreeToTemplate(tree)
|
||||
let data
|
||||
|
||||
if (extra) {
|
||||
// DONE 2.2 支持引用请求参数
|
||||
let keys = Object.keys(template).map(item => item.replace(RE_KEY, '$1'))
|
||||
let extraKeys = _.difference(Object.keys(extra), keys)
|
||||
let scopedData = Tree.TemplateToData(
|
||||
Object.assign({}, _.pick(extra, extraKeys), template)
|
||||
)
|
||||
data = _.pick(scopedData, keys)
|
||||
} else {
|
||||
data = Tree.TemplateToData(template)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
public static ArrayToTreeToTemplateToJSONSchema(list) {
|
||||
let tree = Tree.ArrayToTree(list)
|
||||
let template = Tree.TreeToTemplate(tree)
|
||||
let schema = Mock.toJSONSchema(template)
|
||||
return schema
|
||||
}
|
||||
|
||||
// TODO 2.2 执行 JSON.stringify() 序列化时会丢失正则和函数。需要转为字符串或者函数。
|
||||
// X Function.protytype.toJSON = Function.protytype.toString
|
||||
// X RegExp.protytype.toJSON = RegExp.protytype.toString
|
||||
public static stringifyWithFunctonAndRegExp(json) {
|
||||
return JSON.stringify(json, (k, v) => {
|
||||
if (typeof v === 'function') return v.toString()
|
||||
if (v !== undefined && v !== null && v.exec) return v.toString()
|
||||
else return v
|
||||
}, 2)
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
let pathToRegexp = require('path-to-regexp')
|
||||
|
||||
const pkg = {}
|
||||
|
||||
pkg.getRelative = url => {
|
||||
if (!url || typeof url !== 'string') return null
|
||||
url = url.toLowerCase()
|
||||
const prefixes = ['https://', 'http://']
|
||||
for (let item of prefixes) {
|
||||
if (url.indexOf(item) > -1) {
|
||||
url = url.substring(item.length)
|
||||
if (url.indexOf('/') > -1) {
|
||||
url = url.substring(url.indexOf('/'))
|
||||
} else {
|
||||
url = '/'
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (url.indexOf('?') > -1) {
|
||||
url = url.substring(0, url.indexOf('?'))
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
pkg.urlMatchesPattern = (url, pattern) => {
|
||||
url = pkg.getRelative(url)
|
||||
pattern = pkg.getRelative(pattern)
|
||||
let re = pathToRegexp(pattern)
|
||||
return re.test(url)
|
||||
}
|
||||
|
||||
module.exports = pkg
|
@ -0,0 +1,33 @@
|
||||
let pathToRegexp = require('path-to-regexp')
|
||||
|
||||
export default class pkg {
|
||||
|
||||
public static getRelative = url => {
|
||||
if (!url || typeof url !== 'string') return null
|
||||
url = url.toLowerCase()
|
||||
const prefixes = ['https://', 'http://']
|
||||
for (let item of prefixes) {
|
||||
if (url.indexOf(item) > -1) {
|
||||
url = url.substring(item.length)
|
||||
if (url.indexOf('/') > -1) {
|
||||
url = url.substring(url.indexOf('/'))
|
||||
} else {
|
||||
url = '/'
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (url.indexOf('?') > -1) {
|
||||
url = url.substring(0, url.indexOf('?'))
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
public static urlMatchesPattern = (url, pattern) => {
|
||||
url = pkg.getRelative(url)
|
||||
pattern = pkg.getRelative(pattern)
|
||||
let re = pathToRegexp(pattern)
|
||||
return re.test(url)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { PoolOptions } from "sequelize";
|
||||
import { ISequelizeConfig } from "sequelize-typescript";
|
||||
|
||||
declare interface IConfigOptions {
|
||||
version: string,
|
||||
serve: {
|
||||
port: number
|
||||
},
|
||||
keys: string[],
|
||||
session: {
|
||||
key: string
|
||||
},
|
||||
keycenter?: string | boolean,
|
||||
db: ISequelizeConfig
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"allowJs": true,
|
||||
"target": "es6",
|
||||
"removeComments": true,
|
||||
"sourceMap": true,
|
||||
"watch": false,
|
||||
"baseUrl": "./src",
|
||||
"rootDir": "./src",
|
||||
"paths": {
|
||||
"*": [
|
||||
"node_modules/*",
|
||||
"src/types/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
|
||||
{
|
||||
"rules": {
|
||||
"no-eval": true,
|
||||
"forin": true,
|
||||
"no-any": true,
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-empty": true,
|
||||
"no-for-in-array": false,
|
||||
"no-invalid-this": true,
|
||||
"no-string-literal": true,
|
||||
"no-string-throw": true,
|
||||
"no-unsafe-finally": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"radix": true,
|
||||
"switch-default": true,
|
||||
"triple-equals": [true, "allow-null-check"],
|
||||
|
||||
"adjacent-overload-signatures": true,
|
||||
"array-type": [true, "array"],
|
||||
"arrow-parens": false,
|
||||
"callable-types": true,
|
||||
"class-name": true,
|
||||
"comment-format": [true, "check-space", "check-uppercase"],
|
||||
"interface-name": [true, "always-prefix"],
|
||||
"jsdoc-format": true,
|
||||
"max-classes-per-file": [true, 3],
|
||||
"max-file-line-count": [true, 500],
|
||||
"max-line-length": [true, 140],
|
||||
"member-access": true,
|
||||
"member-ordering": [true, { "order": "fields-first" }],
|
||||
"new-parens": true,
|
||||
"no-construct": true,
|
||||
"no-default-export": true,
|
||||
"no-inferrable-types": false,
|
||||
"no-null-keyword": false,
|
||||
"no-parameter-properties": true,
|
||||
"no-require-imports": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-var-keyword": true,
|
||||
"no-var-requires": true,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-variable-per-declaration": [true, "ignore-for-loop"],
|
||||
"only-arrow-functions": false,
|
||||
"ordered-imports": [true, "case-insensitive"],
|
||||
"prefer-const": false,
|
||||
"prefer-for-of": true,
|
||||
"typedef": [true, "call-signature", "parameter", "property-declaration", "variable-declaration", "member-variable-declaration"],
|
||||
"unified-signatures": true,
|
||||
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"],
|
||||
|
||||
"align": [true, "parameters", "statements"],
|
||||
"curly": true,
|
||||
"eofline": true,
|
||||
"import-spacing": true,
|
||||
"indent": [true, "spaces"],
|
||||
"linebreak-style": true,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"object-literal-key-quotes": [true, "as-needed"],
|
||||
"one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"],
|
||||
"quotemark": [true, "single"],
|
||||
"semicolon": [true, "always"],
|
||||
"trailing-comma": [true, {"singleline": "never", "multiline": "always"}],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
],
|
||||
"whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"],
|
||||
|
||||
"ban": false,
|
||||
"cyclomatic-complexity": false,
|
||||
"file-header": false,
|
||||
"import-blacklist": false,
|
||||
"interface-over-type-literal": false,
|
||||
"no-angle-bracket-type-assertion": false,
|
||||
"no-empty-interface": false,
|
||||
"no-inferred-empty-object-type": false,
|
||||
"no-internal-module": false,
|
||||
"no-magic-numbers": false,
|
||||
"no-mergeable-namespace": false,
|
||||
"no-namespace": false,
|
||||
"no-reference": true,
|
||||
"object-literal-shorthand": false,
|
||||
"space-before-function-paren": false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue