ntrip-caster服务后端

This commit is contained in:
2026-05-12 17:43:03 +08:00
commit 6e761f3727
45 changed files with 3125 additions and 0 deletions

522
app/controller/home.js Normal file
View File

@@ -0,0 +1,522 @@
const { Controller } = require('egg');
const GBK = require("gbk.js");
const decode = (data) => {
let arr = [];
data.match(/..?/g).forEach((element) => {
arr.push(parseInt(element, 16));
});
return GBK.decode(arr);
}
const CryptoJS = require("crypto-js");
const KEY = CryptoJS.enc.Utf8.parse('BDQH');
const decryptRequest = (body) => {
return CryptoJS.AES.decrypt(body, KEY, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
};
const checkUid = async (ctx) => {
const uid = ctx.request.body.uid;
const token = ctx.request.header.authorization;
const redis = ctx.app.redis;
let rdmcode;
if (!uid) {
const [buser, cuser, tuser] = await Promise.all([
redis.get('db0').get(token),
redis.get('db1').get(token),
redis.get('db5').get(token),
]);
rdmcode = buser || cuser || tuser;
} else {
let decryptUid = decryptRequest(uid).split('?')[0];
const redisType = {
"B": "db0",
"C": "db1",
"T": "db5",
};
const lastCode = decryptUid[decryptUid.length - 1];
rdmcode = await redis.get(redisType[lastCode]).get(token);
}
if (!rdmcode) {
return false;
}
return rdmcode;
};
const fileName = "home";
const setResponse = (ctx, code, msg, model, data = {}) => {
const modelType = {
"none": { code, msg },
"deconstruction": { code, msg, ...data },
"polymerization": { code, msg, data }
}
ctx.body = modelType[model];
}
class HomeController extends Controller {
// 保存挂载点
async saveMountpoint() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.saveMountpoint(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
setResponse(ctx, 200, '保存挂载点成功', "none");
} else {
setResponse(ctx, 404, '保存挂载点失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "saveMountpoint";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 修改挂载点密码
async editPassword() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.editPassword(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
setResponse(ctx, 200, '修改挂载点密码成功', "none");
} else {
setResponse(ctx, 404, '修改挂载点密码失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "editPassword";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 修改挂载点名称
async editMountpoint() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.editMountpoint(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
setResponse(ctx, 200, '修改挂载点名称成功', "none");
} else {
setResponse(ctx, 404, '修改挂载点名称失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "editMountpoint";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 删除挂载点
async delMountpoint() {
const ctx = this.ctx;
try {
// 验证用户token
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
// 解析获取用户ID
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
// 调用服务层删除挂载点
const result = await ctx.service.home.delMountpoint(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
// 根据删除结果返回响应
if (result) {
setResponse(ctx, 200, '修改挂载点名称成功', "none");
} else {
setResponse(ctx, 404, '修改挂载点名称失败', "none");
}
} catch (error) {
// 记录异常日志
const blockName = fileName + "--" + "delMountpoint";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 获取挂载点
async getMountpoint() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const result = await ctx.service.home.getMountpoint(uid);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
const data = { mountpoint: result };
setResponse(ctx, 200, '获取挂载点成功', "deconstruction", data);
} else {
setResponse(ctx, 404, '获取挂载点失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "getMountpoint";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 搜索挂载点
async searchMountpoint() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.searchMountpoint(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
const data = { mountpoint: result.dataValues.mountpoint };
setResponse(ctx, 200, '搜索挂载点成功', "deconstruction", data);
} else {
setResponse(ctx, 203, '搜索挂载点失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "searchMountpoint";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 全局寻获挂载点
async findMountpoint() {
const ctx = this.ctx;
try {
const body = ctx.request.body;
const result = await ctx.service.home.findMountpoint(body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
const data = {
mountpoint: result.mountpoint,
password: result.password,
status: result.status
};
setResponse(ctx, 200, '搜索挂载点成功', "deconstruction", data);
} else {
setResponse(ctx, 404, '搜索挂载点失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "findMountpoint";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 查看ntrip-clinet密码
async viewPassword() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.viewPassword(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
const data = { base: result.dataValues.base };
setResponse(ctx, 200, 'ntrip-clinet密码获取成功', "deconstruction", data);
} else {
setResponse(ctx, 404, 'ntrip-clinet密码获取失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "viewPassword";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 获取挂载点记录
async getRecord() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.getRecord(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
let recordMsg = [];
result.rows.forEach(element => {
recordMsg.push(element.dataValues);
});
const data = {
recordMsg: recordMsg,
count: result.count
};
setResponse(ctx, 200, '挂载点记录查询成功', "deconstruction", data);
} else {
setResponse(ctx, 404, '挂载点记录查询失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "getRecord";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 存储消息记录
async saveMsg() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const { mountpoint, original_msg } = body;
const form = {
mountpoint: mountpoint,
original_msg: original_msg,
created_at: new Date()
};
const result = await ctx.service.home.saveMsg(uid, form);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
setResponse(ctx, 200, '消息记录存储成功', "none");
} else {
setResponse(ctx, 404, '消息记录存储失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "saveMsg";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 设置ntrip-clinet密码
async setPassword() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.setPassword(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
setResponse(ctx, 200, 'ntrip-clinet密码设置成功', "none");
} else {
setResponse(ctx, 404, 'ntrip-clinet密码设置失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "setPassword";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 获取ntrip-clinet密码
async getPassword() {
const ctx = this.ctx;
try {
const body = ctx.request.body;
const result = await ctx.service.home.getPassword(body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
const data = { password: result.dataValues.password };
setResponse(ctx, 200, 'ntrip-clinet密码获取成功', "deconstruction", data);
} else {
setResponse(ctx, 404, 'ntrip-clinet密码获取失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "getPassword";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 获取历史消息
async getHistoryMsg() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const result = await ctx.service.home.getHistoryMsg(uid, body);
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
let historyMsg = [];
result.rows.forEach(element => {
historyMsg.push(element.dataValues);
});
const data = {
historyMsg: historyMsg,
count: result.count
};
setResponse(ctx, 200, '历史消息查找成功', "deconstruction", data);
} else {
setResponse(ctx, 404, '历史消息查找失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "getHistoryMsg";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 删除历史消息
async delHistoryMsg() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
const body = ctx.request.body;
const model = decryptRequest(body.model);
const password = decryptRequest(body.password);
const requestMsg = body.userMsg;
const resetList = await ctx.service.home.delHistoryMsg(uid, model, password, requestMsg);
if (resetList == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (resetList) {
setResponse(ctx, 200, '删除历史消息成功', "none");
} else {
setResponse(ctx, 404, '删除历史消息失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "delHistoryMsg";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
// 获取单位用户
async bsideManager() {
const ctx = this.ctx;
try {
let rdmcode = await checkUid(ctx);
if (!rdmcode) {
setResponse(ctx, 401, 'token过期或错误', "none");
return;
}
const uid = JSON.parse(rdmcode).uid;
let result;
if (uid[uid.length - 1] == 'B') {
const body = ctx.request.body;
result = await ctx.service.home.bsideManager(uid, body);
}
if (result == "error") {
setResponse(ctx, 500, '服务器错误', "none");
return;
}
if (result) {
let bsideUsers = [];
result.rows.forEach(element => {
bsideUsers.push(element.dataValues);
});
const data = {
bsideUsers: bsideUsers,
count: result.count
};
setResponse(ctx, 200, '获取单位用户成功', "deconstruction", data);
} else {
setResponse(ctx, 404, '获取单位用户失败', "none");
}
} catch (error) {
const blockName = fileName + "--" + "bsideManager";
ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error);
setResponse(ctx, 500, '服务器错误', "none");
}
}
}
module.exports = HomeController;

50
app/core/base_service.js Normal file
View File

@@ -0,0 +1,50 @@
const { Service } = require('egg');
const sha256 = require("crypto-js/sha256");
const CryptoJS = require("crypto-js");
const KEY = CryptoJS.enc.Utf8.parse('BDQH');
class BaseService extends Service {
shaForm = (body, salt) => {
return sha256(sha256(body).toString() + salt).toString();
}
encryptRequest = (body) => {
return CryptoJS.AES.encrypt(body, KEY, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString();
}
decryptRequest = (body) => {
return CryptoJS.AES.decrypt(body, KEY, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
}
opOptions = (attributes, searchOptions, substring, key, between, sortOptions) => {
let where = {}, order = [];
attributes.forEach(element => {
if (searchOptions[element]) {
if (substring.includes(element)) {
where[element] = { [Op.substring]: searchOptions[element] };
} else if (key.includes(element)) {
where[element] = searchOptions[element];
} else if (between.includes(element)) {
where[element] = { [Op.between]: [searchOptions[element][0], searchOptions[element][1]] };
}
}
if (!sortOptions[element]) {
order.push([element, 'DESC']);
} else if (sortOptions[element]) {
order.push([element, 'ASC']);
}
});
return { where, order }
}
}
module.exports = BaseService;

View File

@@ -0,0 +1,364 @@
const net = require('net');
const CryptoJS = require("crypto-js");
const KEY = CryptoJS.enc.Utf8.parse('BDQH');
class SimpleNtripCaster {
constructor(app) {
this.app = app;
const { config, redis } = app;
const port = config.caster.port;
this.port = port;
this.redis = redis;
this.sourcetable = [];
this.serverSockets = new Map(); // mountpoint -> server socket
this.sizeSockets = new Map(); // client socket -> mountpoint
this.clientSockets = new Map(); // client socket -> mountpoint
this.mountData = new Map(); // mountpoint -> 当前连接的 server socket
console.log(`[Caster] 启动在端口 ${port}`);
}
start() {
this.server = net.createServer((socket) => {
let clientInfo = '';
socket.on('data', (data) => {
const request = data.toString();
clientInfo += request;
// 检查是否收到完整的HTTP请求(以空行结束)
if (clientInfo.includes('\r\n\r\n')) {
this.handleRequest(socket, clientInfo);
clientInfo = '';
}
});
socket.on('end', () => {
console.log('[Caster] 连接关闭');
this.removeClient(socket);
this.broadSizeData();
});
socket.on('error', (err) => {
console.log('[Caster] 连接错误:', err.message);
this.removeClient(socket);
this.broadSizeData();
});
});
this.server.listen(this.port, () => {
console.log(`[Caster] 等待连接...`);
});
}
sendSourcetable(socket, statusCode, statusMessage, headers1 = {}, headers2 = {}, body = '') {
let response = `HTTP/1.1 ${statusCode} ${statusMessage}\r\n`;
for (const [key, value] of Object.entries(headers1)) {
response += `${key}: ${value}\r\n`;
}
response += `\r\n`;
response += `SOURCETABLE ${statusCode} ${statusMessage}\r\n`;
for (const [key, value] of Object.entries(headers2)) {
response += `${key}: ${value}\r\n`;
}
response += '\r\n' + body;
socket.end(response);
}
sendResponse(socket, statusCode, statusMessage, headers = {}, body = '') {
let response = `HTTP/1.1 ${statusCode} ${statusMessage}\r\n`;
for (const [key, value] of Object.entries(headers)) {
response += `${key}: ${value}\r\n`;
}
response += '\r\n' + body;
socket.end(response);
}
clientSendResponse(socket, statusCode, statusMessage, headers = {}, body = '') {
let response = `HTTP/1.1 ${statusCode} ${statusMessage}\r\n`;
for (const [key, value] of Object.entries(headers)) {
response += `${key}: ${value}\r\n`;
}
response += '\r\n' + body;
socket.write(response);
}
handleOptions(socket, headers) {
this.sendResponse(socket, 204, 'No Content', {
'Access-Control-Allow-Origin': headers.origin,
'Access-Control-Allow-Methods': 'GET, OPTIONS, SIZE, SOURCE',
'Access-Control-Allow-Headers': 'Content-Type, uid, Authorization, ntrip-version, Client-Type, Request-Type',
});
}
parseHeaders(headerLines) {
const headers = {};
headerLines.forEach((line) => {
const [key, value] = line.split(': ');
headers[key.toLowerCase()] = value;
});
return headers;
}
handleRequest(socket, request) {
const lines = request.split('\r\n');
const firstLine = lines[0];
console.log(`[Caster] 收到请求: ${firstLine.substring(0, 50)}...`);
// 处理 OPTIONS 请求
if (firstLine.split(' ')[0] === 'OPTIONS') {
const headers = this.parseHeaders(lines.slice(1, lines.indexOf('')));
this.handleOptions(socket, headers);
} else if (firstLine === 'GET / HTTP/1.1' || firstLine === 'GET / HTTP/1.0' || firstLine === 'GET /') {
// 客户端请求源列表
this.handleSourcetable(socket, lines);
} else if (firstLine.startsWith('SOURCE')) {
// Server 挂载请求
this.handleServerMount(socket, lines);
} else if (firstLine.startsWith('GET /')) {
// 客户端请求数据流
this.handleClientRequest(socket, lines);
} else {
this.sendResponse(socket, 400, 'Bad Request', {
'Access-Control-Allow-Origin': '*'
});
}
}
// 发送源列表
handleSourcetable(socket, lines) {
const headers = this.parseHeaders(lines.slice(1, lines.indexOf('')));
let table = '';
this.sourcetable.forEach(element => {
const { mountpoint, MountpointOptions } = element;
// 源ID
const SourceID = MountpointOptions[0] || 'STR: ';
// 格式
const Format = MountpointOptions[1] || 'RTCM';
// 格式详情
const FormatDetails = MountpointOptions[2] || '';
// 载体
const Carrier = MountpointOptions[3] || '';
// 导航系统
const NavSystem = MountpointOptions[4] || '';
// 网络
const NetWork = MountpointOptions[5] || '';
// 国家
const Country = MountpointOptions[6] || '';
// 纬度
const Latitude = MountpointOptions[7] || 0.0;
// 经度
const Longitude = MountpointOptions[8] || 0.0;
// NMEA
const NMEA = MountpointOptions[9] || '';
// 解决方案
const Soution = MountpointOptions[10] || '';
// 发生器
const Generator = MountpointOptions[11] || '';
// 压缩加密
const ComprEncryp = MountpointOptions[12] || '';
// 认证
const Auth = MountpointOptions[13] || '';
// 费用
const Fee = MountpointOptions[14] || '';
// 比特率
const Bitrate = MountpointOptions[15] || '';
const found = lines.find(line => line.startsWith('Ntrip-Version: Ntrip/2.0'));
if (found == 'Ntrip-Version: Ntrip/2.0') {
// 变体B:RTCM/ntrip-2风格
table += `STR;${Format};${mountpoint};${FormatDetails};;${Carrier};${NavSystem};${NetWork};${Country};${Latitude};${Longitude};${NMEA};${SourceID};;;;\r\n`;
} else {
// 变体A:BKG/NTRIP 1.0风格
table += `STR;${Format};${mountpoint};${FormatDetails};;${Carrier};${NavSystem};${SourceID};${Latitude};${Longitude};0.0;${NetWork};${Country};;;;\r\n`;
}
});
table += 'ENDSOURCETABLE\r\n';
this.sendSourcetable(socket, 200, 'OK', {
'Access-Control-Allow-Origin': headers.origin ? headers.origin : '*',
'Server': 'NTRIP Caster',
'Content-Type': 'text/plain',
'Date': 'Tue, 12 May 2026 08:00:00 GMT',
'Connection': 'close'
}, {
'Access-Control-Allow-Origin': headers.origin ? headers.origin : '*',
'Server': 'Caster/1.0',
'Content-Type': 'text/plain',
'Charset': 'UTF-8'
}, table);
console.log('[Caster] 已发送源列表');
}
// 处理 Server 挂载
async handleServerMount(socket, lines) {
const source = lines[0].split(' ');
// 密码
const password = source[1];
// 挂载点
const mountpoint = source[2];
const findMountpoint = await this.app.curl('http://127.0.0.1:' + this.app.server.address().port + '/findMountpoint', {
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
method: 'POST',
timeout: 3000,
data: {
mountpoint: mountpoint
}
});
if (findMountpoint.data.code == 404) {
this.sendResponse(socket, 404, 'Mountpoint Not Found', {});
return;
}
if (password !== findMountpoint.data.password) {
this.sendResponse(socket, 401, 'Unauthorized', {});
return;
}
const MountpointOptions = lines[2].split(';');
this.sourcetable.push({ password, mountpoint, MountpointOptions });
this.serverSockets.set(mountpoint, socket);
this.mountData.set(mountpoint, {
socket: socket,
lastUpdate: Date.now()
});
// 响应 Server
socket.write('ICY 200 OK\r\n\r\n');
console.log(`[Caster] Server 挂载成功: ${mountpoint}`);
// 监听 Server 数据
socket.on('data', (data) => {
this.broadcastData(mountpoint, data);
});
socket.on('end', () => {
console.log(`[Caster] Server 断开: ${mountpoint}`);
this.serverSockets.delete(mountpoint);
this.mountData.delete(mountpoint);
});
}
// 处理 Client 请求
async handleClientRequest(socket, lines) {
// 解析请求的挂载点
const headers = this.parseHeaders(lines.slice(1, lines.indexOf('')));
const firstLine = lines[0];
const match = firstLine.match(/GET \/(\w+) HTTP\/1\.[01]/);
if (!match) {
this.sendResponse(socket, 404, '404 Not Found', {
'Access-Control-Allow-Origin': headers.origin,
});
return;
}
const mountpoint = match[1];
console.log(`[Caster] Client 请求挂载点: ${mountpoint}`);
const find = lines.find(line => line.startsWith('Request-Type: SIZE'));
const found = lines.find(line => line.startsWith('Client-Type: Virtual-NTRIP-Client/1.0'));
if (found !== 'Client-Type: Virtual-NTRIP-Client/1.0') {
if (find !== 'Request-Type: SIZE') {
const findMountpoint = await this.app.curl('http://127.0.0.1:' + this.app.server.address().port + '/findMountpoint', {
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
method: 'POST',
timeout: 3000,
data: {
mountpoint: mountpoint
}
});
if (findMountpoint.data.code == 404) {
this.sendResponse(socket, 404, 'Mountpoint Not Found', {});
return;
}
const getPassword = await this.app.curl('http://127.0.0.1:' + this.app.server.address().port + '/getPassword', {
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
method: 'POST',
timeout: 3000,
data: {
mountpoint: mountpoint,
base: headers.authorization.split(' ')[1],
status: findMountpoint.data.status
}
});
if (getPassword.data.code == 404) {
this.sendResponse(socket, 401, 'Unauthorized', {});
return;
}
}
}
if (find !== 'Request-Type: SIZE') {
if (!this.mountData.has(mountpoint)) {
this.sendResponse(socket, 404, 'Mountpoint Not Found', {
'Access-Control-Allow-Origin': headers.origin
});
return;
}
}
if (found == 'Client-Type: Virtual-NTRIP-Client/1.0') {
const uid = lines[7].split(': ')[1];
const token = lines[6].split(': ')[1];
let decryptUid = CryptoJS.AES.decrypt(uid, KEY, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8).split('?')[0];
const redisType = {
"B": "db0",
"C": "db1",
"T": "db5",
};
const lastCode = decryptUid[decryptUid.length - 1];
const rdmcode = await this.redis.get(redisType[lastCode]).get(token);
if (!rdmcode) {
this.sendResponse(socket, 401, 'Unauthorized', {
'Access-Control-Allow-Origin': headers.origin
});
return;
}
}
if (find == 'Request-Type: SIZE') {
const uid = lines[8].split(': ')[1];
// 接受客户端连接
this.sizeSockets.set(uid + ':' + mountpoint, socket);
const size = [...this.clientSockets.values()].filter(value => value == mountpoint);
this.clientSendResponse(socket, 200, 'OK', {
'Access-Control-Allow-Origin': headers.origin, // 设置允许的源
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive'
}, size.length);
console.log(`[Caster] 已发送Client挂载数量`);
} else {
// 接受客户端连接
this.clientSockets.set(socket, mountpoint);
this.clientSendResponse(socket, 200, 'OK', {
'Access-Control-Allow-Origin': headers.origin, // 设置允许的源
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive'
});
this.broadSizeData();
console.log(`[Caster] Client 已连接至: ${mountpoint}`);
}
}
// 广播已连接到此挂载点的客户端数量数据给所有已连接到此挂载点的客户端
broadSizeData() {
for (const [clientMsg, client] of this.sizeSockets.entries()) {
const mountpoint = clientMsg.split(':')[1];
const size = [...this.clientSockets.values()].filter(value => value == mountpoint);
client.write(size.length + '');
}
}
// 广播数据给所有连接到此挂载点的客户端
broadcastData(mountpoint, data) {
for (const [client, clientMount] of this.clientSockets.entries()) {
if (clientMount === mountpoint && !client.destroyed) {
client.write(data);
}
}
}
removeClient(socket) {
this.clientSockets.delete(socket);
}
findHeader(lines, headerName) {
for (const line of lines) {
if (line.startsWith(headerName + ':')) {
return line.substring(headerName.length + 1).trim();
}
}
return null;
}
}
// 导出类供单独使用
module.exports = { SimpleNtripCaster };

View File

@@ -0,0 +1,72 @@
// 错误日志
const fs = require('fs');
const os = require('os');
const path = require('path');
const dirCheck = (dirPath) => {
// 检查文件夹是否存在
fs.access(dirPath, fs.constants.F_OK, (err) => {
if (err) {
// 文件夹不存在
console.log('文件夹不存在,正在创建...');
fs.mkdir(dirPath, { recursive: true }, (err) => {
if (err) throw err;
console.log('文件夹已创建');
});
} else {
// 文件夹存在
console.log('文件夹已存在');
}
});
}
module.exports = (filePath, msg) => {
// 获取时间
let now = new Date(),
times = {
year: now.getFullYear(),
month: now.getMonth() + 1,
day: now.getDate(),
hour: now.getHours(),
minute: now.getMinutes(),
second: now.getSeconds()
};
// 获取操作系统类型
const type = os.type();
console.log(`操作系统类型: ${type}`);
filePath = path.join(__dirname, filePath);
dirCheck(filePath);
const yearPath = filePath + '/' + times.year;
dirCheck(yearPath);
const monthPath = yearPath + '/' + times.month;
dirCheck(monthPath);
const dayPath = monthPath + '/' + times.day + '.txt';
const logTime = '[' + times.year + '-' + times.month + '-' + times.day + '>>>' + times.hour + ':' + times.minute + ':' + times.second + ']' + '>>>';
let crlf;
// 判断操作系统类型,设置换行符
if (type === 'Windows_NT') {
crlf = '\n';
} else {
crlf = '\r\n';
}
// 读取文件内容
fs.readFile(dayPath, 'utf8', (err, data) => {
if (err) {
if (err.code === 'ENOENT') {
// 文件不存在,直接写入新内容(这里实际上还是使用了覆盖的方式)
fs.writeFile(dayPath, logTime + msg + crlf, (err) => {
if (err) throw err;
console.log('文件已创建并写入新内容');
});
} else {
throw err;
}
} else {
// 文件存在,追加新内容
fs.appendFile(dayPath, logTime + msg + crlf, (err) => {
if (err) throw err;
console.log('新内容已追加到文件');
});
}
});
};

20
app/model/BNtripAdmin.js Normal file
View File

@@ -0,0 +1,20 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING } = app.Sequelize;
const BNtripAdmin = app.model.define('bntrip-admin', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
organization: CHAR(15),
mountpoint: STRING(100),
password: STRING(16)
}, {
freezeTableName: true,
timestamp: false,
createdAt: false,
updatedAt: false
});
return BNtripAdmin;
};

20
app/model/BNtripClient.js Normal file
View File

@@ -0,0 +1,20 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING } = app.Sequelize;
const BNtripClient = app.model.define('bntrip-client', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
organization: CHAR(15),
mountpoint: STRING(100),
base: STRING(104)
}, {
freezeTableName: true,
timestamp: false,
createdAt: false,
updatedAt: false
});
return BNtripClient;
};

21
app/model/BNtripMsg.js Normal file
View File

@@ -0,0 +1,21 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, TEXT, DATE } = app.Sequelize;
const BNtripMsg = app.model.define('bntrip-msg', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
organization: CHAR(15),
mountpoint: STRING(100),
original_msg: TEXT,
created_at: DATE
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: false
});
return BNtripMsg;
};

22
app/model/BNtripRecord.js Normal file
View File

@@ -0,0 +1,22 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, DATE } = app.Sequelize;
const BNtripRecord = app.model.define('bntrip-record', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
organization: CHAR(15),
mountpoint: STRING(100),
befor_edit: STRING(100),
created_at: DATE,
updated_at: DATE
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: true
});
return BNtripRecord;
};

30
app/model/BSide.js Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, DATE } = app.Sequelize;
const BSide = app.model.define('b-side', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
uid: CHAR(15),
salt: CHAR(24),
status: INTEGER(3),
role: INTEGER(3),
account_name: STRING(20),
password: CHAR(64),
organization: CHAR(16),
user_name: STRING(20),
phone: CHAR(11),
msg: STRING(300),
created_at: DATE,
updated_at: DATE,
login_at: DATE,
login_status: INTEGER(3)
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: true
});
return BSide;
};

19
app/model2/CNtripAdmin.js Normal file
View File

@@ -0,0 +1,19 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING } = app.Sequelize;
const CNtripAdmin = app.model2.define('cntrip-admin', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
password: STRING(16)
}, {
freezeTableName: true,
timestamp: false,
createdAt: false,
updatedAt: false
});
return CNtripAdmin;
};

View File

@@ -0,0 +1,19 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING } = app.Sequelize;
const CNtripClient = app.model.define('cntrip-client', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
base: STRING(104)
}, {
freezeTableName: true,
timestamp: false,
createdAt: false,
updatedAt: false
});
return CNtripClient;
};

20
app/model2/CNtripMsg.js Normal file
View File

@@ -0,0 +1,20 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, TEXT, DATE } = app.Sequelize;
const CNtripMsg = app.model2.define('cntrip-msg', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
original_msg: TEXT,
created_at: DATE
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: false
});
return CNtripMsg;
};

View File

@@ -0,0 +1,21 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, DATE } = app.Sequelize;
const CNtripRecord = app.model2.define('cntrip-record', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
befor_edit: STRING(100),
created_at: DATE,
updated_at: DATE
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: true
});
return CNtripRecord;
};

34
app/model2/CSide.js Normal file
View File

@@ -0,0 +1,34 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, DATE } = app.Sequelize;
const CSide = app.model2.define('c-side', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
uid: CHAR(15),
salt: CHAR(24),
status: INTEGER(3),
role: INTEGER(3),
avatar: CHAR(15),
account_name: STRING(20),
password: CHAR(64),
user_name: STRING(20),
phone: CHAR(11),
gender: INTEGER(1),
age: INTEGER(3),
email: STRING(254),
address: STRING(300),
post_code: CHAR(6),
created_at: DATE,
updated_at: DATE,
login_at: DATE,
login_status: INTEGER(3)
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: true
});
return CSide;
};

21
app/model3/Mountpoint.js Normal file
View File

@@ -0,0 +1,21 @@
'use strict';
module.exports = (app) => {
const { INTEGER, STRING, DATE } = app.Sequelize;
const Mountpoint = app.model3.define('mountpoint', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
mountpoint: STRING(100),
password: STRING(16),
status: INTEGER(3),
created_at: DATE,
updated_at: DATE,
},{
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: true,
});
return Mountpoint;
};

24
app/model4/Account.js Normal file
View File

@@ -0,0 +1,24 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, DATE } = app.Sequelize;
const Account = app.model4.define('account', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
uid: CHAR(15),
salt: CHAR(24),
contest_uid: CHAR(15),
account_name: STRING(20),
password: CHAR(64),
status: INTEGER(3),
created_at: DATE,
login_status: INTEGER(3)
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: false
});
return Account;
};

19
app/model4/TNtripAdmin.js Normal file
View File

@@ -0,0 +1,19 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING } = app.Sequelize;
const TNtripAdmin = app.model4.define('tntrip-admin', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
password: STRING(16)
}, {
freezeTableName: true,
timestamp: false,
createdAt: false,
updatedAt: false
});
return TNtripAdmin;
};

View File

@@ -0,0 +1,19 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING } = app.Sequelize;
const TNtripClient = app.model.define('tntrip-client', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
base: STRING(104)
}, {
freezeTableName: true,
timestamp: false,
createdAt: false,
updatedAt: false
});
return TNtripClient;
};

20
app/model4/TNtripMsg.js Normal file
View File

@@ -0,0 +1,20 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, TEXT, DATE } = app.Sequelize;
const TNtripMsg = app.model4.define('tntrip-msg', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
original_msg: TEXT,
created_at: DATE
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: false
});
return TNtripMsg;
};

View File

@@ -0,0 +1,21 @@
'use strict';
module.exports = (app) => {
const { INTEGER, CHAR, STRING, DATE } = app.Sequelize;
const TNtripRecord = app.model4.define('tntrip-record', {
id: { type: INTEGER(8), primaryKey: true, autoIncrement: true },
user_uid: CHAR(15),
mountpoint: STRING(100),
befor_edit: STRING(100),
created_at: DATE,
updated_at: DATE
}, {
freezeTableName: true,
timestamp: true,
createdAt: true,
updatedAt: true
});
return TNtripRecord;
};

43
app/router.js Normal file
View File

@@ -0,0 +1,43 @@
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, middleware, controller } = app;
setTimeout(() => {
// 启动socket客户端
const caster = new middleware.ntripCaster.ntripCaster.SimpleNtripCaster(app);
caster.start();
}, 10000);
// 保存挂载点
router.post('/saveMountpoint', controller.home.saveMountpoint);
// 修改挂载点密码
router.post('/editPassword', controller.home.editPassword);
// 修改挂载点名称
router.post('/editMountpoint', controller.home.editMountpoint);
// 删除挂载点
router.post('/delMountpoint', controller.home.delMountpoint);
// 获取挂载点
router.get('/getMountpoint', controller.home.getMountpoint);
// 搜索挂载点
router.post('/searchMountpoint', controller.home.searchMountpoint);
// 全局寻获挂载点
router.post('/findMountpoint', controller.home.findMountpoint);
// 查看ntrip-clinet密码
router.post('/viewPassword', controller.home.viewPassword);
// 获取挂载点记录
router.post('/getRecord', controller.home.getRecord);
// 存储消息记录
router.post('/saveMsg', controller.home.saveMsg);
// 设置ntrip-clinet密码
router.post('/setPassword', controller.home.setPassword);
// 获取ntrip-clinet密码
router.post('/getPassword', controller.home.getPassword);
// 获取历史消息
router.post('/getHistoryMsg', controller.home.getHistoryMsg);
// 删除历史消息
router.post('/delHistoryMsg', controller.home.delHistoryMsg);
// 获取单位用户
router.post('/bsideManager', controller.home.bsideManager);
};

662
app/service/home.js Normal file
View File

@@ -0,0 +1,662 @@
'use strict';
const Service = require('../core/base_service');
const { Op } = require('sequelize');
const shunt = (uid) => {
return uid[uid.length - 1];
};
const fileName = 'home';
const redisType = {
"B": "db0",
"C": "db1",
"T": "db5",
};
class HomeService extends Service {
// 保存挂载点
async saveMountpoint(uid, body) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
const uidLastCode = shunt(uid);
const form = {
user_uid: uid,
mountpoint: body.mountpoint,
password: body.password
};
let redisKey;
if (uidLastCode == 'B') {
const bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!bside || bside.organization == null) return false;
form.organization = bside.organization;
redisKey = bside.organization;
} else {
redisKey = uid;
}
const model = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin
};
const save = await redis.get(redisType[uidLastCode]).get(redisKey + 'mountpoint');
if (save) {
await redis.get(redisType[uidLastCode]).set(redisKey + 'mountpoint', JSON.stringify([body.mountpoint, ...JSON.parse(save)]));
} else {
const saved = await model[uidLastCode].findAll({ where: uidLastCode == 'B' ? { organization: redisKey } : { user_uid: redisKey } });
let mountpoint = [];
saved.forEach(element => {
mountpoint.push(element.dataValues.mountpoint);
});
mountpoint.push(body.mountpoint);
await redis.get(redisType[uidLastCode]).set(redisKey + 'mountpoint', JSON.stringify(mountpoint));
}
const result = await model[uidLastCode].create(form);
const systemForm = {
mountpoint: body.mountpoint,
password: body.password,
status: uidLastCode == 'B' ? 1 : uidLastCode == 'C' ? 2 : 3,
created_at: new Date(),
updated_at: new Date()
};
await ctx.model3.Mountpoint.create(systemForm);
if (result) await redis.get(redisType[uidLastCode]).set(body.mountpoint, body.password);
form.befor_edit = '';
form.created_at = new Date();
form.updated_at = new Date();
const modelName = {
"B": ctx.model.BNtripRecord,
"C": ctx.model2.CNtripRecord,
"T": ctx.model4.TNtripRecord
};
await modelName[uidLastCode].create(form);
await redis.get(redisType[uidLastCode]).bgsave();
return result || false;
} catch (error) {
const blockName = fileName + "--" + "saveMountpoint";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 修改挂载点密码
async editPassword(uid, body) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
const form = {
user_uid: uid,
mountpoint: body.beforEdit,
};
const uidLastCode = shunt(uid);
if (uidLastCode == 'B') {
const bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!bside || bside.organization == null) return false;
form.organization = bside.organization;
}
const whereAdmin = { mountpoint: body.beforEdit };
const modelType = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin
};
const findAdmin = await modelType[uidLastCode].findOne({ where: whereAdmin });
if (!findAdmin) return false;
const updateAdminForm = { password: body.password };
const result = await modelType[uidLastCode].update(updateAdminForm, { where: whereAdmin });
await ctx.model3.Mountpoint.update(updateAdminForm, { where: whereAdmin });
if (result[0] !== 0) {
await redis.get(redisType[uidLastCode]).set(body.beforEdit, body.password);
await redis.get(redisType[uidLastCode]).bgsave();
}
return result[0] == 0 ? false : result;
} catch (error) {
const blockName = fileName + "--" + "editPassword";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 修改挂载点名称
async editMountpoint(uid, body) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
const form = {
user_uid: uid,
mountpoint: body.beforEdit
};
let redisKey;
const uidLastCode = shunt(uid);
if (uidLastCode == 'B') {
const bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!bside || bside.organization == null) return false;
form.organization = bside.organization;
redisKey = bside.organization;
} else {
redisKey = uid;
}
const save = await redis.get(redisType[uidLastCode]).get(redisKey + 'mountpoint');
if (save) {
await redis.get(redisType[uidLastCode]).set(redisKey + 'mountpoint', JSON.stringify([body.afterEdit, ...JSON.parse(save).filter(item => item !== body.beforEdit)]));
} else {
await redis.get(redisType[uidLastCode]).set(redisKey + 'mountpoint', JSON.stringify([body.afterEdit]));
}
const whereAdmin = { mountpoint: body.beforEdit };
const whereRecord = { mountpoint: body.beforEdit, befor_edit: '' };
const modelType = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin
};
const model = {
"B": ctx.model.BNtripRecord,
"C": ctx.model2.CNtripRecord,
"T": ctx.model4.TNtripRecord
};
const modelName = {
"B": ctx.model.BNtripClient,
"C": ctx.model2.CNtripClient,
"T": ctx.model4.TNtripClient
};
const findAdmin = await modelType[uidLastCode].findOne({ where: whereAdmin });
if (!findAdmin) return false;
const findRecord = await model[uidLastCode].findOne({ where: whereRecord });
if (!findRecord) {
form.befor_edit = '';
form.created_at = new Date();
form.updated_at = new Date();
const createResult = await model[uidLastCode].create(form);
if (!createResult) return false;
}
const updateAdminForm = { mountpoint: body.afterEdit };
const updateRecordForm = {
mountpoint: body.afterEdit,
befor_edit: body.beforEdit,
updated_at: new Date()
};
const result = await modelType[uidLastCode].update(updateAdminForm, { where: whereAdmin });
await ctx.model3.Mountpoint.update(updateAdminForm, { where: whereAdmin });
await model[uidLastCode].update(updateRecordForm, { where: whereRecord });
await modelName[uidLastCode].update(updateAdminForm, { where: whereAdmin });
if (result[0] !== 0) {
await redis.get(redisType[uidLastCode]).del(body.beforEdit);
await redis.get(redisType[uidLastCode]).set(body.afterEdit, findAdmin.dataValues.password);
}
await redis.get(redisType[uidLastCode]).bgsave();
return result[0] == 0 ? false : result;
} catch (error) {
const blockName = fileName + "--" + "editMountpoint";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 删除挂载点
async delMountpoint(uid, body) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
let redisKey;
const uidLastCode = shunt(uid);
if (uidLastCode == 'B') {
const bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!bside || bside.organization == null) return false;
redisKey = bside.organization;
} else {
redisKey = uid;
}
const save = await redis.get(redisType[uidLastCode]).get(redisKey + 'mountpoint');
if (save) {
await redis.get(redisType[uidLastCode]).del(body.mountpoint);
await redis.get(redisType[uidLastCode]).set(redisKey + 'mountpoint', JSON.stringify([...JSON.parse(save).filter(item => item !== body.mountpoint)]));
await redis.get(redisType[uidLastCode]).bgsave();
}
const where = { mountpoint: body.mountpoint };
const modelType = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin
};
const modelName = {
"B": ctx.model.BNtripClient,
"C": ctx.model2.CNtripClient,
"T": ctx.model4.TNtripClient
};
const model = {
"B": ctx.model.BNtripMsg,
"C": ctx.model2.CNtripMsg,
"T": ctx.model4.TNtripMsg
};
const modelRecord = {
"B": ctx.model.BNtripRecord,
"C": ctx.model2.CNtripRecord,
"T": ctx.model4.TNtripRecord
};
const resetList = await modelType[uidLastCode].destroy({ where });
await modelName[uidLastCode].destroy({ where });
await model[uidLastCode].destroy({ where });
await modelRecord[uidLastCode].destroy({ where });
return resetList.length == 0 ? false : resetList;
} catch (error) {
const blockName = fileName + "--" + "delMountpoint";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 获取挂载点
async getMountpoint(uid) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
let where;
const uidLastCode = shunt(uid);
const model = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin,
};
let redisKey;
if (uidLastCode == 'B') {
const bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 } } });
if (!bside || bside.organization == null) return false;
where = { organization: bside.organization };
redisKey = bside.organization;
} else {
where = { user_uid: uid };
redisKey = uid;
}
const mountpoint = await redis.get(redisType[uidLastCode]).get(redisKey + 'mountpoint');
if (mountpoint) {
return JSON.parse(mountpoint);
} else {
const result = await model[uidLastCode].findAll({ where });
if (result.length == 0) return false;
let mountpoint = [];
result.forEach(element => {
mountpoint.push(element.dataValues.mountpoint);
});
await redis.get(redisType[uidLastCode]).set(redisKey + 'mountpoint', JSON.stringify(mountpoint));
await redis.get(redisType[uidLastCode]).bgsave();
return mountpoint;
}
} catch (error) {
const blockName = fileName + "--" + "getMountpoint";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 搜索挂载点
async searchMountpoint(uid, body) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
const uidLastCode = shunt(uid);
const model = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin,
};
const mountpoint = await redis.get(redisType[uidLastCode]).get(body.mountpoint);
if (mountpoint) {
return JSON.parse(mountpoint);
} else {
const where = { mountpoint: body.mountpoint };
const result = await model[uidLastCode].findOne({ where });
return result || false;
}
} catch (error) {
const blockName = fileName + "--" + "searchMountpoint";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 全局寻获挂载点
async findMountpoint(body) {
const { ctx } = this;
const redis = ctx.app.redis;
try {
const where = { mountpoint: body.mountpoint };
const result = await ctx.model3.Mountpoint.findOne({ where });
if (result) {
return result;
}
const attributes = ['mountpoint', 'password'];
// 尝试从 Redis 获取数据
const [bside, cside, contest] = await Promise.all([
redis.get(redisType['B']).get(body.mountpoint),
redis.get(redisType['C']).get(body.mountpoint),
redis.get(redisType['T']).get(body.mountpoint)
]);
// 检查 Redis 中是否有匹配的记录
const redisResult = bside || cside || contest;
if (redisResult) {
return { mountpoint: body.mountpoint, password: redisResult, status: bside ? 1 : cside ? 2 : 3 };
}
// 如果 Redis 中没有匹配的记录,尝试从数据库中获取
const [bAdmin, cAdmin, tAdmin] = await Promise.all([
ctx.model.BNtripAdmin.findOne({ attributes, where }),
ctx.model2.CNtripAdmin.findOne({ attributes, where }),
ctx.model4.TNtripAdmin.findOne({ attributes, where })
]);
// 检查数据库中是否有匹配的记录
const admin = bAdmin || cAdmin || tAdmin;
if (admin) {
admin.dataValues.status = bAdmin ? 1 : cAdmin ? 2 : 3;
return admin.dataValues;
}
return false;
} catch (error) {
const blockName = fileName + "--" + "findMountpoint";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 查看ntrip-clinet密码
async viewPassword(uid, body) {
const { ctx } = this;
try {
const { mountpoint } = body;
const uidLastCode = shunt(uid);
const model = {
"B": ctx.model.BNtripClient,
"C": ctx.model2.CNtripClient,
"T": ctx.model4.TNtripClient
};
const attributes = ['base'];
const where = { user_uid: uid, mountpoint };
const user = await model[uidLastCode].findOne({ attributes, where });
return user || false;
} catch (error) {
const blockName = fileName + "--" + "viewPassword";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 获取挂载点记录
async getRecord(uid, body) {
const { ctx } = this;
try {
let bside;
const uidLastCode = shunt(uid);
if (uidLastCode == 'B') {
bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!bside || bside.organization == null) return false;
}
let { offset, limit, searchOptions, sortOptions } = body;
offset = (offset - 1) * limit;
let attributes = ['mountpoint', 'befor_edit', 'created_at', 'updated_at'],
where = uidLastCode == 'B' ? { organization: bside.organization } : { user_uid: uid }, order = [], dbOptions, joinSubstring = [],
substring = ['mountpoint', 'befor_edit'], key = [], between = ['created_at', 'updated_at'];
attributes.forEach(element => {
if (searchOptions[element]) {
if (searchOptions[element] == '*空') {
if (joinSubstring.includes(element)) {
where[element] = null;
} else {
where[element] = [null, ''];
}
} else if (substring.includes(element)) {
where[element] = { [Op.substring]: searchOptions[element] };
} else if (key.includes(element)) {
where[element] = searchOptions[element] == "1" ? uidLastCode === 'B' ? 1 : 2 : 0;
} else if (between.includes(element)) {
where[element] = { [Op.between]: [searchOptions[element][0], searchOptions[element][1]] };
}
}
if (sortOptions[element] == true) {
order.push([element, 'ASC']);
} else if (sortOptions[element] == false) {
order.push([element, 'DESC']);
}
});
dbOptions = { attributes, offset, limit, where, order };
const model = {
"B": ctx.model.BNtripRecord,
"C": ctx.model2.CNtripRecord,
"T": ctx.model4.TNtripRecord,
};
const result = await model[uidLastCode].findAndCountAll(dbOptions);
return result.rows.length == 0 ? false : result;
} catch (error) {
const blockName = fileName + "--" + "getRecord";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 存储消息记录
async saveMsg(uid, form) {
const { ctx } = this;
try {
const where = { mountpoint: form.mountpoint };
const model = {
"B": ctx.model.BNtripAdmin,
"C": ctx.model2.CNtripAdmin,
"T": ctx.model4.TNtripAdmin,
};
const uidLastCode = shunt(uid);
const bind = await model[uidLastCode].findOne({ where });
if (!bind) return false;
form.user_uid = bind.dataValues.user_uid;
if (uidLastCode == 'B') {
form.organization = bind.dataValues.organization;
}
const modelName = {
"B": ctx.model.BNtripMsg,
"C": ctx.model2.BNtripMsg,
"T": ctx.model4.BNtripMsg
};
const result = await modelName[uidLastCode].create(form);
return result || false;
} catch (error) {
const blockName = fileName + "--" + "saveMsg";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 设置ntrip-clinet密码
async setPassword(uid, body) {
const { ctx } = this;
try {
const uidLastCode = shunt(uid);
const { mountpoint, password } = body;
const model = {
"B": ctx.model.BSide,
"C": ctx.model2.CSide,
"T": ctx.model4.Account
};
const user = await model[uidLastCode].findOne({ where: { uid, status: { [Op.not]: 2 } } });
if (!user) return false;
const form = {
user_uid: uid,
mountpoint,
};
if (uidLastCode == 'B') {
form.organization = user.dataValues.organization;
}
const modelName = {
"B": ctx.model.BNtripClient,
"C": ctx.model2.CNtripClient,
"T": ctx.model4.TNtripClient
};
// 将字符串编码为 UTF-8 字节数组
const bytes = new TextEncoder().encode(user.dataValues.account_name + ':' + password);
// 将字节数组转换为二进制字符串
let binary = '';
bytes.forEach(byte => binary += String.fromCharCode(byte));
const existence = await modelName[uidLastCode].findOne({ where: form });
if (existence) {
const existence = await modelName[uidLastCode].update({ base: btoa(binary) }, { where: form });
return existence[0] == 0 ? false : existence;
} else {
form.base = btoa(binary);
const result = await modelName[uidLastCode].create(form);
return result || false;
}
} catch (error) {
const blockName = fileName + "--" + "setPassword";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 获取ntrip-clinet密码
async getPassword(body) {
const { ctx } = this;
try {
const { mountpoint, base, status } = body;
const model = {
"1": ctx.model.BNtripClient,
"2": ctx.model2.CNtripClient,
"3": ctx.model4.TNtripClient
};
const user = await model[status].findOne({ where: { mountpoint, base } });
return user || false;
} catch (error) {
const blockName = fileName + "--" + "getPassword";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 获取历史消息
async getHistoryMsg(uid, body) {
const { ctx } = this;
try {
let bside;
const uidLastCode = shunt(uid);
if (uidLastCode == 'B') {
bside = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!bside || bside.organization == null) return false;
}
let { offset, limit, searchOptions, sortOptions } = body;
offset = (offset - 1) * limit;
let attributes = ['mountpoint', 'original_msg', 'created_at'],
where = uidLastCode == 'B' ? { organization: bside.organization } : { user_uid: uid }, order = [], dbOptions, joinSubstring = [],
substring = ['mountpoint', 'original_msg'], key = [], between = ['created_at'];
attributes.forEach(element => {
if (searchOptions[element]) {
if (searchOptions[element] == '*空') {
if (joinSubstring.includes(element)) {
where[element] = null;
} else {
where[element] = [null, ''];
}
} else if (substring.includes(element)) {
where[element] = { [Op.substring]: searchOptions[element] };
} else if (key.includes(element)) {
where[element] = searchOptions[element] == "1" ? true : false;
} else if (between.includes(element)) {
where[element] = { [Op.between]: [searchOptions[element][0], searchOptions[element][1]] };
}
}
if (sortOptions[element] == true) {
order.push([element, 'ASC']);
} else if (sortOptions[element] == false) {
order.push([element, 'DESC']);
}
});
dbOptions = { attributes, offset, limit, where, order };
const model = {
"B": ctx.model.BNtripMsg,
"C": ctx.model2.BNtripMsg,
"T": ctx.model4.BNtripMsg
};
const result = await model[uidLastCode].findAndCountAll(dbOptions);
return result.rows.length == 0 ? false : result;
} catch (error) {
const blockName = fileName + "--" + "getHistoryMsg";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 删除历史消息
async delHistoryMsg(uid, modelType, password, requestMsg) {
const { ctx } = this;
try {
const uidLastCode = shunt(uid);
const model = {
"B": ctx.model.BSide,
"C": ctx.model2.CSide,
"T": ctx.model4.Account,
};
const userWhere = uidLastCode == 'B' ? { uid, status: { [Op.not]: 2 }, role: 1 } : { uid };
const result = await model[uidLastCode].findOne({ where: userWhere });
if (!result || result.dataValues.password !== this.shaForm(password, this.decryptRequest(result.dataValues.salt))) return false;
let where = {};
if (modelType == "single") {
const { mountpoint, original_msg, origin_time } = requestMsg;
where = uidLastCode == 'B' ? { organization: bside.organization, mountpoint, original_msg, created_at: origin_time } : { user_uid: uid, mountpoint, original_msg, created_at: origin_time }
} else if (modelType == "multiple") {
let mountpoint = [], original_msg = [], created_at = [];
for (const key in requestMsg) {
requestMsg[key].forEach(element => {
mountpoint.push(element.mountpoint),
original_msg.push(element.original_msg),
created_at.push(element.origin_time)
});
}
where = uidLastCode == 'B' ? { organization: bside.organization, mountpoint, original_msg, created_at } : { user_uid: uid, mountpoint, original_msg, created_at }
} else if (modelType == "all") {
where = uidLastCode == 'B' ? { organization: bside.organization } : { user_uid: uid }
}
const modelName = {
"B": ctx.model.BNtripMsg,
"C": ctx.model2.BNtripMsg,
"T": ctx.model4.BNtripMsg
};
const resetList = await modelName[uidLastCode].destroy({ where });
return resetList.length == 0 ? false : resetList;
} catch (error) {
const blockName = fileName + "--" + "delHistoryMsg";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
// 获取单位用户
async bsideManager(uid, body) {
const { ctx } = this;
try {
const result = await ctx.model.BSide.findOne({ where: { uid, status: { [Op.not]: 2 }, role: 1 } });
if (!result || result.organization == null) return false;
let { offset, limit, searchOptions } = body;
offset = (offset - 1) * limit;
let attributes = ['account_name', 'user_name', 'phone'],
where = { organization: result.organization, role: { [Op.not]: 1 } }, dbOptions, joinSubstring = [];
attributes.forEach(element => {
if (searchOptions[element]) {
if (searchOptions[element] == '*空') {
if (joinSubstring.includes(element)) {
where[element] = null;
} else {
where[element] = [null, ''];
}
} else {
where[element] = { [Op.substring]: searchOptions[element] };
}
}
});
dbOptions = { attributes, offset, limit, where };
const userList = await ctx.model.BSide.findAndCountAll(dbOptions);
return userList.rows.length == 0 ? false : userList;
} catch (error) {
const blockName = fileName + "--" + "bsideManager";
ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error);
return 'error';
}
}
}
module.exports = HomeService;