|
@@ -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;
|
|
|
+};
|