groups upd, more org settings, bug fixes

This commit is contained in:
Ivan 2023-09-14 05:40:03 +03:00
parent ccb1931d02
commit 8ec567409b
Signed by untrusted user who does not match committer: ppechenkoo
GPG Key ID: 0C191B86D9582583
13 changed files with 1240 additions and 202 deletions

643
server.js
View File

@ -46,12 +46,14 @@ app.get("/register", register);
app.get("/live", live); app.get("/live", live);
app.get("/reports", reports); app.get("/reports", reports);
app.get("/devices", devices); app.get("/devices", devices);
app.get("/devices/drivers", drivers); // app.get("/devices/drivers", drivers);
app.get("/devices/update", update); app.get("/devices/update", update);
app.get("/devices/groups", groups)
app.get("/videos", videos); app.get("/videos", videos);
app.get("/videos/export",videoExport); app.get("/videos/export",videoExport);
app.get("/settings", settings); app.get("/settings", settings);
app.get("/admin", adminPanel); app.get("/admin", adminPanel);
app.get("/admin/organisation", organisation);
const connectionProperties = { const connectionProperties = {
host: process.env.SSH_HOST, host: process.env.SSH_HOST,
@ -673,28 +675,37 @@ async function live(req, res) {
templateData.Count = templateData.Alarms.length; templateData.Count = templateData.Alarms.length;
// Получаем список групп и их идентификаторов из таблицы groups
const groupsQuery = "SELECT id, name FROM groups";
const groupsResult = await client.query(groupsQuery);
const groupsMap = {};
groupsResult.rows.forEach((group) => {
groupsMap[group.id] = group.name;
});
// Выполняем запрос, чтобы получить все данные из таблицы registrars // Выполняем запрос, чтобы получить все данные из таблицы registrars
const queryRegistrars = ` const queryRegistrars = `
SELECT id, serial, channels, lastkeepalive, name, "group", plate, sim, ip, port SELECT id, serial, channels, lastkeepalive, "group", name, plate, sim, ip, port
FROM registrars FROM registrars
ORDER BY id ORDER BY id
`; `;
const registrarsResult = await client.query(queryRegistrars); const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows; const allRegistrars = registrarsResult.rows;
// Группируем устройства по группам и сохраняем данные каждого устройства
const groupedRegistrars = {}; const groupedRegistrars = {};
allRegistrars.forEach((registrar) => { allRegistrars.forEach((registrar) => {
if (!groupedRegistrars[registrar.group]) { const groupName = groupsMap[registrar.group] || "Другое"; // Используем "Другое", если группа неизвестна
groupedRegistrars[registrar.group] = []; if (!groupedRegistrars[groupName]) {
groupedRegistrars[groupName] = [];
} }
groupedRegistrars[registrar.group].push({ groupedRegistrars[groupName].push({
id: registrar.id, id: registrar.id,
serial: registrar.serial, serial: registrar.serial,
channels: registrar.channels, channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis, status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name, name: registrar.name,
group: registrar.group, group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate, plate: registrar.plate,
sim: registrar.sim, sim: registrar.sim,
ip: registrar.ip, ip: registrar.ip,
@ -702,13 +713,23 @@ async function live(req, res) {
}); });
}); });
// Заполняем массив групп данными устройств в каждой группе templateData.Registrars = allRegistrars.map((registrar) => ({
for (const groupName in groupedRegistrars) { id: registrar.id,
templateData.Groups.push({ serial: registrar.serial,
channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name,
group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate,
sim: registrar.sim,
ip: registrar.ip,
port: registrar.port,
}));
templateData.Groups = Object.keys(groupedRegistrars).map((groupName) => ({
name: groupName, name: groupName,
devices: groupedRegistrars[groupName], devices: groupedRegistrars[groupName],
}); }));
}
const source = fs.readFileSync("static/templates/live.html", "utf8"); const source = fs.readFileSync("static/templates/live.html", "utf8");
@ -966,31 +987,37 @@ async function reports(req, res) {
}; };
})) }))
// Выполняем запрос, чтобы получить все данные из таблицы registrars const groupsQuery = "SELECT id, name FROM groups";
const queryRegistrars = ` const groupsResult = await client.query(groupsQuery);
SELECT id, serial, lastkeepalive, name, "group", plate, sim, ip, port const groupsMap = {};
FROM registrars groupsResult.rows.forEach((group) => {
ORDER BY id groupsMap[group.id] = group.name;
`;
const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows;
// Группируем устройства по группам
const groupedRegistrars = {};
allRegistrars.forEach((registrar) => {
if (!groupedRegistrars[registrar.group]) {
groupedRegistrars[registrar.group] = [];
}
groupedRegistrars[registrar.group].push(registrar.serial);
}); });
// Заполняем массив групп и серийными номерами устройств в каждой группе const minuteInMillis = 90 * 1000;
for (const groupName in groupedRegistrars) {
templateData.Groups.push({ // Выполняем запрос, чтобы получить все данные из таблицы registrars
const queryRegistrars = `
SELECT id, serial, lastkeepalive, "group", name, plate, sim, ip, port
FROM registrars
ORDER BY id
`;
const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows;
const groupedRegistrars = {};
allRegistrars.forEach((registrar) => {
const groupName = groupsMap[registrar.group] || "Другое"; // Используем "Другое", если группа неизвестна
if (!groupedRegistrars[groupName]) {
groupedRegistrars[groupName] = [];
}
groupedRegistrars[groupName].push(registrar.serial);
});
templateData.Groups = Object.keys(groupedRegistrars).map((groupName) => ({
name: groupName, name: groupName,
serials: groupedRegistrars[groupName], serials: groupedRegistrars[groupName],
}); }));
}
const source = fs.readFileSync( const source = fs.readFileSync(
"static/templates/reports/index.html", "static/templates/reports/index.html",
@ -1707,15 +1734,17 @@ async function devices(req, res) {
if (req.session.userId === undefined) { if (req.session.userId === undefined) {
return res.redirect("/signin"); return res.redirect("/signin");
} }
const userInfo = await getUserInfo(req.session.userId);
let userInfo;
let templateData = { let templateData = {
Organisation: userInfo.Organisation, Organisation: '',
User: userInfo.User, User: '',
UserInfo: userInfo.Users, UserInfo: '',
isAdmin: req.session.userId === 'admin', isAdmin: req.session.userId === 'admin',
ifDBError: false, ifDBError: false,
Registrars: [], Registrars: [],
Groups: [], Groups: [],
GroupsList: [],
}; };
try { try {
@ -1728,48 +1757,59 @@ async function devices(req, res) {
}); });
const client = await pool.connect(); const client = await pool.connect();
const groupsQuery = "SELECT id, name FROM groups";
const groupsResult = await client.query(groupsQuery);
const groupsMap = {};
groupsResult.rows.forEach((group) => {
groupsMap[group.id] = group.name;
});
const minuteInMillis = 90 * 1000; const minuteInMillis = 90 * 1000;
// Выполняем запрос, чтобы получить все данные из таблицы registrars
const queryRegistrars = ` const queryRegistrars = `
SELECT id, serial, lastkeepalive, name, "group", plate, sim, ip, port SELECT id, serial, lastkeepalive, "group", name, plate, sim, ip, port
FROM registrars FROM registrars
ORDER BY id ORDER BY id
`; `;
const registrarsResult = await client.query(queryRegistrars); const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows; const allRegistrars = registrarsResult.rows;
// Определяем статус connected на основе lastkeepalive const groupedRegistrars = {};
templateData.Registrars = allRegistrars.map((registrar) => ({ allRegistrars.forEach((registrar) => {
const groupName = groupsMap[registrar.group] || "Другое"; // Используем "Другое", если группа неизвестна
if (!groupedRegistrars[groupName]) {
groupedRegistrars[groupName] = [];
}
groupedRegistrars[groupName].push(registrar.serial);
});
userInfo = await getUserInfo(req.session.userId);
templateData = {
Organisation: userInfo.Organisation,
User: userInfo.User,
UserInfo: userInfo.Users,
isAdmin: req.session.userId === 'admin',
ifDBError: false,
Registrars: allRegistrars.map((registrar) => ({
id: registrar.id, id: registrar.id,
serial: registrar.serial, serial: registrar.serial,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis, status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name, name: registrar.name,
group: registrar.group, group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate, plate: registrar.plate,
sim: registrar.sim, sim: registrar.sim,
ip: registrar.ip, ip: registrar.ip,
port: registrar.port, port: registrar.port,
})); })),
Groups: Object.keys(groupedRegistrars).map((groupName) => ({
name: groupName,
serials: groupedRegistrars[groupName],
})),
GroupsList: groupsResult.rows,
};
// Группируем устройства по группам console.log(templateData);
const groupedRegistrars = {};
allRegistrars.forEach((registrar) => {
if (!groupedRegistrars[registrar.group]) {
groupedRegistrars[registrar.group] = [];
}
groupedRegistrars[registrar.group].push(registrar.serial);
});
// Заполняем массив групп и серийными номерами устройств в каждой группе
for (const groupName in groupedRegistrars) {
templateData.Groups.push({
name: groupName,
serials: groupedRegistrars[groupName],
});
}
// console.log(templateData);
const source = fs.readFileSync("static/templates/devices/index.html", "utf8"); const source = fs.readFileSync("static/templates/devices/index.html", "utf8");
const template = handlebars.compile(source); const template = handlebars.compile(source);
@ -1777,12 +1817,64 @@ async function devices(req, res) {
res.send(resultT); res.send(resultT);
client.release(); client.release();
} catch (error) {
console.error(error);
if (templateData) {
templateData.ifDBError = true;
const source = fs.readFileSync(
"static/templates/devices/index.html",
"utf8"
);
const template = handlebars.compile(source);
const resultT = template(templateData);
res.send(resultT);
}
}
}
async function groups(req, res) {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
const userInfo = await getUserInfo(req.session.userId);
let templateData = {
Organisation: userInfo.Organisation,
User: userInfo.User,
ifDBError: false,
UserInfo: userInfo.Users,
isAdmin: req.session.userId === 'admin',
Groups: [],
};
try {
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
const client = await pool.connect();
const result = await client.query('SELECT id, name FROM groups');
const groups = result.rows;
client.release();
templateData.Groups = groups;
const source = fs.readFileSync("static/templates/devices/groups.html", "utf8");
const template = handlebars.compile(source);
const resultT = template(templateData);
res.send(resultT);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
templateData.ifDBError = true; templateData.ifDBError = true;
const source = fs.readFileSync( const source = fs.readFileSync(
"static/templates/devices/index.html", "static/templates/devices/groups.html",
"utf8" "utf8"
); );
const template = handlebars.compile(source); const template = handlebars.compile(source);
@ -1791,6 +1883,33 @@ async function devices(req, res) {
} }
} }
app.post('/update-group', async (req, res) => {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
try {
const { groupId, newName } = req.body;
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
const query = 'UPDATE groups SET name = $1 WHERE id = $2';
const values = [newName, groupId];
await pool.query(query, values);
res.status(200).json({ message: 'Данные группы обновлены успешно' });
} catch (error) {
console.error('Ошибка при обновлении данных группы:', error);
res.status(500).json({ error: 'Внутренняя ошибка сервера' });
}
});
async function getParameterByName(serial, fieldName) { async function getParameterByName(serial, fieldName) {
const requestPayload = { const requestPayload = {
FIELDS: [fieldName], FIELDS: [fieldName],
@ -2286,6 +2405,34 @@ app.post("/userdata", async (req, res) => {
} }
}); });
app.post("/groupdata", async (req, res) => {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
const id = req.body.id;
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
const client = await pool.connect();
try {
// Выполняем запрос и получаем результат
const query = "SELECT * FROM groups WHERE id = $1;";
const userdata = await client.query(query, [id]);
// Формирование и отправка ответа
const response = userdata.rows[0];
res.json(response);
} finally {
client.release();
}
});
app.post("/deletedriver", async (req, res) => { app.post("/deletedriver", async (req, res) => {
if (req.session.userId === undefined) { if (req.session.userId === undefined) {
return res.redirect("/signin"); return res.redirect("/signin");
@ -2340,6 +2487,70 @@ app.post("/deleteuser", async (req, res) => {
} }
}); });
app.post("/deletegroup", async (req, res) => {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
const id = req.body.id;
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
const client = await pool.connect();
try {
// Выполняем запрос и получаем результат
const query = "DELETE FROM groups WHERE id = $1;";
const userdata = await client.query(query, [id]);
// Формирование и отправка ответа
res.send("Data deleted successfully");
} finally {
client.release();
}
});
app.post("/add-group", async (req, res) => {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
const { name } = req.body;
try {
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
const client = await pool.connect();
const query = `
INSERT INTO groups (name)
VALUES ($1)
RETURNING id
`;
const result = await client.query(query, [name]);
// Освобождение клиента
client.release();
console.log("Группа успешно добавлена");
res.json({ message: "Группа успешно добавлена" });
} catch (err) {
console.error("Ошибка при вставке данных в базу данных:", err);
res.status(500).json({ error: "Ошибка при добавлении пользователя" });
}
});
async function drivers(req, res) { async function drivers(req, res) {
if (req.session.userId === undefined) { if (req.session.userId === undefined) {
return res.redirect("/signin"); return res.redirect("/signin");
@ -2483,6 +2694,72 @@ async function settings(req, res) {
} }
} }
async function organisation(req, res) {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
if (req.session.userId != "admin") {
return res.redirect("/signin");
}
const userInfo = await getUserInfo(req.session.userId);
let templateData = {
Organisation: userInfo.Organisation,
User: userInfo.User,
ifDBError: false,
UserInfo: userInfo.Users,
isAdmin: req.session.userId === 'admin',
};
try {
const source = fs.readFileSync("static/templates/admin/organisation.html", "utf8");
const template = handlebars.compile(source);
const resultT = template(templateData);
res.send(resultT);
} catch (error) {
console.error(error);
templateData.ifDBError = true;
const source = fs.readFileSync(
"static/templates/admin/organisation.html",
"utf8"
);
const template = handlebars.compile(source);
const resultT = template(templateData);
res.send(resultT);
}
}
app.post('/update-organisation', async (req, res) => {
if (req.session.userId === undefined) {
return res.redirect("/signin");
}
if (req.session.userId != "admin") {
return res.redirect("/signin");
}
try {
const { name } = req.body;
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
const client = await pool.connect();
await client.query('UPDATE main SET organisation = $1 WHERE id = 1', [name]);
client.release();
res.status(200).json({ message: 'Значение успешно обновлено' });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Произошла ошибка при обновлении значения' });
}
});
async function adminPanel(req, res) { async function adminPanel(req, res) {
if (req.session.userId === undefined) { if (req.session.userId === undefined) {
return res.redirect("/signin"); return res.redirect("/signin");
@ -2637,52 +2914,62 @@ app.get('/admin/user/:id', async (req, res) => {
}); });
const client = await pool.connect(); const client = await pool.connect();
const queryRegistrars = ` const groupsQuery = "SELECT id, name FROM groups";
SELECT id, serial, lastkeepalive, name, "group", plate, sim, ip, port const groupsResult = await client.query(groupsQuery);
FROM registrars const groupsMap = {};
ORDER BY id groupsResult.rows.forEach((group) => {
`; groupsMap[group.id] = group.name;
const registrarsResult = await client.query(queryRegistrars); });
const allRegistrars = registrarsResult.rows;
const groupedRegistrars = {}; const minuteInMillis = 90 * 1000;
allRegistrars.forEach((registrar) => {
if (!groupedRegistrars[registrar.group]) { const queryRegistrars = `
groupedRegistrars[registrar.group] = []; SELECT id, serial, lastkeepalive, "group", name, plate, sim, ip, port
} FROM registrars
groupedRegistrars[registrar.group].push({ serial: registrar.serial, checked: false }); ORDER BY id
`;
const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows;
const groupedRegistrars = {};
allRegistrars.forEach((registrar) => {
const groupName = groupsMap[registrar.group] || "Другое"; // Используем "Другое", если группа неизвестна
if (!groupedRegistrars[groupName]) {
groupedRegistrars[groupName] = [];
}
groupedRegistrars[groupName].push({
serial: registrar.serial,
checked: false,
});
});
const query = "SELECT * FROM users WHERE id = $1;";
const userdata = await client.query(query, [id]);
const response = userdata.rows[0];
if (response.devices && response.devices.length > 0) {
for (const groupName in groupedRegistrars) {
groupedRegistrars[groupName].forEach((serialObj) => {
serialObj.checked = response.devices.includes(serialObj.serial);
}); });
}
}
for (const groupName in groupedRegistrars) { templateData.Name = response.name;
templateData.Groups.push({ templateData.Surname = response.surname;
name: groupName, templateData.Email = response.email;
serials: groupedRegistrars[groupName], templateData.Phone = response.phone;
}); templateData.Password = response.password;
} templateData.Devices = response.devices;
templateData.DeleteTransport = response.deletetransport;
templateData.EditTransport = response.edittransport;
templateData.Update = response.update;
const query = "SELECT * FROM users WHERE id = $1;"; templateData.Groups = Object.keys(groupedRegistrars).map((groupName) => ({
const userdata = await client.query(query, [id]); name: groupName,
serials: groupedRegistrars[groupName],
// Формирование и отправка ответа }));
const response = userdata.rows[0];
if (response.devices && response.devices.length > 0) {
templateData.Groups.forEach((group) => {
group.serials.forEach((serial) => {
serial.checked = response.devices.includes(serial.serial);
});
});
}
templateData.Name = response.name;
templateData.Surname = response.surname;
templateData.Email = response.email;
templateData.Phone = response.phone;
templateData.Password = response.password;
templateData.Devices = response.devices;
templateData.DeleteTransport = response.deletetransport;
templateData.EditTransport = response.edittransport;
templateData.Update = response.update;
// console.log(templateData); // console.log(templateData);
@ -2808,39 +3095,37 @@ async function videos(req, res) {
const minuteInMillis = 60 * 1000; const minuteInMillis = 60 * 1000;
const query = ` // Получаем список групп и их идентификаторов из таблицы groups
SELECT id, serial, lastkeepalive FROM registrars ORDER BY id ASC const groupsQuery = "SELECT id, name FROM groups";
`; const groupsResult = await client.query(groupsQuery);
const registrars = await client.query(query); const groupsMap = {};
groupsResult.rows.forEach((group) => {
groupsMap[group.id] = group.name;
});
templateData.Registrars = registrars.rows.map((row) => ({
id: row.id,
serial: row.serial,
status: Date.now() - Date.parse(row.lastkeepalive) <= minuteInMillis,
}));
// Выполняем запрос, чтобы получить все данные из таблицы registrars // Выполняем запрос, чтобы получить все данные из таблицы registrars
const queryRegistrars = ` const queryRegistrars = `
SELECT id, serial, channels, lastkeepalive, name, "group", plate, sim, ip, port SELECT id, serial, channels, lastkeepalive, "group", name, plate, sim, ip, port
FROM registrars FROM registrars
ORDER BY id ORDER BY id
`; `;
const registrarsResult = await client.query(queryRegistrars); const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows; const allRegistrars = registrarsResult.rows;
// Группируем устройства по группам и сохраняем данные каждого устройства
const groupedRegistrars = {}; const groupedRegistrars = {};
allRegistrars.forEach((registrar) => { allRegistrars.forEach((registrar) => {
if (!groupedRegistrars[registrar.group]) { const groupName = groupsMap[registrar.group] || "Другое"; // Используем "Другое", если группа неизвестна
groupedRegistrars[registrar.group] = []; if (!groupedRegistrars[groupName]) {
groupedRegistrars[groupName] = [];
} }
groupedRegistrars[registrar.group].push({ groupedRegistrars[groupName].push({
id: registrar.id, id: registrar.id,
serial: registrar.serial, serial: registrar.serial,
channels: registrar.channels, channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis, status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name, name: registrar.name,
group: registrar.group, group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate, plate: registrar.plate,
sim: registrar.sim, sim: registrar.sim,
ip: registrar.ip, ip: registrar.ip,
@ -2848,13 +3133,23 @@ async function videos(req, res) {
}); });
}); });
// Заполняем массив групп данными устройств в каждой группе templateData.Registrars = allRegistrars.map((registrar) => ({
for (const groupName in groupedRegistrars) { id: registrar.id,
templateData.Groups.push({ serial: registrar.serial,
channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name,
group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate,
sim: registrar.sim,
ip: registrar.ip,
port: registrar.port,
}));
templateData.Groups = Object.keys(groupedRegistrars).map((groupName) => ({
name: groupName, name: groupName,
devices: groupedRegistrars[groupName], devices: groupedRegistrars[groupName],
}); }));
}
// console.log(templateData); // console.log(templateData);
@ -2902,55 +3197,61 @@ async function videoExport(req, res) {
const minuteInMillis = 60 * 1000; const minuteInMillis = 60 * 1000;
const query = ` // Получаем список групп и их идентификаторов из таблицы groups
SELECT id, serial, lastkeepalive FROM registrars ORDER BY id ASC const groupsQuery = "SELECT id, name FROM groups";
`; const groupsResult = await client.query(groupsQuery);
const registrars = await client.query(query); const groupsMap = {};
groupsResult.rows.forEach((group) => {
templateData.Registrars = registrars.rows.map((row) => ({ groupsMap[group.id] = group.name;
id: row.id, });
serial: row.serial,
status: Date.now() - Date.parse(row.lastkeepalive) <= minuteInMillis,
}));
// Выполняем запрос, чтобы получить все данные из таблицы registrars
const queryRegistrars = `
SELECT id, serial, channels, lastkeepalive, name, "group", plate, sim, ip, port
FROM registrars
ORDER BY id
`;
const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows;
// Группируем устройства по группам и сохраняем данные каждого устройства
const groupedRegistrars = {};
allRegistrars.forEach((registrar) => {
if (!groupedRegistrars[registrar.group]) {
groupedRegistrars[registrar.group] = [];
}
groupedRegistrars[registrar.group].push({
id: registrar.id,
serial: registrar.serial,
channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name,
group: registrar.group,
plate: registrar.plate,
sim: registrar.sim,
ip: registrar.ip,
port: registrar.port,
});
});
// Заполняем массив групп данными устройств в каждой группе
for (const groupName in groupedRegistrars) {
templateData.Groups.push({
name: groupName,
devices: groupedRegistrars[groupName],
});
}
// Выполняем запрос, чтобы получить все данные из таблицы registrars
const queryRegistrars = `
SELECT id, serial, channels, lastkeepalive, "group", name, plate, sim, ip, port
FROM registrars
ORDER BY id
`;
const registrarsResult = await client.query(queryRegistrars);
const allRegistrars = registrarsResult.rows;
const groupedRegistrars = {};
allRegistrars.forEach((registrar) => {
const groupName = groupsMap[registrar.group] || "Другое"; // Используем "Другое", если группа неизвестна
if (!groupedRegistrars[groupName]) {
groupedRegistrars[groupName] = [];
}
groupedRegistrars[groupName].push({
id: registrar.id,
serial: registrar.serial,
channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name,
group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate,
sim: registrar.sim,
ip: registrar.ip,
port: registrar.port,
});
});
templateData.Registrars = allRegistrars.map((registrar) => ({
id: registrar.id,
serial: registrar.serial,
channels: registrar.channels,
status: Date.now() - Date.parse(registrar.lastkeepalive) <= minuteInMillis,
name: registrar.name,
group: groupsMap[registrar.group] || "Другое",
plate: registrar.plate,
sim: registrar.sim,
ip: registrar.ip,
port: registrar.port,
}));
templateData.Groups = Object.keys(groupedRegistrars).map((groupName) => ({
name: groupName,
devices: groupedRegistrars[groupName],
}));
// console.log(templateData); // console.log(templateData);
const source = fs.readFileSync("static/templates/videos/export.html", "utf8"); const source = fs.readFileSync("static/templates/videos/export.html", "utf8");

View File

@ -1004,11 +1004,38 @@ tr:nth-child(even) {
object-fit: cover; object-fit: cover;
} }
.copyright { #copyright {
width: 50%; width: 50%;
position: absolute; position: absolute;
bottom: 10px; bottom: 10px;
text-align: center; text-align: center;
opacity: 0;
transition: opacity 0.4s;
}
#info-icon {
position: absolute;
bottom: 15px;
left: 15px;
cursor: pointer;
padding: 5px 16px;
border-radius: 50%;
font-size: 20px;
background: #FFFFFF69;
}
#info-icon.right {
right: 15px !important;
left: auto;
}
#info-icon a {
text-decoration: none;
color:rgba(0, 0, 0, 0.6);
}
#info-icon:hover + #copyright {
opacity: 1;
} }
.copyright a { .copyright a {
@ -1156,7 +1183,9 @@ tr:nth-child(even) {
background-color: #0000000a; background-color: #0000000a;
} }
#edit-user-form { #edit-user-form,
#edit-group-form,
#new-group-form {
width: 550px; width: 550px;
/* float: right; */ /* float: right; */
margin: 30px 0 0 44px; margin: 30px 0 0 44px;
@ -1166,7 +1195,9 @@ tr:nth-child(even) {
.update-info label, .update-info label,
.input-name, .input-name,
.add-user-form label, .add-user-form label,
#edit-user-form label { #edit-user-form label,
#edit-group-form label,
#new-group-form label {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
font-weight: 400; font-weight: 400;
@ -1187,7 +1218,12 @@ tr:nth-child(even) {
.new-parameters select, .new-parameters select,
.update-info input, .update-info input,
.add-user-form input, .add-user-form input,
#edit-user-form input { #edit-user-form input,
#edit-user-form select,
#edit-group-form input,
#edit-group-form select,
#new-group-form input,
#new-group-form select {
margin-top: 6px; margin-top: 6px;
margin-bottom: 10px; margin-bottom: 10px;
display: inline-block; display: inline-block;
@ -1211,7 +1247,9 @@ tr:nth-child(even) {
height: 25px; height: 25px;
} }
#edit-user-form input { #edit-user-form input,
#edit-group-form select,
#new-group-form select {
width: 500px; width: 500px;
} }
@ -1222,12 +1260,19 @@ tr:nth-child(even) {
.add-user-form input:hover, .add-user-form input:hover,
#edit-user-form input:hover, #edit-user-form input:hover,
#edit-user-form input:focus, #edit-user-form input:focus,
#edit-group-form select:hover,
#edit-group-form select:focus,
#new-group-form select:hover,
#new-group-form select:focus,
.update-info input:hover, .update-info input:hover,
.update-info input:focus { .update-info input:focus {
border: 1px solid rgba(0, 0, 0, 0.3); border: 1px solid rgba(0, 0, 0, 0.3);
} }
.new-parameters select { .new-parameters select,
#edit-user-form select,
#edit-group-form select,
#new-group-form select {
cursor: pointer; cursor: pointer;
width: 340px; width: 340px;
-moz-appearance: none; -moz-appearance: none;
@ -1239,7 +1284,9 @@ tr:nth-child(even) {
.new-parameters input::placeholder, .new-parameters input::placeholder,
.add-user-form input::placeholder, .add-user-form input::placeholder,
#edit-user-form input::placeholder { #edit-user-form input::placeholder,
#edit-group-form input::placeholder,
#new-group-form input::placeholder {
color: rgba(0, 0, 0, 0.25); color: rgba(0, 0, 0, 0.25);
} }

View File

@ -100,6 +100,7 @@
<nav> <nav>
<a class="selected" href="/admin">Пользователи</a> <a class="selected" href="/admin">Пользователи</a>
<a href="/admin/organisation">Организация</a>
</nav> </nav>
<section class="bg"> <section class="bg">
<section id="content" class="content"> <section id="content" class="content">

View File

@ -0,0 +1,259 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{Name}} {{Surname}}</title>
<link rel="stylesheet" href="../../styles/main.css" />
</head>
<body>
<header>
<h1>Аргус</h1>
<h2><span>/</span> {{Organisation}}</h2>
</header>
<section class="account-info">
<div id="account-main">
<img id="person" src="../../img/person.svg">
<span>{{User}}</span>
<img id="down" src="../../img/down.svg">
<img id="up" src="../../img/up.svg">
</div>
<a href="/logout"><div id="account-additional" class="additional">Выйти</div></a>
</section>
<section class="navigation">
<a href="/">
<div><img src="../../img/chart.svg">Главная</div>
</a>
<a href="/devices">
<div><img src="../../img/cloud.svg">Устройства</div>
</a>
<a href="/reports">
<div><img src="../../img/bubble.svg">Отчёты</div>
</a>
<a href="/live">
<div><img src="../../img/waves.svg">Трансляция</div>
</a>
<a href="/videos">
<div><img src="../../img/play.svg">Записи</div>
</a>
<a class="admin-panel" href="/admin">
<div class="selected"><img src="../../img/keyboard.svg">Админка</div>
</a>
<a class="settings" href="/settings">
<div><img src="../../img/gear.svg">Настройки</div>
</a>
</section>
<section class="main">
{{#if ifDBError}}
<section class="dberror">
<div class="erorr-container">
<img src="../../img/warning.svg"> <br>
<h1>Ошибка </h1> <br>
<span>Не удалось получить данные из БД</span>
<button type="button" onclick="location.reload();">Повторить попытку</button>
</div>
</section>
{{/if}}
<section style="display: none;" class="dberror" id="addInformation" >
<div class="erorr-container">
<div id="loader" class="loader">
<div class="square" id="sq1"></div>
<div class="square" id="sq2"></div>
<div class="square" id="sq3"></div>
<div class="square" id="sq4"></div>
<div class="square" id="sq5"></div>
<div class="square" id="sq6"></div>
<div class="square" id="sq7"></div>
<div class="square" id="sq8"></div>
<div class="square" id="sq9"></div>
</div>
<svg id="success-mark" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="108" height="108" fill="none" viewBox="0 0 108 108">
<g clip-path="url(#a)">
<path fill="#8086F9" fill-opacity=".85" d="M54 107.947c29.541 0 54-24.5 54-53.973C108 24.447 83.488 0 53.947 0 24.459 0 0 24.447 0 53.974c0 29.474 24.512 53.973 54 53.973Zm0-8.995c-24.988 0-44.947-20.002-44.947-44.978 0-24.976 19.906-44.978 44.894-44.978S99 28.998 99 53.974c0 24.976-20.012 44.978-45 44.978Zm-5.824-19.844c1.747 0 3.23-.846 4.289-2.487l24.194-38.046c.582-1.058 1.27-2.222 1.27-3.386 0-2.382-2.117-3.916-4.341-3.916-1.323 0-2.647.847-3.653 2.381l-21.97 35.241-10.43-13.493c-1.27-1.693-2.435-2.116-3.917-2.116-2.277 0-4.077 1.852-4.077 4.18 0 1.164.477 2.276 1.218 3.28l12.917 15.875c1.324 1.747 2.753 2.487 4.5 2.487Z"/>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h108v108H0z"/>
</clipPath>
</defs>
</svg>
<h1>Обновление организации</h1> <br>
<span id="status">Пожалуйста, подождите</span>
<button id="closeButton" style="display: none;" onclick="hideMessage()">Закрыть</button>
</div>
</section>
<div class="name">
<span>Панель управления</span>
</div>
<nav>
<a href="/admin">Пользователи</a>
<a class="selected" href="/admin/organisation">Организация</a>
</nav>
<section class="bg">
<section class="content">
<section class="for-table">
<section class="whole-width">
<h1>Данные организации</h1>
<h3>Здесь вы можете управлять данными вашей организации.</h3>
<form id="edit-user-form">
<div class="parameters-input">
<label for="user-name">Название<span style="color: rgba(255, 69, 58, 1);">*</span></label>
<input name="name" type="text" id="user-name" placeholder="Название организации" value="{{Organisation}}" required>
</div>
</form>
<div style="margin-left: 44px; margin-right: 72px; width: calc(100% - 44px - 72px);" class="horizontal-line"></div>
<button style="margin-top: 15px;" id="user-edit" class="button-purple" type="button">Обновить</button>
</section>
</section>
</section>
</section>
</section>
<script src="../../scripts/jquery.min.js"></script>
<script src="https://rawgit.com/RobinHerbots/Inputmask/5.x/dist/jquery.inputmask.js"></script>
<script>
$(document).ready(function(){
$('#user-phone').inputmask({"mask": "+7 (999) 999-9999"});
});
</script>
<script>
document.getElementById('user-edit').addEventListener('click', () => {
const name = document.getElementById('user-name').value;
// Создаем объект с данными для отправки
const data = { name };
const deleteConfirmation = document.getElementById("addInformation");
const loader = document.getElementById("loader");
const status = document.getElementById("status");
const closeButton = document.getElementById("closeButton");
const mark = document.getElementById("success-mark");
loader.style.display = "block";
closeButton.style.display = "none";
deleteConfirmation.style.display = "flex";
mark.style.display = "none";
fetch('/update-organisation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
showMessage("Данные успешно обновлены", true);
})
.catch(error => {
showMessage("Не удалось обновить данные", false);
console.error("Ошибка:", error);
});
});
function hideMessage() {
location.reload();
const deleteConfirmation = document.getElementById("addInformation");
deleteConfirmation.style.display = "none";
}
function showMessage(messageText, isSuccess) {
const loader = document.getElementById("loader");
const status = document.getElementById("status");
const closeButton = document.getElementById("closeButton");
const mark = document.getElementById("success-mark");
loader.style.display = "none";
status.textContent = messageText;
if (isSuccess) {
mark.style.display = "block";
status.style.color = "green";
} else {
status.style.color = "red";
}
closeButton.style.display = "block";
}
</script>
<script>
// Скрытие/Показ дополнительных меню аккаунта
const accountMain = document.getElementById('account-main');
const accountAdditional = document.getElementById('account-additional');
const accountUp = document.getElementById('up');
const accountDown = document.getElementById('down');
accountAdditional.style.display = 'none';
accountUp.style.display = 'none';
accountMain.addEventListener('click', () => {
if (accountAdditional.style.display === 'none') {
accountAdditional.style.display = 'flex';
accountUp.style.display = 'unset';
accountDown.style.display = 'none';
} else {
accountAdditional.style.display = 'none';
accountUp.style.display = 'none';
accountDown.style.display = 'unset';
}
});
</script>
<script>
const checkboxes = document.querySelectorAll('.organisation .checkbox-input');
checkboxes.forEach((checkbox) => {
checkbox.addEventListener('change', function() {
const devices = this.parentNode.querySelector('.area-devices');
if (this.checked) {
devices.style.display = 'block';
// Активируем дочерние чекбоксы
const childCheckboxes = devices.querySelectorAll('.device-filter');
childCheckboxes.forEach((childCheckbox) => {
childCheckbox.checked = true;
});
} else {
devices.style.display = 'none';
// Деактивируем дочерние чекбоксы
const childCheckboxes = devices.querySelectorAll('.device-filter');
childCheckboxes.forEach((childCheckbox) => {
childCheckbox.checked = false;
});
}
// Деактивируем дочерние чекбоксы, если родительский чекбокс не выбран
if (!this.checked) {
const childCheckboxes = devices.querySelectorAll('.device-filter');
childCheckboxes.forEach((childCheckbox) => {
childCheckbox.checked = false;
});
devices.style.display = 'none';
}
});
});
</script>
</body>
</html>

View File

@ -244,11 +244,9 @@
closeButton.style.display = "none"; closeButton.style.display = "none";
deleteConfirmation.style.display = "flex"; deleteConfirmation.style.display = "flex";
mark.style.display = "none"; mark.style.display = "none";
// Собираем данные из инпутов с классом "device" в массив
const deviceInputs = document.querySelectorAll('.device-serial:checked'); const deviceInputs = document.querySelectorAll('.device-serial:checked');
const devices = Array.from(deviceInputs).map(input => input.value); const devices = Array.from(deviceInputs).map(input => input.value);
// Собираем остальные данные из формы
const formData = { const formData = {
name: document.getElementById('user-name').value, name: document.getElementById('user-name').value,
surname: document.getElementById('user-surname').value, surname: document.getElementById('user-surname').value,
@ -258,7 +256,6 @@
EditTransport: document.getElementById('device-edit').checked, EditTransport: document.getElementById('device-edit').checked,
DeleteTransport: document.getElementById('device-delete').checked, DeleteTransport: document.getElementById('device-delete').checked,
Update: document.getElementById('update-do').checked, Update: document.getElementById('update-do').checked,
// Другие данные, которые необходимо включить
}; };
// Объединяем все данные в один объект // Объединяем все данные в один объект

View File

@ -0,0 +1,393 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{Name}} {{Surname}}</title>
<link rel="stylesheet" href="../../styles/main.css" />
</head>
<body>
<header>
<h1>Аргус</h1>
<h2><span>/</span> {{Organisation}}</h2>
</header>
<section class="account-info">
<div id="account-main">
<img id="person" src="../../img/person.svg">
<span>{{User}}</span>
<img id="down" src="../../img/down.svg">
<img id="up" src="../../img/up.svg">
</div>
<a href="/logout"><div id="account-additional" class="additional">Выйти</div></a>
</section>
<section class="navigation">
<a href="/">
<div><img src="../../img/chart.svg">Главная</div>
</a>
<a href="/devices">
<div class="selected"><img src="../../img/cloud.svg">Устройства</div>
</a>
<a href="/reports">
<div><img src="../../img/bubble.svg">Отчёты</div>
</a>
<a href="/live">
<div><img src="../../img/waves.svg">Трансляция</div>
</a>
<a href="/videos">
<div><img src="../../img/play.svg">Записи</div>
</a>
<a class="admin-panel" href="/admin">
<div><img src="../../img/keyboard.svg">Админка</div>
</a>
<a class="settings" href="/settings">
<div><img src="../../img/gear.svg">Настройки</div>
</a>
</section>
<section class="main">
{{#if ifDBError}}
<section class="dberror">
<div class="erorr-container">
<img src="../../img/warning.svg"> <br>
<h1>Ошибка </h1> <br>
<span>Не удалось получить данные из БД</span>
<button type="button" onclick="location.reload();">Повторить попытку</button>
</div>
</section>
{{/if}}
<section style="display: none;" class="dberror" id="addInformation" >
<div class="erorr-container">
<div id="loader" class="loader">
<div class="square" id="sq1"></div>
<div class="square" id="sq2"></div>
<div class="square" id="sq3"></div>
<div class="square" id="sq4"></div>
<div class="square" id="sq5"></div>
<div class="square" id="sq6"></div>
<div class="square" id="sq7"></div>
<div class="square" id="sq8"></div>
<div class="square" id="sq9"></div>
</div>
<svg id="success-mark" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="108" height="108" fill="none" viewBox="0 0 108 108">
<g clip-path="url(#a)">
<path fill="#8086F9" fill-opacity=".85" d="M54 107.947c29.541 0 54-24.5 54-53.973C108 24.447 83.488 0 53.947 0 24.459 0 0 24.447 0 53.974c0 29.474 24.512 53.973 54 53.973Zm0-8.995c-24.988 0-44.947-20.002-44.947-44.978 0-24.976 19.906-44.978 44.894-44.978S99 28.998 99 53.974c0 24.976-20.012 44.978-45 44.978Zm-5.824-19.844c1.747 0 3.23-.846 4.289-2.487l24.194-38.046c.582-1.058 1.27-2.222 1.27-3.386 0-2.382-2.117-3.916-4.341-3.916-1.323 0-2.647.847-3.653 2.381l-21.97 35.241-10.43-13.493c-1.27-1.693-2.435-2.116-3.917-2.116-2.277 0-4.077 1.852-4.077 4.18 0 1.164.477 2.276 1.218 3.28l12.917 15.875c1.324 1.747 2.753 2.487 4.5 2.487Z"/>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h108v108H0z"/>
</clipPath>
</defs>
</svg>
<h1 id="loader-header">Обновление группы</h1> <br>
<span id="status">Пожалуйста, подождите</span>
<button id="closeButton" style="display: none;" onclick="hideMessage()">Закрыть</button>
</div>
</section>
<div class="name">
<span>Устройства</span>
</div>
<nav>
<a href="/devices">Список устройств</a>
<a class="selected" href="/devices/groups">Группы</a>
<a class="update" href="/devices/update">Обновление ПО</a>
</nav>
<section class="bg">
<section class="content">
<section class="for-table">
<section class="whole-width">
<h1>Изменить данные группы</h1>
<h3>Здесь вы можете управлять названиями групп и удалять их.</h3>
<form id="edit-group-form">
<div class="parameters-input">
<label for="group-id">Название<span style="color: rgba(255, 69, 58, 1);">*</span></label>
<select name="group-id" id="group-id">
<option value="">Выберите название</option>
{{#each Groups}}
<option value="{{this.id}}">{{this.name}}</option>
{{/each}}
</select>
</div>
<div style="margin-top: 10px;" class="parameters-input">
<label for="new-name">Новое название</label>
<input name="new-name" type="text" id="new-name" placeholder="Введите новое название группы" required>
</div>
</form>
<div style="margin-left: 44px; margin-right: 72px; width: calc(100% - 44px - 72px);" class="horizontal-line"></div>
<button style="margin-top: 15px;" id="group-edit" class="button-purple" type="button">Обновить</button> <button style="margin-top: 15px; background-color: rgb(255, 69, 58); margin-left: 10px;" id="group-delete" class="button-purple" type="button" onclick="deleteUser();">Удалить</button>
</section>
<section style="margin-top: 25px;" class="whole-width">
<h1>Добавить новую группу</h1>
<form id="new-group-form">
<div class="parameters-input">
<label for="new-group-name">Название<span style="color: rgba(255, 69, 58, 1);">*</span></label>
<input name="name" type="text" id="new-group-name" placeholder="Введите название новой группы" required>
</div>
</form>
<div style="margin-left: 44px; margin-right: 72px; width: calc(100% - 44px - 72px);" class="horizontal-line"></div>
<button style="margin-top: 15px;" id="group-new" class="button-purple" type="button">Добавить</button>
</section>
</section>
</section>
</section>
</section>
<section style="display: none;" class="dberror" id="deleteConfirmation">
<div class="erorr-container">
<img src="../img/warning.svg"> <br>
<h1>Удаление группы </h1> <br>
<span>Вы уверены что хотите удалить <span id="driverDeleteInfo"></span>?</span>
<div class="buttons">
<button id="deleteCancel" onclick="closeDeletion();" style="display: inline-block; background-color: white; color: rgba(0, 0, 0, 0.7); margin-right: 5px;" type="button" onclick="deleteDriver()">Отменить</button>
<button id="deleteDriver" style="display: inline-block;" type="button">Подтвердить</button>
</div>
</div>
</section>
<script src="../../scripts/jquery.min.js"></script>
<script src="https://rawgit.com/RobinHerbots/Inputmask/5.x/dist/jquery.inputmask.js"></script>
<script>
$(document).ready(function(){
$('#user-phone').inputmask({"mask": "+7 (999) 999-9999"});
});
</script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const editButton = document.getElementById('group-edit');
const groupIdSelect = document.getElementById('group-id');
const newNameInput = document.getElementById('new-name');
groupIdSelect.addEventListener('change', () => {
const selectedOption = groupIdSelect.options[groupIdSelect.selectedIndex];
const groupName = selectedOption.textContent;
newNameInput.value = groupName;
});
editButton.addEventListener('click', async () => {
const groupId = document.getElementById('group-id').value;
const newName = document.getElementById('new-name').value;
if (!groupId || !newName) {
alert('Пожалуйста, выберите группу и введите новое имя');
return;
}
const deleteConfirmation = document.getElementById("addInformation");
const loader = document.getElementById("loader");
const status = document.getElementById("status");
const closeButton = document.getElementById("closeButton");
const mark = document.getElementById("success-mark");
loader.style.display = "block";
closeButton.style.display = "none";
deleteConfirmation.style.display = "flex";
mark.style.display = "none";
try {
const response = await fetch('/update-group', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ groupId, newName }),
});
if (response.ok) {
showMessage("Данные успешно обновлены", true);
} else {
showMessage("Не удалось обновить данные", false);
}
} catch (error) {
showMessage("Не удалось обновить данные", false);
console.error("Ошибка:", error);
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
const form = document.getElementById("new-group-form");
document.getElementById('group-new').addEventListener('click', () => {
event.preventDefault();
const newName = document.getElementById('new-group-name').value;
if (!newName) {
alert('Пожалуйста, выберите группу и введите новое имя');
return;
}
$('#loader-header').html("Добавление группы");
const deleteConfirmation = document.getElementById("addInformation");
const loader = document.getElementById("loader");
const status = document.getElementById("status");
const closeButton = document.getElementById("closeButton");
const mark = document.getElementById("success-mark");
loader.style.display = "block";
closeButton.style.display = "none";
deleteConfirmation.style.display = "flex";
mark.style.display = "none";
const formData = new FormData(form);
const jsonObject = {};
formData.forEach((value, key) => {
jsonObject[key] = value;
});
const jsonData = JSON.stringify(jsonObject);
fetch("/add-group", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: jsonData,
})
.then((response) => response.json())
.then((data) => {
showMessage("Группа успешно добавлена", true);
})
.catch((error) => {
showMessage("Не удалось добавить группу", false);
console.error("Ошибка:", error);
});
});
});
</script>
<script>
function hideMessage() {
location.reload();
const deleteConfirmation = document.getElementById("addInformation");
deleteConfirmation.style.display = "none";
}
function showMessage(messageText, isSuccess) {
const loader = document.getElementById("loader");
const status = document.getElementById("status");
const closeButton = document.getElementById("closeButton");
const mark = document.getElementById("success-mark");
loader.style.display = "none";
status.textContent = messageText;
if (isSuccess) {
mark.style.display = "block";
status.style.color = "green";
} else {
status.style.color = "red";
}
closeButton.style.display = "block";
}
</script>
<script>
function deleteUser() {
const id = document.getElementById('group-id').value;
const newName = document.getElementById('new-name').value;
if (!id) {
alert('Пожалуйста, выберите группу');
return;
}
var deleteConfirmation = $("#deleteConfirmation");
$.ajax({
url: "/groupdata",
method: "POST",
contentType: "application/json",
data: JSON.stringify({ id: id }),
success: function(response) {
$("#driverDeleteInfo").html(response.name);
document.getElementById('deleteDriver').setAttribute("onclick", `confirmDelete(${response.id})`);
document.getElementById('deleteConfirmation').style.display = "flex";
$("body").css("overflow", "hidden");
},
error: function() {
alert("Произошла ошибка при запросе к серверу.");
}
});
}
function closeDeletion() {
document.getElementById('deleteConfirmation').style.display = "none";
}
function confirmDelete(id) {
$.ajax({
url: "/deletegroup",
method: "POST",
contentType: "application/json",
data: JSON.stringify({ id: id }),
success: function(response) {
location.reload();
},
error: function() {
alert("Произошла ошибка при запросе к серверу.");
}
});
}
</script>
<script>
// Скрытие/Показ дополнительных меню аккаунта
const accountMain = document.getElementById('account-main');
const accountAdditional = document.getElementById('account-additional');
const accountUp = document.getElementById('up');
const accountDown = document.getElementById('down');
accountAdditional.style.display = 'none';
accountUp.style.display = 'none';
accountMain.addEventListener('click', () => {
if (accountAdditional.style.display === 'none') {
accountAdditional.style.display = 'flex';
accountUp.style.display = 'unset';
accountDown.style.display = 'none';
} else {
accountAdditional.style.display = 'none';
accountUp.style.display = 'none';
accountDown.style.display = 'unset';
}
});
</script>
</body>
</html>

View File

@ -69,6 +69,7 @@
</div> </div>
<nav> <nav>
<a class="selected" href="/devices">Список устройств</a> <a class="selected" href="/devices">Список устройств</a>
<a href="/devices/groups">Группы</a>
<!-- <a href="/devices/drivers">Водители</a> --> <!-- <a href="/devices/drivers">Водители</a> -->
<!-- <a href="/devices/newdevice">Добавить устройство</a> --> <!-- <a href="/devices/newdevice">Добавить устройство</a> -->
<!-- <a href="/devices/newdriver">Добавить водителя</a> --> <!-- <a href="/devices/newdriver">Добавить водителя</a> -->
@ -206,7 +207,12 @@
</div> </div>
<div class="parameters-input"> <div class="parameters-input">
<label for="parameters-group">Группа<span style="color: rgba(255, 69, 58, 1);">*</span></label> <label for="parameters-group">Группа<span style="color: rgba(255, 69, 58, 1);">*</span></label>
<input name="deviceGroup" type="text" id="parameters-group" placeholder="Название группы" required> <select name="deviceGroup" id="parameters-group">
<option value="0">Другое</option>
{{#each GroupsList}}
<option value="{{this.id}}">{{this.name}}</option>
{{/each}}
</select>
</div> </div>
<div class="parameters-input"> <div class="parameters-input">
<label for="parameters-port">Порт<span style="color: rgba(255, 69, 58, 1);">*</span></label> <label for="parameters-port">Порт<span style="color: rgba(255, 69, 58, 1);">*</span></label>
@ -440,19 +446,25 @@
<div class="parameters-input"> <div class="parameters-input">
<label for="system-video">Формат видео</label> <label for="system-video">Формат видео</label>
<select name="VIDEOFORMAT" id="system-video"> <select name="VIDEOFORMAT" id="system-video">
<option value="0">0</option> <option value="0">800 x 600 px</option>
<option value="1">1</option> <option value="1">1024 x 768 px</option>
<option value="2">2</option> <option value="2">1280 x 1024 px</option>
<option value="3">1366 x 768 px</option>
<option value="4">1440 x 900 px</option>
<option value="5">720p</option>
<option value="6">1080I</option>
<option value="7">1080p</option>
<option value="8">480p</option>
<option value="9">576p</option>
</select> </select>
</div> </div>
<div class="parameters-input"> <div class="parameters-input">
<label for="system-stream">Формат трансляции</label> <label for="system-stream">Формат трансляции</label>
<select name="SUBSTREAMMODE" id="system-stream"> <select name="SUBSTREAMMODE" id="system-stream">
<option value="0">0</option> <option value="0">Плавно</option>
<option value="1">1</option> <option value="1">Менее качественно, но более плавно</option>
<option value="2">2</option> <option value="2">Менее плавно, но более качественно</option>
<option value="3">3</option> <option value="3">Качество</option>
<option value="4">4</option>
</select> </select>
</div> </div>
<div class="parameters-input"> <div class="parameters-input">

View File

@ -59,6 +59,7 @@
</div> </div>
<nav> <nav>
<a href="/devices">Список устройств</a> <a href="/devices">Список устройств</a>
<a href="/devices/groups">Группы</a>
<!-- <a href="/devices/drivers">Водители</a> --> <!-- <a href="/devices/drivers">Водители</a> -->
<!-- <a href="/devices/newdevice">Добавить устройство</a> --> <!-- <a href="/devices/newdevice">Добавить устройство</a> -->
<!-- <a href="/devices/newdriver">Добавить водителя</a> --> <!-- <a href="/devices/newdriver">Добавить водителя</a> -->

View File

@ -72,7 +72,7 @@
<span>Прямые трансляции</span> <span>Прямые трансляции</span>
</div> </div>
<nav> <nav>
<a class="selected" href="/live">Трансляции</a> <a class="selected" href="/live">Просмотр</a>
</nav> </nav>
<section class="bg"> <section class="bg">
<section class="content"> <section class="content">

View File

@ -34,7 +34,19 @@
<video class="animation right" autoplay muted loop> <video class="animation right" autoplay muted loop>
<source src="../img/traffic.mp4" type="video/mp4"> <source src="../img/traffic.mp4" type="video/mp4">
</video> </video>
<span class="copyright right"><a href="https://dribbble.com/shots/15608015-Traffic">Видеоматериал создан Igor Kozak для 10Clouds</a></span> <span class="copyright right" id="copyright"><a href="https://dribbble.com/shots/15608015-Traffic" target="_blank">Видеоматериал создан Igor Kozak для 10Clouds</a></span>
<span id="info-icon" class="right"><a href="https://dribbble.com/shots/15608015-Traffic" target="_blank">i</a></span>
<script>
document.getElementById('info-icon').addEventListener('mouseenter', function () {
document.getElementById('copyright').style.opacity = '1';
});
document.getElementById('info-icon').addEventListener('mouseleave', function () {
document.getElementById('copyright').style.opacity = '0';
});
</script>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {

View File

@ -26,7 +26,22 @@
<video class="animation left" autoplay muted loop> <video class="animation left" autoplay muted loop>
<source src="../img/traffic.mp4" type="video/mp4"> <source src="../img/traffic.mp4" type="video/mp4">
</video> </video>
<span class="copyright left"><a href="https://dribbble.com/shots/15608015-Traffic">Видеоматериал создан Igor Kozak для 10Clouds</a></span> <span class="copyright left" id="copyright"><a href="https://dribbble.com/shots/15608015-Traffic" target="_blank">Видеоматериал создан Igor Kozak для 10Clouds</a></span>
<span id="info-icon"><a href="https://dribbble.com/shots/15608015-Traffic" target="_blank">i</a></span>
<script>
document.getElementById('info-icon').addEventListener('mouseenter', function () {
document.getElementById('copyright').style.opacity = '1';
});
document.getElementById('info-icon').addEventListener('mouseleave', function () {
document.getElementById('copyright').style.opacity = '0';
});
</script>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {

View File

@ -107,7 +107,7 @@
<li class="device"> <li class="device">
<img> <img>
<input type="radio" name="camera-serial" id="radio-{{this.serial}}" class="radio-input" value="{{this.serial}}" hidden> <input type="radio" name="camera-serial" id="radio-{{this.serial}}" class="radio-input" value="{{this.serial}}" hidden>
<label for="radio-{{this.serial}}" class="radio-label"> <label for="radio-{{this.serial}}" class="radio-label active-{{this.status}}">
{{this.serial}} {{this.serial}}
</label> </label>
</li> </li>

View File

@ -90,7 +90,7 @@
<li class="device"> <li class="device">
<img> <img>
<input type="radio" name="camera-serial" id="radio-{{this.serial}}" class="radio-input" value="{{this.serial}}" hidden> <input type="radio" name="camera-serial" id="radio-{{this.serial}}" class="radio-input" value="{{this.serial}}" hidden>
<label for="radio-{{this.serial}}" class="radio-label"> <label for="radio-{{this.serial}}" class="radio-label active-{{this.status}}">
{{this.serial}} {{this.serial}}
</label> </label>
</li> </li>