first commit

This commit is contained in:
xin
2024-07-19 13:58:44 +08:00
commit 3efe6cb5b6
42 changed files with 7606 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
ui

0
Readme.md Normal file
View File

2
server/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
node_modules

79
server/app.js Normal file
View File

@ -0,0 +1,79 @@
const express = require('express')
// 创建 express 的服务器实例
const app = express()
const mqtt = require('mqtt');
// 创建 MQTT 客户端实例
const client = mqtt.connect('mqtt://82.156.1.111:40000', {
clientId: 'web_collector',
username: 'xin',
password: 'irishk'
});
global.MqttClient=client;
// 引入body-parser
app.use(express.json())
app.use(express.urlencoded({extended:false}))
var session=require("express-session")
app.use(
session({
secret: 'iris',
resave: false,
saveUninitialized: true,
})
)
// write your code here...
const userRouter = require('./router/user')
const mqqtRouter=require('./router/mqtt_router');
const devinfoRouter=require('./router/devinfo')
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
//const repairrouter=require('./router/task')
app.use('/public', userRouter)
app.use('/mqtt', mqqtRouter)
app.use('/devinfo', devinfoRouter)
//app.use(express.static(__dirname+"/html"))
//app.use('/task', taskrouter)
app.use('/', (req, res,next)=>{
if (req.session.islogin!=true)
{
// res.send("need login");
// return;
}
next()
})
// app.use('/task', taskrouterinside)
// app.use('/repair', taskrouter)
app.use('/api/home1', (req, res)=>{
res.send("welcome to iris");
})
// 调用 app.listen 方法指定端口号并启动web服务器
app.listen(1000, function () {
console.log('api server running at http://127.0.0.1:1000')
})
const schedule = require('node-schedule');
const taskforupdate=require('./comman/frpclinet')
const scheduleCronstyle = ()=>{
//每分钟的第30秒定时执行一次:
schedule.scheduleJob('* 30 * * * *',()=>{
console.log('scheduleCronstyle:' + new Date());
taskforupdate.getfrpserver();
});
}
taskforupdate.getfrpserver();
scheduleCronstyle();

204
server/comman/db.js Normal file
View File

@ -0,0 +1,204 @@
const mysql = require('mysql')
const db = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: '',
database: 'remotemqtt',
})
let queryacy = function( sql, values ) {
// 返回一个 Promise
return new Promise(( resolve, reject ) => {
db.getConnection(function(err, connection) {
if (err) {
reject( err )
} else {
connection.query(sql, values, ( err, rows) => {
if ( err ) {
reject( err )
} else {
resolve( rows )
}
// 结束会话
//console.log(sql);
connection.release()
})
}
})
})
}
// const sqlite3 = require('sqlite3').verbose()
// //打开或者创建一个数据库
// var db = new sqlite3.Database(
// './db.sqlite3',
// sqlite3.OPEN_READWRITE,
// function (err) {
// if (err) {
// return console.log(err.message)
// }
// }
// )
// resetpath=async function(path)
// {
// db = new sqlite3.Database(
// path,
// sqlite3.OPEN_READWRITE,
// function (err) {
// if (err) {
// return console.log(err.message)
// }
// console.log('connect database successfully')
// }
// )
// db.run("CREATE TABLE IF NOT EXISTS remoteswitch (\
// `AUTOID` INTEGER PRIMARY KEY AUTOINCREMENT,\
// `id` tinytext NOT NULL,\
// `type` tinytext NOT NULL,\
// `tips` text NOT NULL,\
// `other` text NOT NULL,\
// `switch1` tinyint(1) NOT NULL,\
// `switch2` tinyint(1) NOT NULL,\
// `switch3` tinyint(1) NOT NULL,\
// `switch4` tinyint(1) NOT NULL,\
// `is4G` tinyint(1) NOT NULL,\
// `device` tinytext DEFAULT NULL)",(err)=>{console.log(err);
// })
// }
// // db.query=db.run
// //如果不存在 remoteswitch 表则创建 并打印创建成功 如果错误则打印错误
// function ConvertoKeyValue(data)
// {
// let fields = [];
// let fields1 = [];
// let fields2 = [];
// for (let key in data) {
// // 如果值是字符串,需要用引号包裹
// let value = typeof data[key] === 'string' ? `'${data[key]}'` : data[key];
// fields1.push(key);
// fields2.push( value);
// }
// fields.push(fields1.join(","))
// fields.push(fields2.join(","))
// return fields
// }
// function ConvertoKeyEQValue(data)
// {
// let fields = [];
// for (let key in data) {
// // 如果值是字符串,需要用引号包裹
// let value = typeof data[key] === 'string' ? `'${data[key]}'` : data[key];
// fields.push(key+"="+value);
// }
// return fields.join(",")
// }
db.query("CREATE TABLE IF NOT EXISTS remoteswitch (\
AUTOID INT AUTO_INCREMENT PRIMARY KEY,\
id TINYTEXT NOT NULL,\
type TINYTEXT NOT NULL,\
tips TEXT NOT NULL,\
other TEXT NOT NULL,\
switch1 TINYINT(1) NOT NULL,\
switch2 TINYINT(1) NOT NULL,\
switch3 TINYINT(1) NOT NULL,\
switch4 TINYINT(1) NOT NULL,\
is4G TINYINT(1) NOT NULL,\
device TINYTEXT DEFAULT NULL\
)",(err)=>{;
})
// db.all("CREATE TABLE IF NOT EXISTS remoteswitch1 (\
// `AUTOID` INTEGER PRIMARY KEY AUTOINCREMENT,\
// `id` tinytext NOT NULL,\
// `type` tinytext NOT NULL,\
// `tips` text NOT NULL,\
// `other` text NOT NULL,\
// `switch1` tinyint(1) NOT NULL,\
// `switch2` tinyint(1) NOT NULL,\
// `switch3` tinyint(1) NOT NULL,\
// `switch4` tinyint(1) NOT NULL,\
// `is4G` tinyint(1) NOT NULL,\
// `device` tinytext DEFAULT NULL)",(err)=>{console.log(err);
// })
// let init=async function()
// {
// db.run("CREATE TABLE IF NOT EXISTS remoteswitch (\
// `AUTOID` INTEGER PRIMARY KEY AUTOINCREMENT,\
// `id` tinytext NOT NULL,\
// `type` tinytext NOT NULL,\
// `tips` text NOT NULL,\
// `other` text NOT NULL,\
// `switch1` tinyint(1) NOT NULL,\
// `switch2` tinyint(1) NOT NULL,\
// `switch3` tinyint(1) NOT NULL,\
// `switch4` tinyint(1) NOT NULL,\
// `is4G` tinyint(1) NOT NULL,\
// `device` tinytext DEFAULT NULL)",(err)=>{console.log(err);
// console.log("remoteswitch table created")})
// }
// let queryacy = function( sql, values ) {
// // 返回一个 Promise
// return new Promise(( resolve, reject ) => {
// db.getConnection(function(err, connection) {
// if (err) {
// reject( err )
// } else {
// connection.query(sql, values, ( err, rows) => {
// if ( err ) {
// reject( err )
// } else {
// resolve( rows )
// }
// // 结束会话
// //console.log(sql);
// connection.release()
// })
// }
// })
// })
// }
// db.resetpath=resetpath
// db.query=db.all
db.queryacy= queryacy
// db.init=init
// db.ConvertoKeyValue=ConvertoKeyValue
// db.ConvertoKeyEQValue=ConvertoKeyEQValue
module.exports = db

458
server/comman/frpclinet.js Normal file
View File

@ -0,0 +1,458 @@
const frpserveraxios=require('axios');
const herader = {
'Content-Type': 'application/json',
'Authorization':"Basic YWRtaW46bGljYWhr"
}
const db=require("../comman/db")
const mywebsocket=require("./tstws")
exports.getfrpserver=getfrpserver
function createdfrpdb( )
{
const createTableQuery = `
CREATE TABLE IF NOT EXISTS frpinfo (
autoid BIGINT NOT NULL AUTO_INCREMENT,
id VARCHAR(255) NOT NULL,
remote_port INT NOT NULL,
serial VARCHAR(255) NOT NULL,
last_online DATETIME NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (autoid)
) ENGINE = InnoDB;
`;
db.query(createTableQuery, function (error, results, fields) {
// if (error) throw error;
// console.log('Table created or already exists.');
});
}
async function insertfrpinfo(data)
{
var insertdata={id:data.nameup,serial:data.serial,name:data.name}
if(data.status=="online")
{
insertdata.remote_port=data.conf.remote_port
insertdata.last_online=data.nowtime
}else
{
insertdata.remote_port=0
}
//查看看id是否存在
let sql="SELECT * FROM `frpinfo` WHERE `id`=?"
let result=await db.queryacy(sql,data.nameup)
if(result.length==0)
{
sql="INSERT INTO `frpinfo` SET ?"
if(insertdata.remote_port==0)
{
insertdata.last_online=new Date("2000-01-01").toLocaleString()
}
await db.queryacy(sql,insertdata)
//console.log("inserted")
}else
{
//如果remote_port为0 删除emote_port
if(insertdata.remote_port==0)
{
delete insertdata.remote_port
}
sql="UPDATE `frpinfo` SET ? WHERE `id`='"+data.nameup+"'"
await db.queryacy(sql,[insertdata])
// console.log("updated")
}
}
async function createdFileInfodb( )
{
const createTableQuery = 'CREATE TABLE IF NOT EXISTS filebrowerinfo (\
`autoid` INT NOT NULL AUTO_INCREMENT,\
`name` VARCHAR(255) NOT NULL,\
remote_port VARCHAR(255) NOT NULL,\
`lasttime` DATETIME NOT NULL,\
`sppercent` INT NOT NULL,\
`sptotal` FLOAT NOT NULL,\
mountedon VARCHAR(255) NOT NULL,\
PRIMARY KEY (`autoid`)\
) ENGINE = InnoDB;\
';
db.query(createTableQuery, function (error, results, fields) {
if (error) throw error;
console.log('Table created or already exists.');
});
}
// createdFileInfodb();
// createdfrpdb();
// getfrpserver();
let devlistname=[]
async function getfrpserver()
{
devlistname=[];
createdFileInfodb();
createdfrpdb();
sql='SELECT * FROM `filebrowerinfo` WHERE 1'
let result=await db.queryacy(sql)
// console.log(result)
for (var i=0;i<result.length;i++)
{
devlistname.push(result[i].name)
}
var aaa=await frpserveraxios.get('http://106.75.72.40:7500/api/proxy/tcp',{headers:herader})
nowtime=new Date()
nowtime=nowtime.toLocaleString()
aaa=aaa.data.proxies;
for (var i=0;i<aaa.length;i++)
{
if(aaa[i].name.endsWith('_data'))
{
var data=aaa[i]
//console.log(aaa[i].name)
//去除后缀
var name=aaa[i].name.replace('_data','')
//console.log(name)
name=name.toUpperCase()
var list=name.split('_')
var serial=list[list.length-1]
data.serial=serial
data.nowtime=nowtime
data.nameup=name
if (data.name=="TowerIS2_2006_data")
{
//将data.nameup 从devlistname中删除
console.log("delete TowerIS2_2006_data")
}
if (data.conf!=null && devlistname.includes(data.nameup))
{
//将data.nameup 从devlistname中删除
let index=devlistname.indexOf(data.nameup)
devlistname.splice(index,1)
console.log("delete "+data.nameup)
console.log(devlistname);
}
if(data.nameup=="NOCONFIG")
{
continue;
}
// console.log(data.name)
//年月日时分秒
insertfrpinfo(data);
//插入数据库 构建object
if(name.startsWith('TowerIS2'.toUpperCase()))
{
//将name转换为大写
await dealwithIS2(data)
}
if(name.startsWith('TOWER_ISIF')||name.startsWith('TOWER_OSIF'))
{
//将name转换为大写
await dealwithISIF(data)
}
if(name.startsWith('ASD_S'))
{
//将name转换为大写
await dealwithASD(data)
}
// // console.log("name:"+name+"\tserial:"+i+" "+name.startsWith('TOWER_OSIF'))
// if(name.startsWith('TOWER_OSIF'))
// {
// await dealwithISIF(data)
// }
}
}
console.log(devlistname)
console.log("done")
}
async function dealwithIS2(data)
{
// console.log(data)
// console.log("im IS2 my serial is:"+data.serial+"\tmy name is:"+data.nameup)
await panduanshebei(data)
}
async function dealwithISIF(data)
{
// console.log("im ISIF my serial is:"+data.serial+"\tmy name is:"+data.nameup)
await panduanshebei(data)
}
async function dealwithASD(data)
{
// console.log("im OSIF my serial is:"+data.serial+"\tmy name is:"+data.nameup)
await panduanshebei(data)
}
async function insettofilebrowerinfo(data)
{
let sql="SELECT * FROM `filebrowerinfo` WHERE `name`='"+data.name+"'"
let result=await db.queryacy(sql)
if(result.length==0)
{
sql="INSERT INTO `filebrowerinfo` SET ?"
await db.queryacy(sql,data)
}else
{
sql="UPDATE `filebrowerinfo` SET ? WHERE `name`='"+data.name+"'"
await db.queryacy(sql,data)
}
}
async function panduanshebei(data)
{
if(data.conf!=null)
{
let frpret=await getdevicelastdatatime(data.conf)
let timelast= frpret.timelast
let dataforinsert=
{
name:data.nameup,
lasttime:frpret.lasttimedata,
sppercent:frpret.space.percent,
sptotal:frpret.space.total/1024/1024+"GB",
remote_port:"http://106.75.72.40:"+data.conf.remote_port,
mountedon:frpret.space.mountedon
}
if (timelast<24.5)
{
console.log(data.name+"\t数据正常 "+timelast+"小时内新的日期文件创建");
await insettofilebrowerinfo(dataforinsert)
}else if(timelast==100000)
{
console.log("\x1b[33m"+data.name+"\t数据管理页面无法打开 请检查设备是否正常连接 \x1b[0m");
}
else
{
console.log("\x1b[31m"+data.name+"\t严重错误 数据异常"+timelast+"小时内没有新的日期文件创建 \x1b[0m");
await insettofilebrowerinfo(dataforinsert)
}
}else{
console.log("\x1b[33m"+ data.name+"\t设备未连接 \x1b[0m")
}
}
async function getdevicelastdatatime(device)
{
var ret={
timelast:100000,
space:{
percent:0,
total:0,
mountedon:" "
}
}
// console.log(device)
let port=device.remote_port;
let logaddress="http://106.75.72.40:"+port+"/api/login"
let passbody={
"username":"admin",
"password":"licahk"
}
let loginkey="";
try {
loginkey = await frpserveraxios.post(logaddress, passbody);
// 请求成功的处理逻辑
} catch (error) {
loginkey= 'ECONNRESET';
// 请求失败的处理逻辑
if (error.code === 'ECONNRESET') {
// 处理 socket hang up 错误
} else {
// 其他类型的错误处理
}
}
if(loginkey=='ECONNRESET')
{
ret.timelast=100000
return ret
}
//let loginkey=await frpserveraxios.post(logaddress,passbody).catch((err)=>{console.log(err)})
loginkey=loginkey.data;
let dataaddress="http://106.75.72.40:"+port+"/api/resources";
let datainfo=await frpserveraxios.get(dataaddress,{headers:{'X-Auth':loginkey}});
datainfo=datainfo.data;
//console.log(datainfo);
filelist=datainfo.items;
let diff=100000;
filelist.forEach(file => {
if(file.path.toUpperCase()=="DATA")
{
let filemodtime=file.modified
//计算与当前时间的差值 modified 举例2024-07-12T05:00:40.664988617+08:00
let now=new Date()
let nowtime=now.getTime()
let filetime=new Date(filemodtime)
ret.lasttimedata=filetime.toLocaleString();
let filetime1=filetime.getTime()
diff=nowtime-filetime1
diff=diff/1000/3600
}
});
ret.timelast=diff
if(port==13012)
{
ret.space.percent=0
ret.space.total=0
}
var wsaddres="ws://106.75.72.40:"+port+"/api/command/?auth="+loginkey;
var message="df /dev/mmcblk1p1"
var aa=await mywebsocket.runinws(message,wsaddres)
let commandret=aa[0].toString()
if(commandret=="error")
{
ret.space.percent=0
ret.space.total=0
}
else if(commandret=="Command not allowed.")
{
console.log("Command not allowed")
console.log("\x1b[31m"+device.proxy_name +" need allow df in filebrowser\x1b[0m")
}
else
{
if(aa.length==1)
{
let cc=aa[0].toString()
let bb=cc.split(":")
if(bb.length==3)
{
if (bb[2].trim()==" No such file or directory".trim())
{
console.log("No such file or directory")
ret.space.percent=0
ret.space.total=0
ret.space.mountedon="No SD card"
//return ret
}
}
console.log("error")
aa.push(Buffer.from("udev aa bb"))
}
let data=aa[1].toString()
let space=data.split(" ")
//移除space中的空格
space=space.filter(function(s) {
return s && s.trim();
});
if(space[0]=="udev")
{
message="df /home"
aa=await mywebsocket.runinws(message,wsaddres)
data=aa[1].toString()
space=data.split(" ")
space=space.filter(function(s) {
return s && s.trim();
});
}
ret.space.percent=space[4]
ret.space.total=space[1]
if(ret.space.mountedon!="No SD card")
ret.space.mountedon=space[5]
}
return ret;
}
// runinws("df /dev/mmcblk1p1","ws://106.75.72.40:30012/api/command/?auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJsb2NhbGUiOiJ6aC1jbiIsInZpZXdNb2RlIjoibGlzdCIsInNpbmdsZUNsaWNrIjpmYWxzZSwicGVybSI6eyJhZG1pbiI6dHJ1ZSwiZXhlY3V0ZSI6dHJ1ZSwiY3JlYXRlIjp0cnVlLCJyZW5hbWUiOnRydWUsIm1vZGlmeSI6dHJ1ZSwiZGVsZXRlIjp0cnVlLCJzaGFyZSI6dHJ1ZSwiZG93bmxvYWQiOnRydWV9LCJjb21tYW5kcyI6WyJkZiJdLCJsb2NrUGFzc3dvcmQiOmZhbHNlLCJoaWRlRG90ZmlsZXMiOmZhbHNlfSwiZXhwIjoxNzIxMDM3MjE0LCJpYXQiOjE3MjEwMzAwMTQsImlzcyI6IkZpbGUgQnJvd3NlciJ9.YNDbRregnpkZw6UFS0EJjX0DcCFQQoFmnCCusyFzstQ" )

158
server/comman/mqqtclinet.js Normal file
View File

@ -0,0 +1,158 @@
const mqtt = require('mqtt');
const db=require("./db")
// db.resetpath("../db.sqlite3")
// 创建 MQTT 客户端实例
const client = mqtt.connect('mqtt://82.156.1.111:40000', {
clientId: 'web_collectorserver',
username: 'xin',
password: 'irishk'
});
client.subscribe("topic_who_is_here_back");
// 定义事件处理程序
client.on('connect', () => {
console.log('MQTT client connected');
client.subscribe('my/topic');
});
function converjsontokeyvalue(data)
{
let fields = [];
let fields1 = [];
let fields2 = [];
for (let key in data) {
// 如果值是字符串,需要用引号包裹
let value = typeof data[key] === 'string' ? `'${data[key]}'` : data[key];
fields1.push(key);
fields2.push( value);
}
fields.push(fields1.join(","))
fields.push(fields2.join(","))
return fields
}
function converjsontokeyequalvalue(data)
{
let fields = [];
for (let key in data) {
// 如果值是字符串,需要用引号包裹
let value = typeof data[key] === 'string' ? `'${data[key]}'` : data[key];
fields.push(key+"="+value);
}
return fields.join(",")
}
client.on('message', async (topic, message) => {
if (topic==="topic_who_is_here_back")
{
var data=JSON.parse(message.toString())
data.switch1=data.st[0];
data.switch2=data.st[1];
data.switch3=data.st[2];
data.switch4=data.st[3];
if (data.switch1===undefined)
{
data.switch1=true
}
if (data.switch2===undefined)
{
data.switch2=true
}
if (data.switch3===undefined)
{
data.switch3=true
}
if (data.switch4===undefined)
{
data.switch4=true
}
if (data.id.split("_")[1]==="4G")
{
data.is4G=true
}else
{
data.is4G=false
}
data.device="nothing"
delete data.st;
let sql="SELECT * FROM `remoteswitch` WHERE `id`=?"
db.query(sql,data.id,function(err, rows){
if (1)
{
}
var result=rows
if (result.length===0)
{
console.log(data)
sql="insert into `remoteswitch` ( "+converjsontokeyvalue(data)[0]+" ) values ("+converjsontokeyvalue(data)[1]+")"
console.log(sql)
db.query(sql,(err,result)=>{
console.log(err)
console.log("insert ok")
})
}else
{
sql='update `remoteswitch` set '+converjsontokeyequalvalue(data)+' where id=?'
console.log(sql)
db.query(sql,[data.id],(err,result)=>{
console.log(err)
console.log("update ok")
})
}
})
//console.log(aaa)
}
// console.log('Received message:', topic, message.toString());
});
client.on('reconnect', () => {
console.log('MQTT client reconnected');
});
client.on('offline', () => {
console.log('MQTT client offline');
});
client.on('error', (err) => {
console.error('MQTT client error:', err);
});
// 在退出时关闭客户端连接
process.on('SIGINT', () => {
console.log('Exiting...');
client.end(() => {
console.log('MQTT client disconnected');
process.exit();
});
});
function getdevicemqtt() {
client.publish('topic_who_is_here', 'who is here');
}
exports.getdevicemqtt=getdevicemqtt

59
server/comman/mqqthttp.js Normal file
View File

@ -0,0 +1,59 @@
const axios=require("./mqttaxios");
async function queryDeviceStatus(deviceId) {
try {
const response = await axios.get('/api/v4/clients/', {
params: {
clientid: deviceId
}
});
//cl console.log(response)
const clients = response.data.data;
if (clients.length > 0) {
console.log(clients[0].connected);
console.log("on working");
return clients[0].connected;
} else {
console.log("not on working");
return false;
}
} catch (error) {
console.error(error);
return false;
}
}
async function queryalldeviceonline() {
const listofcliensidonline=new Array();
try {
const response = await axios.get('/api/v4/clients/');
//cl console.log(response)
const clients = response.data.data;
for (item in clients)
{
// console.log(clients[item])
listofcliensidonline.push(clients[item].clientid);
}
console.log(listofcliensidonline)
return listofcliensidonline;
} catch (error) {
console.error(error);
return listofcliensidonline;
}
}
function queryDeviceStatus(clientid,list)
{
return list.includes(clientid);
}
exports.queryalldeviceonline=queryalldeviceonline;
//queryalldeviceonline()

View File

@ -0,0 +1,10 @@
const myaxios = require('axios');
myaxios.defaults.baseURL = 'http://82.156.1.111:18083';
const mqtusername = 'admin';
const mqtpassword = 'licahk';
const input = mqtusername+":"+mqtpassword;
const token = Buffer.from(input).toString('base64');
//console.log(token);
myaxios.defaults.headers.common['Authorization'] = 'Basic ' + token;
module.exports = myaxios

View File

@ -0,0 +1,7 @@
const mqtt = require('mqtt');
const db=require("../comman/db")
const client = mqtt.connect('mqtt://82.156.1.111:40000', {
clientId: 'web_collector',
username: 'xin',
password: 'irishk'
});

46
server/comman/tstws.js Normal file
View File

@ -0,0 +1,46 @@
const WebSocket = require('ws');
async function sendMessageAndWaitForResponse(addres, message) {
return new Promise((resolve, reject) => {
const socket = new WebSocket(addres);
buffarr=[];
socket.on('open', () => {
socket.send(message);
});
// 监听服务器返回的消息
function messageHandler(response) {
// 在这里处理服务器返回的消息
// 可以根据需要解析 JSON 或其他处理
buffarr.push(response)
socket.off('error',errorHandler)
}
function errorHandler(err)
{
console.log(err)
buffarr.push(Buffer.from("error"))
resolve(buffarr);
}
socket.on('error',errorHandler);
// 监听服务器返回的消息
socket.on('message', messageHandler);
// 处理连接关闭情况
socket.on('close', () => {
resolve(buffarr);
});
});
}
async function runinws( message,addres){
let aa=await sendMessageAndWaitForResponse(addres,message)
return aa
// console.log(aa[1].toString())
}
exports.runinws=runinws

BIN
server/db.sqlite3 Normal file

Binary file not shown.

BIN
server/mqtt/db.sqlite3 Normal file

Binary file not shown.

4405
server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
server/package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "ars-server",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"axios": "^1.3.4",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.1",
"debug": "~2.6.9",
"express": "^4.19.2",
"express-session": "^1.17.3",
"mqtt": "^4.3.7",
"mysql": "^2.18.1",
"node-schedule": "^2.1.1",
"sqlite3": "^5.1.7",
"ws": "^8.18.0"
}
}

View File

@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}

6
server/router/devinfo.js Normal file
View File

@ -0,0 +1,6 @@
var express = require('express');
var router = express.Router();
const devinfoHandler = require('../router_handler/devinof_handle')
router.get('/getfilelistinfo', devinfoHandler.getfilelistinfo)
module.exports = router;

9
server/router/index.js Normal file
View File

@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;

View File

@ -0,0 +1,9 @@
const express = require('express')
const router = express.Router()
const mqttHandler = require('../router_handler/mqtt_router_handle')
router.post('/getSwitchStat', mqttHandler.getSwitchStat)
router.get('/getSwitchList', mqttHandler.getSwitchList)
router.get('/SetSwitch', mqttHandler.SetSwitch)
module.exports = router

13
server/router/user.js Normal file
View File

@ -0,0 +1,13 @@
const express = require('express')
const router = express.Router()
// 导入用户路由处理函数模块
const userHandler = require('../router_handler/user')
// 注册新用户
router.post('/reguser', userHandler.regUser)
// 登录
router.post('/login', userHandler.login)
router.get('/logout', userHandler.logout)
module.exports = router

View File

@ -0,0 +1,9 @@
const db=require("../comman/db")
exports.getfilelistinfo=(req, res) => {
sql='SELECT d.*, i.last_online FROM filebrowerinfo d JOIN frpinfo i ON d.name = i.id;'
db.query(sql,(err,result)=>{
res.send(result);
})
}

View File

@ -0,0 +1,152 @@
const db=require("../comman/db")
const mqttserver=require("../comman/mqqthttp")
const {log} = require("debug");
exports.getSwitchStat=(req, res) => {
}
exports.getSwitchList= async (req, res) => {
let listonline=await mqttserver.queryalldeviceonline()
sql='SELECT * FROM `remoteswitch` WHERE 1'
db.query(sql,(err,result)=>{
let devicelist=new Array()
for(rowindex in result) {
// console.log(result)
Data={
ID: rowindex,
SwitchID:result[rowindex].id,
Type:result[rowindex].type,
Detail:result[rowindex].tips,
SwitchData: [
{ Stat: result[rowindex].switch1===1?true:false, ID: 1},
{ Stat: result[rowindex].switch2===1?true:false, ID: 2},
{ Stat: result[rowindex].switch3===1?true:false, ID: 3},
{Stat: result[rowindex].switch4===1?true:false, ID: 4}
]
}
if (listonline.includes(result[rowindex].id))
{
Data.isonline=true;
}else
{
continue
Data.isonline=false;
}
//res.send(Data);
devicelist.push(Data);
}
//console.log(devicelist)
backdata={
devicelist:devicelist
}
res.send(backdata);
// console.log(devicelist[0].SwitchData)
// res.send("sdfsdfsdf");
// res.send(devicelist);
})
}
exports.SetSwitch=async (req, res) => {
req.body=req.query
//console.log(req)
Data={
id:req.body.Data.SwitchID,
sid:req.body.Data.number,
st:(req.body.Data.stat==="true"?1:0)
}
//console.log("data send")
// console.log(Data)
let mqttclient = global.MqttClient
mqttclient.publish("remoteShutter",JSON.stringify(Data));
message= await waitForMessage("remoteShuttersend")
message=JSON.parse(message)
// console.log("message send")
// console.log(message)
Data=req.body.Data
for (i=0;i<4;i++)
{
Data.SwitchData[i]=message.st[i]
}
delete Data.number
backdata={
index:req.body.index,
Data://Data
{
ID: req.body.Data.ID,
SwitchID:message.id,
Type:req.body.Data.Type,
Detail:req.body.Data.Detail,
isonline:true,
SwitchData: [
{ Stat: message.st[0], ID: 1},
{ Stat: message.st[1], ID: 2},
{ Stat: message.st[2], ID: 3},
{Stat: message.st[3], ID: 4}
]
}
}
console.log(backdata.Data)
res.send(backdata);
}
function waitForMessage(topic) {
return new Promise((resolve, reject) => {
let client = global.MqttClient
client.subscribe(topic, (err) => {
if (err) {
reject(err);
} else {
client.on('message', (t, message) => {
if (t === topic) {
client.unsubscribe(topic);
resolve(message.toString());
}
});
}
});
});
}

View File

@ -0,0 +1,100 @@
var db = require("../comman/db.js")
const bcrypt = require('bcryptjs')
/**
* 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用
*/
// 注册用户的处理函数
exports.regUser = (req, res) => {
var data = req.body;
if (data.Username == "" || data.Name == "" || data.Password == "") {
return res.send("传入数据非法")
}
const sqlStr = 'select * from employeelist where Username=?'
//res.setHeader("refresh","3; url=../index.html")
db.query(sqlStr, data.Username, (err, result) => {
if (err) {
return res.send(err)
}
if (result.length > 0) {
res.setHeader("refresh", "3; url=../html/register.html")
return res.send("用户已存在 请更换用户名")
}
delete data.configPassword
data.Password = bcrypt.hashSync(data.Password, 10)
data.Acess = 0
data.Pic = null
console.log(data);
const sql = "insert into employeelist set ?";
db.query(sql, data, (err, result) => {
console.log(result);
console.log(err)
res.setHeader("refresh", "3; url=../index.html")
res.send('reguser OK')
});
// res.setHeader('refresh:3; url=../index.html')
//res.render('new.html')
})
}
// 登录的处理函数
exports.login = (req, res) => {
const userinfo = req.body
// 定义 SQL 语句
const sql = `select * from employeelist where Username=?`
// 执行 SQL 语句,根据用户名查询用户的信息
db.query(sql, userinfo.Username, (err, results) => {
// 执行 SQL 语句失败
if (err) return res.send(err)
// 执行 SQL 语句成功,但是获取到的数据条数不等于 1
if (results.length !== 1) {
res.setHeader("refresh", "3; url=../html/login.html")
return res.send('登录失败!')
}
// TODO判断密码是否正确
const compareResult = bcrypt.compareSync(userinfo.Password, results[0].Password)
if (!compareResult) {
res.setHeader("refresh", "3; url=../html/login.html")
return res.send('登录失败!')
}
// TODO在服务器端生成 Token 的字符串
req.session.username=userinfo.Username;
req.session.islogin = true;
if (userinfo.Username=="renlixin"){
req.session.isadmin=true;
}else
{
req.session.isadmin=false;
}
var message = {
message: "login ok"
}
res.setHeader("refresh", "3; url=../index.html")
res.send(message.message)
})
}
exports.logout = (req, res) => {
req.session.islogin = false;
req.session.destroy();
res.setHeader("refresh", "3; url=../html/login.html")
res.send('logout ok')
}

24
ui2/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
ui2/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

5
ui2/README.md Normal file
View File

@ -0,0 +1,5 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

13
ui2/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

1357
ui2/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
ui2/package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "ui2",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.2",
"vue": "^3.4.31"
},
"devDependencies": {
"@arco-design/web-vue": "^2.55.3",
"@vitejs/plugin-vue": "^5.0.5",
"typescript": "^5.2.2",
"vite": "^5.3.4",
"vue-tsc": "^2.0.24"
}
}

1
ui2/public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

15
ui2/src/App.vue Normal file
View File

@ -0,0 +1,15 @@
<script setup >
import devinfoshow from './components/devinfoshow.vue'
import devinfoMain from './components/devinfoMain.vue';
</script>
<template>
<!-- <devinfoshow name="devinfoshow" /> -->
<devinfo-main></devinfo-main>
</template>
<style scoped>
</style>

1
ui2/src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<a-carousel
style="width: 100vw;height:100vh;"
:default-current="1"
:auto-play="{ interval: 5000}"
indicator-type="line"
show-arrow="always"
arrow-class="color:red !important;"
>
<a-carousel-item v-for="(item,index) in datalist" :key="index">
<devinfoshow :devinfo="item"></devinfoshow>
</a-carousel-item>
</a-carousel>
</template>
<script>
import devinfoshow from './devinfoshow.vue'
import axios from 'axios';
export default {
name: 'DevinfoMain',
components: {
devinfoshow
},
data() {
return {
datalist: []
// Your component's data goes here
}
},
mounted() {
this.onloaddata();
//每隔1分钟刷新一次
setInterval(() => {
this.onloaddata();
}, 60000);
},
methods: {
// Your component's methods go here
async onloaddata() {
const data=await axios.get('devinfo/getfilelistinfo');
let datalist=data.data;
//每9个数据为一组 不足的不补齐
let newdatalist=[];
let temp=[];
for(let i=0;i<datalist.length;i++)
{
const datestr=datalist[i].last_online
//如果上次时间距离现在超过1天则显示红色
let lasttime=new Date(datestr);
let nowtime=new Date();
datalist[i].from_last_online_time=(nowtime-lasttime)/60000;
}
for(let i=0;i<datalist.length;i++){
if(i%9==0&&i!=0){
newdatalist.push(temp);
temp=[];
}
temp.push(datalist[i]);
}
newdatalist.push(temp);
this.datalist=newdatalist;
// console.log(newdatalist);
}
}
// Your component's JavaScript logic goes here
}
</script>
<style >
/* Your component's CSS styles go here */
.arco-carousel-arrow > div > svg {
color: rgb(0, 255, 132) !important;
font-size: 20px;
width: 100px !important;
height: 100px !important;
}
.arco-carousel-arrow-right {
width: 100px !important;
}
.arco-carousel-arrow-left {
width: 100px !important;
}
</style>

View File

@ -0,0 +1,117 @@
<script>
import axios from 'axios';
export default {
name: 'devinfoshow',
data() {
return {
}
},
props: {
devinfo: {
type: Array,
required: true,
default:()=>[{
name: "设备序列号",
status: "在线",
lasttime: "2021-10-10 10:10:10",
sppercent:10,
sptotal:100,
mountedon:"No SD card",
}]
}
// Your component's props go here
},
methods: {
panduansd(dev){
if(dev.mountedon=="/"||dev.mountedon=="No SD card"){
return "red"
}else{
return "black"
}
},
paduanshijian(dev){
let datestr=dev.lasttime;
//如果上次时间距离现在超过1天则显示红色
let lasttime=new Date(datestr);
let nowtime=new Date();
if((nowtime-lasttime)>1000*60*60*24){
return "red"
}else{
return "black"
}
}
// Your component's methods go here
}
}
</script>
<template>
<a-card :bordered="false" :style="{ width: '100vw',margin:'auto' }" :hoverable="true">
<a-card-grid
v-for="(dev, index) in devinfo"
:key="index"
:hoverable="index % 2 === 0"
:style="{ width: '32%',height: '31vh',margin: '10px', background: paduanshijian(dev)==='red'?'#FFCCCC':'#CCFFCC'}"
>
<a-card
class="card-demo"
:title="dev.name"
:bordered="false"
>
<template #extra>
<a-link :href="dev.remote_port" target="_blank">查看数据</a-link>
</template>
<p :style="{ margin: 0,color:dev.from_last_online_time>30?'red':'green'}">
设备状态{{ dev.from_last_online_time>30?'离线'+dev.from_last_online_time+'分钟':'在线' }}
</p>
<p :style="{ margin: 0 ,color:paduanshijian(dev)}">
最新文件时间{{ dev.lasttime }}
</p>
<template #actions>
</template>
</a-card>
<p style="bottom: 0px; position: absolute; width: 100%; border-top: 1px solid #e8e8e8;margin-bottom: 0px;min-height: 10% "
:style="{background:panduansd(dev)==='red'?'#FFCCCC':'#CCFFCC'}">
<a-row class="grid-demo" :gutter="24" >
<a-col :span="14">
<div> SD: {{ dev.sptotal }}GB <a-progress :style="{ width: '60%' }" :percent="dev.sppercent/100" color="green" :status="dev.sppercent>90?'danger':'normal'"/></div>
</a-col>
<a-col :span="10">
<div :style="{color:panduansd(dev)}">挂载路径{{dev.mountedon }}</div>
</a-col>
</a-row>
</p>
</a-card-grid>
</a-card>
</template>
<style scoped>
/* Your component's CSS styles go here */
h1 {
color: blue;
}
.card-demo {
width: 100%;
}
</style>

15
ui2/src/main.ts Normal file
View File

@ -0,0 +1,15 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import axios from 'axios'
import ArcoVue from '@arco-design/web-vue';
import '@arco-design/web-vue/dist/arco.css';
console.log(window.location.hostname);
axios.defaults.baseURL = "http://"+window.location.hostname+":1000";
const app=createApp(App);
app.use(ArcoVue);
app.mount('#app');

80
ui2/src/style.css Normal file
View File

@ -0,0 +1,80 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
width: 100vw;
height: 100vh;
margin: 0 ;
padding: 0;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

1
ui2/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

27
ui2/tsconfig.app.json Normal file
View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

11
ui2/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
]
}

13
ui2/tsconfig.node.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true,
"noEmit": true
},
"include": ["vite.config.ts"]
}

10
ui2/vite.config.ts Normal file
View File

@ -0,0 +1,10 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server:{
host:'0.0.0.0'
},
})