|  | @@ -0,0 +1,132 @@
 | 
	
		
			
				|  |  | +'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 登录日志-数据模型
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * @author lanjianrong
 | 
	
		
			
				|  |  | + * @date 2020/8/31
 | 
	
		
			
				|  |  | + * @version
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +const UAParser = require('ua-parser-js');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +module.exports = app => {
 | 
	
		
			
				|  |  | +    class LoginLogging extends app.BaseService {
 | 
	
		
			
				|  |  | +        constructor(ctx) {
 | 
	
		
			
				|  |  | +            super(ctx);
 | 
	
		
			
				|  |  | +            this.tableName = 'login_logging';
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /**
 | 
	
		
			
				|  |  | +         * 创建记录
 | 
	
		
			
				|  |  | +         * @param {Object} payload - 载荷
 | 
	
		
			
				|  |  | +         */
 | 
	
		
			
				|  |  | +        async createLog(payload) {
 | 
	
		
			
				|  |  | +            const transaction = await this.db.beginTransaction();
 | 
	
		
			
				|  |  | +            try {
 | 
	
		
			
				|  |  | +                transaction.insert(this.tableName, payload);
 | 
	
		
			
				|  |  | +                await transaction.commit();
 | 
	
		
			
				|  |  | +            } catch (error) {
 | 
	
		
			
				|  |  | +                await transaction.rollback();
 | 
	
		
			
				|  |  | +                throw error;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /**
 | 
	
		
			
				|  |  | +         * 创建登录日志
 | 
	
		
			
				|  |  | +         * @return {Boolean} 日志是否创建成功
 | 
	
		
			
				|  |  | +         */
 | 
	
		
			
				|  |  | +        async addLoginLog() {
 | 
	
		
			
				|  |  | +            const { ctx } = this;
 | 
	
		
			
				|  |  | +            const ip = ctx.header['x-real-ip'] ? ctx.header['x-real-ip'] : '';
 | 
	
		
			
				|  |  | +            const ipInfo = await this.getIpInfoFromApi(ip);
 | 
	
		
			
				|  |  | +            const parser = new UAParser(ctx.header['user-agent']);
 | 
	
		
			
				|  |  | +            const osInfo = parser.getOS();
 | 
	
		
			
				|  |  | +            const cpuInfo = parser.getCPU();
 | 
	
		
			
				|  |  | +            const browserInfo = parser.getBrowser();
 | 
	
		
			
				|  |  | +            const payload = {
 | 
	
		
			
				|  |  | +                os: `${osInfo.name} ${osInfo.version} ${cpuInfo.architecture}`,
 | 
	
		
			
				|  |  | +                browser: `${browserInfo.name} ${browserInfo.version}`,
 | 
	
		
			
				|  |  | +                ip,
 | 
	
		
			
				|  |  | +                address: ipInfo,
 | 
	
		
			
				|  |  | +                uid: ctx.session.sessionUser.accountId,
 | 
	
		
			
				|  |  | +                pid: ctx.session.sessionProject.id,
 | 
	
		
			
				|  |  | +            };
 | 
	
		
			
				|  |  | +            return await this.createLog(payload);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /**
 | 
	
		
			
				|  |  | +         * 根据ip请求获取详细地址
 | 
	
		
			
				|  |  | +         * @param {String} a_ip - ip地址
 | 
	
		
			
				|  |  | +         * @return {String} 详细地址
 | 
	
		
			
				|  |  | +         */
 | 
	
		
			
				|  |  | +        async getIpInfoFromApi(a_ip = '') {
 | 
	
		
			
				|  |  | +            if (!a_ip) return '';
 | 
	
		
			
				|  |  | +            if (a_ip === '127.0.0.1') return '服务器本机访问';
 | 
	
		
			
				|  |  | +            const { ip = '', region = '', city = '', isp = '' } = await this.sendRequest(a_ip);
 | 
	
		
			
				|  |  | +            let address = '';
 | 
	
		
			
				|  |  | +            region && (address += region + '省');
 | 
	
		
			
				|  |  | +            city && (address += city + '市 ');
 | 
	
		
			
				|  |  | +            isp && (address += isp + ' ');
 | 
	
		
			
				|  |  | +            ip && (address += `(${ip})`);
 | 
	
		
			
				|  |  | +            return address;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /**
 | 
	
		
			
				|  |  | +         * 发送请求获取详细地址
 | 
	
		
			
				|  |  | +         * @param {String} ip - ip地址
 | 
	
		
			
				|  |  | +         * @param {Number} max - 最大重试次数
 | 
	
		
			
				|  |  | +         * @return {Object} the result of request
 | 
	
		
			
				|  |  | +         * @private
 | 
	
		
			
				|  |  | +         */
 | 
	
		
			
				|  |  | +        async sendRequest(ip, max = 3) {
 | 
	
		
			
				|  |  | +            return new Promise(resolve => {
 | 
	
		
			
				|  |  | +                const start = () => {
 | 
	
		
			
				|  |  | +                    if (max <= 0) {
 | 
	
		
			
				|  |  | +                        resolve(); // 已达到最大重试次数,返回空的执行承若
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    max--;
 | 
	
		
			
				|  |  | +                    this.ctx.curl(`https://api01.aliyun.venuscn.com/ip?ip=${ip}`, {
 | 
	
		
			
				|  |  | +                        dateType: 'json',
 | 
	
		
			
				|  |  | +                        encoding: 'utf8',
 | 
	
		
			
				|  |  | +                        timeout: 2000,
 | 
	
		
			
				|  |  | +                        headers: {
 | 
	
		
			
				|  |  | +                            Authorization: 'APPCODE 85c64bffe70445c4af9df7ae31c7bfcc',
 | 
	
		
			
				|  |  | +                        },
 | 
	
		
			
				|  |  | +                    }).then(({ status, data }) => {
 | 
	
		
			
				|  |  | +                        if (status === 200) {
 | 
	
		
			
				|  |  | +                            const result = JSON.parse(data.toString()).data;
 | 
	
		
			
				|  |  | +                            if (!result.ip) {
 | 
	
		
			
				|  |  | +                                start();
 | 
	
		
			
				|  |  | +                            } else {
 | 
	
		
			
				|  |  | +                                max++;
 | 
	
		
			
				|  |  | +                                resolve(result);
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        } else {
 | 
	
		
			
				|  |  | +                            max--;
 | 
	
		
			
				|  |  | +                            start();
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }).catch(() => {
 | 
	
		
			
				|  |  | +                        start();
 | 
	
		
			
				|  |  | +                    });
 | 
	
		
			
				|  |  | +                };
 | 
	
		
			
				|  |  | +                start();
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /**
 | 
	
		
			
				|  |  | +         * 获取登录日志
 | 
	
		
			
				|  |  | +         * @param {Number} pid - 项目id
 | 
	
		
			
				|  |  | +         * @param {Number} uid - 用户id
 | 
	
		
			
				|  |  | +         * @return {Promise<Array>} 日志数组
 | 
	
		
			
				|  |  | +         */
 | 
	
		
			
				|  |  | +        async getLoginLogs(pid, uid) {
 | 
	
		
			
				|  |  | +            return this.db.select(this.tableName, {
 | 
	
		
			
				|  |  | +                where: { pid, uid },
 | 
	
		
			
				|  |  | +                orders: [['create_time', 'desc']],
 | 
	
		
			
				|  |  | +                columns: ['browser', 'create_time', 'ip', 'os', 'address'],
 | 
	
		
			
				|  |  | +                limit: 10, offset: 0,
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return LoginLogging;
 | 
	
		
			
				|  |  | +};
 |