Додана сторінка "Стенд"

Додане повідомлення про оновлення застосунку
Оновлен Service Worker
Перероблен WebSocket APІ
This commit is contained in:
2025-10-19 00:55:30 +03:00
parent 6ec6523d71
commit 3f08f3f6c9
46 changed files with 2651 additions and 2691 deletions

View File

@@ -15,7 +15,8 @@ db.serialize(() => {
uuid_manager TEXT,
appointment TEXT DEFAULT 'lamb',
mode INTEGER DEFAULT 0,
mode_title TEXT DEFAULT 'Користувач'
mode_title TEXT DEFAULT 'Користувач',
FOREIGN KEY (group_id) REFERENCES groups(group_number)
)
`);
@@ -40,7 +41,7 @@ db.serialize(() => {
db.run(`
CREATE TABLE IF NOT EXISTS groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_number INTEGER,
group_number INTEGER UNIQUE,
share_hash TEXT
)
`);
@@ -49,7 +50,11 @@ db.serialize(() => {
CREATE TABLE IF NOT EXISTS subscription (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sheep_id INTEGER,
token TEXT,
endpoint TEXT,
keys TEXT,
device_name TEXT,
device_model TEXT,
created_at TIMESTAMP,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
@@ -66,7 +71,6 @@ db.serialize(() => {
db.run(`
CREATE TABLE IF NOT EXISTS house (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER,
title TEXT,
number TEXT,
points TEXT DEFAULT '[]',
@@ -76,8 +80,7 @@ db.serialize(() => {
settlement TEXT,
description TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
FOREIGN KEY (group_id) REFERENCES groups(group_number)
updated_at TIMESTAMP
)
`);
@@ -87,10 +90,6 @@ db.serialize(() => {
house_id INTEGER,
entrance_number INTEGER,
title TEXT,
points TEXT DEFAULT '[]',
points_number TEXT DEFAULT '[]',
floors_quantity TEXT,
apartments_quantity TEXT,
description TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
@@ -106,9 +105,11 @@ db.serialize(() => {
date_start TIMESTAMP,
date_end TIMESTAMP,
group_id INTEGER,
sheep_id TEXT,
sheep_id INTEGER,
working INTEGER DEFAULT 0,
FOREIGN KEY (entrance_id) REFERENCES entrance(id)
FOREIGN KEY (entrance_id) REFERENCES entrance(id),
FOREIGN KEY (sheep_id) REFERENCES sheeps(id),
FOREIGN KEY (group_id) REFERENCES groups(group_number)
)
`);
@@ -121,20 +122,22 @@ db.serialize(() => {
floors_number INTEGER,
status INTEGER,
description TEXT,
sheep_id TEXT,
sheep_id INTEGER,
updated_at TIMESTAMP,
FOREIGN KEY (entrance_id) REFERENCES entrance(id)
FOREIGN KEY (entrance_id) REFERENCES entrance(id),
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS apartments_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sheep_id TEXT,
sheep_id INTEGER,
apartments_id INTEGER,
status INTEGER,
description TEXT,
created_at TIMESTAMP,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id),
FOREIGN KEY (apartments_id) REFERENCES apartments(id)
)
`);
@@ -142,18 +145,16 @@ db.serialize(() => {
db.run(`
CREATE TABLE IF NOT EXISTS homestead (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER,
title TEXT,
number TEXT,
points TEXT DEFAULT '[]',
point_icons TEXT DEFAULT '[]',
geo TEXT DEFAULT '[]',
zoom INTEGER DEFAULT 18,
osm_id TEXT DEFAULT '[]',
settlement TEXT,
description TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
FOREIGN KEY (group_id) REFERENCES groups(group_number)
updated_at TIMESTAMP
)
`);
@@ -165,9 +166,11 @@ db.serialize(() => {
date_start TIMESTAMP,
date_end TIMESTAMP,
group_id INTEGER,
sheep_id TEXT,
sheep_id INTEGER,
working INTEGER DEFAULT 0,
FOREIGN KEY (homestead_id) REFERENCES homestead(id)
FOREIGN KEY (homestead_id) REFERENCES homestead(id),
FOREIGN KEY (sheep_id) REFERENCES sheeps(id),
FOREIGN KEY (group_id) REFERENCES groups(group_number)
)
`);
@@ -177,7 +180,7 @@ db.serialize(() => {
date TIMESTAMP,
type INTEGER,
name TEXT,
sheep_id TEXT,
sheep_id INTEGER,
title TEXT,
number TEXT,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
@@ -188,23 +191,38 @@ db.serialize(() => {
CREATE TABLE IF NOT EXISTS stand_list (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
hour_start INTEGER DEFAULT 10,
hour_end INTEGER DEFAULT 16,
geo TEXT DEFAULT '[]',
hour_start INTEGER DEFAULT 9,
hour_end INTEGER DEFAULT 18,
quantity_sheep INTEGER DEFAULT 2,
week_days TEXT DEFAULT '[0, 1, 2, 3, 4, 5, 6]'
week_days TEXT DEFAULT '[0, 1, 2, 3, 4, 5, 6]',
processing_time REAL DEFAULT 1,
status INTEGER DEFAULT 0,
updated_at TIMESTAMP
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS stand_schedule (
id INTEGER PRIMARY KEY AUTOINCREMENT,
stand INTEGER,
stand_id INTEGER,
date TIMESTAMP,
hour INTEGER,
sheep_id TEXT,
sheep_id INTEGER,
number_sheep TEXT,
updated_at TIMESTAMP,
FOREIGN KEY (stand) REFERENCES stand_list(id),
FOREIGN KEY (stand_id) REFERENCES stand_list(id),
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS stand_schedule_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
stand_schedule_id INTEGER,
sheep_id INTEGER,
created_at TIMESTAMP,
FOREIGN KEY (stand_schedule_id) REFERENCES stand_schedule(id),
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);

View File

@@ -2,26 +2,20 @@ const HomesteadsService = require('../services/homesteads.service');
class HomesteadsController {
async getList(req, res) {
const { mode } = req.query;
const { mode, sheep_id } = req.query;
if (req.possibilities.can_view_territory) {
let group_id = 0;
let sheepName = false;
let id = null;
// if (req.mode == 2) {
// group_id = 0;
// } else if (req.mode == 1) {
// group_id = req.group_id;
// }
if (mode == "sheep") {
group_id = req.group_id;
sheepName = req.sheepName;
} else if (mode == "group"){
group_id = req.group_id;
if (mode == "admin" && req.possibilities.can_manager_territory) {
id = sheep_id;
} else if (mode == "sheep") {
id = req.sheepId;
} else if (mode == "group") {
id = req.group_id;
}
let result = await HomesteadsService.getList(group_id, sheepName);
let result = await HomesteadsService.getList(mode, id);
if (result) {
return res
.status(200)

View File

@@ -22,26 +22,20 @@ class HousesController {
}
async getList(req, res) {
const { mode } = req.query;
const { mode, sheep_id } = req.query;
if (req.possibilities.can_view_territory) {
let group_id = 0;
let sheepName = false;
let id = null;
// if (req.mode == 2) {
// group_id = 0;
// } else if (req.mode == 1) {
// group_id = req.group_id;
// }
if (mode == "sheep") {
group_id = req.group_id;
sheepName = req.sheepName;
} else if (mode == "group"){
group_id = req.group_id;
if (mode == "admin" && req.possibilities.can_manager_territory) {
id = sheep_id;
} else if (mode == "sheep") {
id = req.sheepId;
} else if (mode == "group") {
id = req.group_id;
}
let result = await HousesService.getList(group_id, sheepName);
let result = await HousesService.getList(mode, id);
if (result) {
return res
.status(200)

View File

@@ -4,7 +4,26 @@ class StandController {
async getStand(req, res) {
const { stand_id } = req.params;
return res.status(200).send({ stand_id });
if (stand_id) {
if (req.possibilities.can_view_stand) {
const result = await StandService.getStand(stand_id);
if (result) {
return res.status(200).send(result);
} else {
return res.status(404).send({
message: 'Stand not found.'
});
}
} else {
return res
.status(403)
.send({ message: 'The sheep does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'Stand not found.' });
}
}
async getList(req, res) {
@@ -23,7 +42,6 @@ class StandController {
.status(404)
.send({ message: 'The sheep does not have enough rights.' });
}
}
async createStand(req, res) {
@@ -130,7 +148,29 @@ class StandController {
}
async getScheduleList(req, res) {
return res.status(200).send({});
const { stand_id } = req.params;
if (stand_id) {
if (req.possibilities.can_view_stand) {
const result = await StandService.getScheduleList(stand_id);
if (result) {
return res.status(200).send(result);
} else {
return res
.status(404)
.send({ message: 'Schedule not found.' });
}
} else {
return res
.status(404)
.send({ message: 'The sheep does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'Stand id not found.' });
}
}
async getScheduleHistory(req, res) {

133
api/package-lock.json generated
View File

@@ -14,6 +14,7 @@
"exceljs": "^4.4.0",
"express": "^4.21.0",
"node-telegram-bot-api": "^0.66.0",
"pg": "^8.16.3",
"puppeteer": "^24.4.0",
"sharp": "^0.33.5",
"sqlite3": "^5.1.7",
@@ -4251,6 +4252,87 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"node_modules/pg": {
"version": "8.16.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
"dependencies": {
"pg-connection-string": "^2.9.1",
"pg-pool": "^3.10.1",
"pg-protocol": "^1.10.3",
"pg-types": "2.2.0",
"pgpass": "1.0.5"
},
"engines": {
"node": ">= 16.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.2.7"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4264,6 +4346,41 @@
"node": ">= 0.4"
}
},
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
@@ -5228,6 +5345,14 @@
"node": ">=0.10.0"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sqlite3": {
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz",
@@ -6007,6 +6132,14 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@@ -11,12 +11,12 @@
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^17.2.0",
"exceljs": "^4.4.0",
"express": "^4.21.0",
"node-telegram-bot-api": "^0.66.0",
"puppeteer": "^24.4.0",
"sharp": "^0.33.5",
"sqlite3": "^5.1.7",
"web-push": "^3.6.7",
"exceljs": "^4.4.0"
"web-push": "^3.6.7"
}
}

View File

@@ -15,7 +15,7 @@ router
.delete(authenticate, StandController.deleteStand);
router
.route('/schedule/list')
.route('/schedule/list/:stand_id')
.get(authenticate, StandController.getScheduleList)
router

View File

@@ -1,7 +1,7 @@
const db = require("../config/db");
class HomesteadsService {
getList(group, sheepName) {
getList(mode, id) {
return new Promise((res, rej) => {
let sql = `
SELECT
@@ -17,7 +17,7 @@ class HomesteadsService {
homestead
`;
if (group != "0" && !sheepName) {
if (mode == "group") {
sql = `
SELECT
homestead.*,
@@ -39,11 +39,9 @@ class HomesteadsService {
AND
homestead_history.name = 'Групова'
AND
homestead_history.group_id == '${group}'
homestead_history.group_id = '${id}'
`;
}
if (sheepName) {
} else if (mode == "sheep" || mode == "admin") {
sql = `
SELECT
homestead.*,
@@ -63,7 +61,7 @@ class HomesteadsService {
AND
homestead_history.working = 1
AND
homestead_history.name = '${sheepName}';
homestead_history.sheep_id = '${id}';
`;
}

View File

@@ -45,7 +45,7 @@ class HousesService {
"history": {
"id": row.entrance_history_id ? Number(row.entrance_history_id) : null,
"name": row.entrance_history_name,
"group_id": row.entrance_history_group_id ? Number(row.entrance_history_group_id) : null,
"group_id": row.entrance_history_group_id ? Number(row.entrance_history_group_id) : null,
"sheep_id": row.entrance_history_sheep_id ? Number(row.entrance_history_sheep_id) : null,
"date": {
"start": row.entrance_history_date_start ? Number(row.entrance_history_date_start) : null,
@@ -61,7 +61,7 @@ class HousesService {
});
}
getList(group, sheepName) {
getList(mode, id) {
return new Promise((res, rej) => {
let sql = `
SELECT
@@ -72,7 +72,7 @@ class HousesService {
house
`;
if (group != "0" && !sheepName) {
if (mode == "group") {
sql = `
SELECT DISTINCT
house.*,
@@ -89,11 +89,9 @@ class HousesService {
AND
entrance_history.name = 'Групова'
AND
entrance_history.group_id == '${group}'
entrance_history.group_id = '${id}'
`;
}
if (sheepName) {
} else if (mode == "sheep" || mode == "admin") {
sql = `
SELECT DISTINCT
house.*,
@@ -108,7 +106,7 @@ class HousesService {
WHERE
entrance_history.working = 1
AND
entrance_history.name = '${sheepName}'
entrance_history.sheep_id = '${id}'
`;
}

View File

@@ -4,7 +4,34 @@ const db = require("../config/db");
class StandService {
getStand(id) {
return new Promise((res, rej) => {
return res({ id });
const sql = `SELECT * FROM stand_list WHERE id = ?`;
db.get(sql, [id], (err, row) => {
if (err) {
console.error(err.message);
return res(false);
}
if (!row) {
console.log({ error: "id not found" });
return res(false);
}
let data = {
"id": Number(row.id),
"title": row.title,
"geo": JSON.parse(row.geo),
"hour_start": Number(row.hour_start),
"hour_end": Number(row.hour_end),
"quantity_sheep": Number(row.quantity_sheep),
"week_days": JSON.parse(row.week_days),
"processing_time": Number(row.processing_time),
"status": row.status == 1 ? true : false,
"updated_at": Number(row.updated_at)
}
return res(data);
});
});
}
@@ -165,7 +192,7 @@ class StandService {
if (row && row.max_date) {
date_start = getNextMonday(row.max_date); // заменить начальную дату
} else {
date_start = getNextMonday(Date.now()); // заменить начальную дату
date_start = getNextMonday(Date.now()); // заменить начальную дату
}
// 3. Генерация новых записей
@@ -206,7 +233,7 @@ class StandService {
console.error(err.message);
return res(false);
}
res({ status: "ok", inserted: list.length });
res({ status: "ok", inserted: list.length});
});
}
);
@@ -214,9 +241,45 @@ class StandService {
});
}
getScheduleList(data) {
getScheduleList(stand_id) {
return new Promise((res, rej) => {
return res({ data });
const sql = `
SELECT
ss.*,
s.name AS sheep_name
FROM
stand_schedule AS ss
LEFT JOIN
sheeps AS s
ON
s.id = ss.sheep_id
WHERE
ss.stand_id = ?
ORDER BY
ss.id;
`;
db.all(sql, [stand_id], (err, rows) => {
if (err) {
console.error(err.message);
return res(false);
} else {
let data = rows.map((row) => {
return {
"id": Number(row.id),
"stand_id": Number(row.stand_id),
"date": Number(row.date),
"hour": Number(row.hour),
"sheep_id": Number(row.sheep_id),
"sheep_name": row.sheep_name,
"number_sheep": Number(row.number_sheep),
"updated_at": Number(row.updated_at)
}
})
return res(data);
}
});
});
}