This commit is contained in:
2025-09-09 00:10:53 +03:00
parent 38f2a05107
commit 204fc092d7
239 changed files with 22447 additions and 9536 deletions

View File

@@ -51,7 +51,12 @@ cd /home/rozenrod/webapps/sheep-service.com
``` ```
``` ```
docker-compose pull && docker-compose -p Sheep-Service up --build -d cd /home/rozenrod/webapps/sheep-service.com && docker-compose pull && docker-compose -p sheep-service up --build -d api ws web
```
or
```
cd /home/rozenrod/webapps/sheep-service.com && docker-compose pull && docker-compose --profile with-nginx -p sheep-service up --build -d
``` ```
<br> <br>
@@ -259,3 +264,28 @@ echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time;
``` ```
sudo /opt/certbot/bin/pip install --upgrade certbot certbot-nginx sudo /opt/certbot/bin/pip install --upgrade certbot certbot-nginx
``` ```
<br>
<br>
# CRONTAB
```
crontab -e
```
```
#####################
# Script for saving a backup copy of the Sheep-Service database every day at 22:30
30 22 * * * cd /home/rozenrod/webapps/sheep-service.com && /usr/bin/python3 backup.py >> /home/rozenrod/webapps/sheep-service.com/log/backup.log 2>&1
#####################
```
```
sudo pip3 install python-dotenv
```
<br>
<br>

View File

@@ -1,9 +1,10 @@
const express = require('express'); const express = require('express');
const app = express(); const app = express();
const routes = require('./routes/index'); const routes = require('./routes/index');
require("dotenv").config();
// const cors = require('cors'); // const cors = require('cors');
const port = 4000; const port = process.env.API_PORT || 4000;
// app.use(cors()) // app.use(cors())
app.use(express.json({ limit: '50mb' })); app.use(express.json({ limit: '50mb' }));

View File

@@ -4,31 +4,210 @@ const path = require('path');
const dbPath = process.env.DATABASE_PATH || '../'; const dbPath = process.env.DATABASE_PATH || '../';
const db = new sqlite3.Database(path.join(dbPath, 'database.sqlite')); const db = new sqlite3.Database(path.join(dbPath, 'database.sqlite'));
// db.serialize(() => { db.serialize(() => {
// db.run(` db.run(`
// CREATE TABLE IF NOT EXISTS sheeps ( CREATE TABLE IF NOT EXISTS sheeps (
// id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
// uuid TEXT UNIQUE group_id INTEGER,
// ) name TEXT,
// `); icon TEXT,
uuid TEXT,
uuid_manager TEXT,
appointment TEXT DEFAULT 'lamb',
mode INTEGER DEFAULT 0,
mode_title TEXT DEFAULT 'Користувач'
)
`);
// db.run(` db.run(`
// CREATE TABLE IF NOT EXISTS administrators ( CREATE TABLE IF NOT EXISTS possibilities (
// sheep_id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
// can_view_sheeps INTEGER DEFAULT 0, sheep_id INTEGER,
// FOREIGN KEY (sheep_id) REFERENCES sheeps(id) can_add_sheeps INTEGER DEFAULT 0,
// ) can_view_sheeps INTEGER DEFAULT 0,
// `); can_add_territory INTEGER DEFAULT 0,
can_view_territory INTEGER DEFAULT 0,
can_manager_territory INTEGER DEFAULT 0,
can_add_stand INTEGER DEFAULT 0,
can_view_stand INTEGER DEFAULT 0,
can_manager_stand INTEGER DEFAULT 0,
can_add_schedule INTEGER DEFAULT 0,
can_view_schedule INTEGER DEFAULT 0,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
// db.run(` db.run(`
// CREATE TABLE IF NOT EXISTS sessions ( CREATE TABLE IF NOT EXISTS groups (
// session_id TEXT PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
// sheep_id INTEGER, group_number INTEGER,
// role TEXT DEFAULT 'sheep', share_hash TEXT
// expires_at INTEGER, )
// FOREIGN KEY (sheep_id) REFERENCES sheeps(id) `);
// )
// `); db.run(`
// }); CREATE TABLE IF NOT EXISTS subscription (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sheep_id INTEGER,
token TEXT,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS badges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sheep_id INTEGER,
quantity INTEGER,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS house (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER,
title TEXT,
number TEXT,
points TEXT DEFAULT '[]',
points_number TEXT DEFAULT '[]',
geo TEXT DEFAULT '[]',
osm_id TEXT DEFAULT '[]',
settlement TEXT,
description TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
FOREIGN KEY (group_id) REFERENCES groups(group_number)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS entrance (
id INTEGER PRIMARY KEY AUTOINCREMENT,
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,
FOREIGN KEY (house_id) REFERENCES house(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS entrance_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entrance_id INTEGER,
name TEXT,
date_start TIMESTAMP,
date_end TIMESTAMP,
group_id INTEGER,
sheep_id TEXT,
working INTEGER DEFAULT 0,
FOREIGN KEY (entrance_id) REFERENCES entrance(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS apartments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entrance_id INTEGER,
apartment_number INTEGER,
title TEXT,
floors_number INTEGER,
status INTEGER,
description TEXT,
sheep_id TEXT,
updated_at TIMESTAMP,
FOREIGN KEY (entrance_id) REFERENCES entrance(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS apartments_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sheep_id TEXT,
apartments_id INTEGER,
status INTEGER,
description TEXT,
created_at TIMESTAMP,
FOREIGN KEY (apartments_id) REFERENCES apartments(id)
)
`);
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 '[]',
osm_id TEXT DEFAULT '[]',
settlement TEXT,
description TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
FOREIGN KEY (group_id) REFERENCES groups(group_number)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS homestead_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
homestead_id INTEGER,
name TEXT,
date_start TIMESTAMP,
date_end TIMESTAMP,
group_id INTEGER,
sheep_id TEXT,
working INTEGER DEFAULT 0,
FOREIGN KEY (homestead_id) REFERENCES homestead(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS meetings_schedule (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TIMESTAMP,
type INTEGER,
name TEXT,
sheep_id TEXT,
title TEXT,
number TEXT,
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS stand_list (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
hour_start INTEGER DEFAULT 10,
hour_end INTEGER DEFAULT 16,
quantity_sheep INTEGER DEFAULT 2,
week_days TEXT DEFAULT '[0, 1, 2, 3, 4, 5, 6]'
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS stand_schedule (
id INTEGER PRIMARY KEY AUTOINCREMENT,
stand INTEGER,
date TIMESTAMP,
hour INTEGER,
sheep_id TEXT,
number_sheep TEXT,
updated_at TIMESTAMP,
FOREIGN KEY (stand) REFERENCES stand_list(id),
FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
)
`);
});
module.exports = db; module.exports = db;

View File

@@ -1,6 +0,0 @@
const TelegramConfig = {
token: "7855966674:AAEw9l_EF0GcpjrkSFzt0aLukEfJxBA2gcY",
chatId: "224538769"
}
module.exports = TelegramConfig;

View File

@@ -5,7 +5,7 @@ class ApartmentsController {
const { entrance_id } = req.params; const { entrance_id } = req.params;
if (entrance_id) { if (entrance_id) {
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) { if (req.possibilities.can_view_territory) {
let result = await ApartmentsService.getApartments(entrance_id); let result = await ApartmentsService.getApartments(entrance_id);
if (result) { if (result) {
return res return res
@@ -33,7 +33,7 @@ class ApartmentsController {
const data = req.body; const data = req.body;
if (entrance_id && data) { if (entrance_id && data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await ApartmentsService.createApartments( let result = await ApartmentsService.createApartments(
entrance_id, entrance_id,
data data
@@ -62,7 +62,7 @@ class ApartmentsController {
const data = req.body; const data = req.body;
if (data) { if (data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await ApartmentsService.updateApartments(data); let result = await ApartmentsService.updateApartments(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -87,7 +87,7 @@ class ApartmentsController {
const data = req.body; const data = req.body;
if (data) { if (data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await ApartmentsService.deleteApartments(data); let result = await ApartmentsService.deleteApartments(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -2,8 +2,8 @@ const AuthService = require('../services/auth.service');
class AuthController { class AuthController {
async login(req, res) { async login(req, res) {
if (req.sheepId && req.sheepRole) { if (req.sheepId) {
const result = await AuthService.findUserByID(req.sheepId, req.sheepRole); const result = await AuthService.findUserByID(req.sheepId, req.mode);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
} else { } else {

View File

@@ -0,0 +1,112 @@
const BuildingsService = require('../services/buildings.service');
class BuildingsController {
async getList(req, res) {
const { id } = req.params;
if (id) {
if (req.possibilities.can_view_territory) {
let result = await BuildingsService.getList(id);
if (result) {
return res
.status(200)
.send(result);
} else {
return res
.status(500)
.send({ message: 'Internal server error.' });
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'Buildings not found.' });
}
}
async createBuildings(req, res) {
const { id } = req.params;
const data = req.body;
if (id && data) {
if (req.possibilities.can_add_territory) {
let result = await BuildingsService.createBuildings(
id,
data
);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable create building.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'Entrance not found.' });
}
}
async updateBuildings(req, res) {
const data = req.body;
if (data) {
if (req.possibilities.can_add_territory) {
let result = await BuildingsService.updateBuildings(data);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable update building.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'Data not found.' });
}
}
async deleteBuildings(req, res) {
const { id } = req.params;
if (id) {
if (req.possibilities.can_add_territory) {
let result = await BuildingsService.deleteBuildings(id);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable delete building.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'building id not found.' });
}
}
}
module.exports = new BuildingsController();

View File

@@ -5,7 +5,7 @@ class ConstructorController {
const data = req.body; const data = req.body;
if (data) { if (data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await ConstructorService.createPack(data); let result = await ConstructorService.createPack(data);
if (result) { if (result) {

View File

@@ -5,7 +5,7 @@ class EntrancesController {
const { house_id } = req.params; const { house_id } = req.params;
if (house_id) { if (house_id) {
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) { if (req.possibilities.can_view_territory) {
let result = await EntrancesService.getEntrances(house_id); let result = await EntrancesService.getEntrances(house_id);
if (result) { if (result) {
return res return res
@@ -33,7 +33,7 @@ class EntrancesController {
const data = req.body; const data = req.body;
if (house_id) { if (house_id) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await EntrancesService.createEntrance( let result = await EntrancesService.createEntrance(
house_id, house_id,
data data
@@ -62,7 +62,7 @@ class EntrancesController {
const data = req.body; const data = req.body;
if (data) { if (data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await EntrancesService.updateEntrance(data); let result = await EntrancesService.updateEntrance(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -87,7 +87,7 @@ class EntrancesController {
const data = req.body; const data = req.body;
if (data) { if (data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await EntrancesService.deleteEntrance(data); let result = await EntrancesService.deleteEntrance(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -4,7 +4,7 @@ class GeneratorCardsController {
async getScreen(req, res) { async getScreen(req, res) {
const { lat, lng, type, wayId, zoom, id, address, number } = req.query; const { lat, lng, type, wayId, zoom, id, address, number } = req.query;
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await saveCards({ center: {lat:lat,lng:lng}, wayId: wayId, zoom: zoom ?? 18, type: type, number: id, address: `${address} ${number}` }); let result = await saveCards({ center: {lat:lat,lng:lng}, wayId: wayId, zoom: zoom ?? 18, type: type, number: id, address: `${address} ${number}` });
console.log(result); console.log(result);

View File

@@ -5,7 +5,7 @@ class HistoryEntranceController {
const { entrance_id } = req.params; const { entrance_id } = req.params;
if (entrance_id) { if (entrance_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryEntranceService.getHistoryEntrance(entrance_id); let result = await HistoryEntranceService.getHistoryEntrance(entrance_id);
if (result) { if (result) {
return res return res
@@ -33,7 +33,7 @@ class HistoryEntranceController {
const data = req.body; const data = req.body;
if (entrance_id) { if (entrance_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryEntranceService.createHistoryEntrance( let result = await HistoryEntranceService.createHistoryEntrance(
entrance_id, entrance_id,
data data
@@ -62,7 +62,7 @@ class HistoryEntranceController {
const { entrance_id } = req.params; const { entrance_id } = req.params;
if (entrance_id) { if (entrance_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryEntranceService.updateHistoryEntrance(entrance_id); let result = await HistoryEntranceService.updateHistoryEntrance(entrance_id);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -87,7 +87,7 @@ class HistoryEntranceController {
const { entrance_id } = req.params; const { entrance_id } = req.params;
if (entrance_id) { if (entrance_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryEntranceService.deleteHistoryEntrance(entrance_id); let result = await HistoryEntranceService.deleteHistoryEntrance(entrance_id);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -5,7 +5,7 @@ class HistoryHomesteadController {
const { homestead_id } = req.params; const { homestead_id } = req.params;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryHomesteadService.getHistoryHomestead(homestead_id); let result = await HistoryHomesteadService.getHistoryHomestead(homestead_id);
if (result) { if (result) {
return res return res
@@ -33,7 +33,7 @@ class HistoryHomesteadController {
const data = req.body; const data = req.body;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryHomesteadService.createHistoryHomestead( let result = await HistoryHomesteadService.createHistoryHomestead(
homestead_id, homestead_id,
data data
@@ -63,7 +63,7 @@ class HistoryHomesteadController {
const { homestead_id } = req.params; const { homestead_id } = req.params;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryHomesteadService.updateHistoryHomestead(homestead_id); let result = await HistoryHomesteadService.updateHistoryHomestead(homestead_id);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -88,7 +88,7 @@ class HistoryHomesteadController {
const { homestead_id } = req.params; const { homestead_id } = req.params;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HistoryHomesteadService.deleteHistoryHomestead(homestead_id); let result = await HistoryHomesteadService.deleteHistoryHomestead(homestead_id);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -4,19 +4,21 @@ class HomesteadsController {
async getList(req, res) { async getList(req, res) {
const { mode } = req.query; const { mode } = req.query;
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) { if (req.possibilities.can_view_territory) {
let group_id = 0; let group_id = 0;
let sheepName = false; let sheepName = false;
if (req.sheepRole == "administrator") { // if (req.mode == 2) {
group_id = 0; // group_id = 0;
} else if (req.sheepRole == "moderator") { // } else if (req.mode == 1) {
group_id = req.group_id; // group_id = req.group_id;
} // }
if (mode == "sheep") { if (mode == "sheep") {
group_id = req.group_id; group_id = req.group_id;
sheepName = req.sheepName; sheepName = req.sheepName;
} else if (mode == "group"){
group_id = req.group_id;
} }
let result = await HomesteadsService.getList(group_id, sheepName); let result = await HomesteadsService.getList(group_id, sheepName);
@@ -40,7 +42,7 @@ class HomesteadsController {
const { homestead_id } = req.params; const { homestead_id } = req.params;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) { if (req.possibilities.can_view_territory) {
let result = await HomesteadsService.getHomestead(homestead_id); let result = await HomesteadsService.getHomestead(homestead_id);
if (result) { if (result) {
return res return res
@@ -67,7 +69,7 @@ class HomesteadsController {
const data = req.body; const data = req.body;
if (data) { if (data) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await HomesteadsService.createHomestead(data); let result = await HomesteadsService.createHomestead(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -93,7 +95,7 @@ class HomesteadsController {
const data = req.body; const data = req.body;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HomesteadsService.updateHomestead(homestead_id, data); let result = await HomesteadsService.updateHomestead(homestead_id, data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -118,7 +120,7 @@ class HomesteadsController {
const { homestead_id } = req.params; const { homestead_id } = req.params;
if (homestead_id) { if (homestead_id) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await HomesteadsService.deleteHomestead(homestead_id); let result = await HomesteadsService.deleteHomestead(homestead_id);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -1,22 +1,44 @@
const HousesService = require('../services/houses.service'); const HousesService = require('../services/houses.service');
class HousesController { class HousesController {
async getListEntrance(req, res) {
if (req.possibilities.can_manager_territory) {
let result = await HousesService.getListEntrance();
if (result) {
return res
.status(200)
.send(result);
} else {
return res
.status(500)
.send({ message: 'Internal server error.' });
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
}
async getList(req, res) { async getList(req, res) {
const { mode } = req.query; const { mode } = req.query;
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) { if (req.possibilities.can_view_territory) {
let group_id = 0; let group_id = 0;
let sheepName = false; let sheepName = false;
if (req.sheepRole == "administrator") { // if (req.mode == 2) {
group_id = 0; // group_id = 0;
} else if (req.sheepRole == "moderator") { // } else if (req.mode == 1) {
group_id = req.group_id; // group_id = req.group_id;
} // }
if (mode == "sheep") { if (mode == "sheep") {
group_id = req.group_id; group_id = req.group_id;
sheepName = req.sheepName; sheepName = req.sheepName;
} else if (mode == "group"){
group_id = req.group_id;
} }
let result = await HousesService.getList(group_id, sheepName); let result = await HousesService.getList(group_id, sheepName);
@@ -40,7 +62,7 @@ class HousesController {
const { house_id } = req.params; const { house_id } = req.params;
if (house_id) { if (house_id) {
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) { if (req.possibilities.can_view_territory) {
let result = await HousesService.getHouse(house_id); let result = await HousesService.getHouse(house_id);
if (result) { if (result) {
return res return res
@@ -66,7 +88,7 @@ class HousesController {
async createHouse(req, res) { async createHouse(req, res) {
const data = req.body; const data = req.body;
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await HousesService.createHouse(data); let result = await HousesService.createHouse(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -87,7 +109,7 @@ class HousesController {
const data = req.body; const data = req.body;
if (house_id) { if (house_id) {
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) { if (req.possibilities.can_manager_territory) {
let result = await HousesService.updateHouse(house_id, data); let result = await HousesService.updateHouse(house_id, data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -112,7 +134,7 @@ class HousesController {
const { house_id } = req.params; const { house_id } = req.params;
if (house_id) { if (house_id) {
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) { if (req.possibilities.can_add_territory) {
let result = await HousesService.deleteHouse(house_id); let result = await HousesService.deleteHouse(house_id);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -0,0 +1,67 @@
const PushService = require('../services/push.service');
class PushController {
async getKey(req, res) {
let result = await PushService.getKey();
if (result) {
return res.status(200).send(result);
} else {
return res.status(404).send({
message: 'Key not found.'
});
}
}
async createSubscribe(req, res) {
const data = req.body;
if (data) {
if (req.sheepId) {
let result = await PushService.createSubscribe(req.sheepId, data);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable create subscribe.',
});
}
} else {
return res
.status(401)
.send({ message: 'Sheeps not found.' });
}
} else {
return res
.status(401)
.send({ message: 'Data not found.' });
}
}
async deleteSubscribe(req, res) {
const data = req.body;
if (data) {
if (req.sheepId) {
let result = await PushService.deleteSubscribe(data);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable delete subscribe.',
});
}
} else {
return res
.status(401)
.send({ message: 'Sheeps not found.' });
}
} else {
return res
.status(401)
.send({ message: 'Data not found.' });
}
}
}
module.exports = new PushController();

View File

@@ -1,24 +0,0 @@
const RotationService = require('../services/rotation.service');
class RotationController {
async editTables(req, res) {
if (req.sheepRole == "administrator") {
let result = await RotationService.editTables();
if (result) {
return res.status(200).send(result);
} else {
return res
.status(404)
.send({ message: 'User not found.' });
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
}
}
module.exports = new RotationController();

View File

@@ -2,11 +2,11 @@ const SheepsService = require('../services/sheeps.service');
class SheepsController { class SheepsController {
async getSheep(req, res) { async getSheep(req, res) {
const { uuid } = req.query; const { id } = req.query;
if (uuid) { if (id) {
if (req.sheepRole) { if (req.possibilities.can_view_sheeps) {
const result = await SheepsService.getSheep(uuid, req.sheepRole); const result = await SheepsService.getSheep(id, req.mode);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
} else { } else {
@@ -27,8 +27,8 @@ class SheepsController {
} }
async getList(req, res) { async getList(req, res) {
if (req.sheepRole) { if (req.possibilities.can_view_sheeps) {
const result = await SheepsService.getList(req.sheepRole); const result = await SheepsService.getList(req.mode);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);
@@ -40,14 +40,14 @@ class SheepsController {
} else { } else {
return res return res
.status(404) .status(404)
.send({ message: 'Users not found.' }); .send({ message: 'The sheep does not have enough rights.' });
} }
} }
async createSheep(req, res) { async createSheep(req, res) {
const data = req.body; const data = req.body;
if (req.sheepRole && (req.sheepRole == "administrator" || req.moderator.can_add_sheeps)) { if (req.possibilities.can_add_sheeps) {
let result = await SheepsService.createSheep(data); let result = await SheepsService.createSheep(data);
if (result) { if (result) {
@@ -65,12 +65,9 @@ class SheepsController {
} }
async updateSheep(req, res) { async updateSheep(req, res) {
const { uuid } = req.query;
const data = req.body; const data = req.body;
console.log("data", data);
if (req.mode == 2) {
if (req.sheepRole == "administrator") {
let result = await SheepsService.updateSheep(data); let result = await SheepsService.updateSheep(data);
if (result) { if (result) {
@@ -90,7 +87,7 @@ class SheepsController {
async deleteSheep(req, res) { async deleteSheep(req, res) {
const data = req.body; const data = req.body;
if (req.sheepRole == "administrator") { if (req.mode == 2) {
let result = await SheepsService.deleteSheep(data); let result = await SheepsService.deleteSheep(data);
if (result) { if (result) {
return res.status(200).send(result); return res.status(200).send(result);

View File

@@ -0,0 +1,143 @@
const StandService = require('../services/stand.service');
class StandController {
async getStand(req, res) {
const { stand_id } = req.params;
return res.status(200).send({ stand_id });
}
async getList(req, res) {
if (req.possibilities.can_view_stand) {
const result = await StandService.getList();
if (result) {
return res.status(200).send(result);
} else {
return res
.status(404)
.send({ message: 'Stands not found.' });
}
} else {
return res
.status(404)
.send({ message: 'The sheep does not have enough rights.' });
}
}
async createStand(req, res) {
const data = req.body;
if (data) {
if (req.possibilities.can_add_stand) {
let result = await StandService.createStand(data);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable create stand.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'Data not found.' });
}
}
async updateStand(req, res) {
const { stand_id } = req.params;
const data = req.body;
if (stand_id && data) {
if (req.possibilities.can_add_stand) {
let result = await StandService.updateStand(stand_id, data);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable update stand.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'stand_id or data not found.' });
}
}
async deleteStand(req, res) {
const { stand_id } = req.params;
if (stand_id) {
if (req.possibilities.can_add_stand) {
let result = await StandService.deleteStand(stand_id);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable delete stend.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'stand_id not found.' });
}
}
async createSchedule(req, res) {
const { stand_id } = req.params;
if (stand_id) {
if (req.possibilities.can_add_stand) {
let result = await StandService.createSchedule(stand_id);
if (result) {
return res.status(200).send(result);
} else {
return res.status(500).send({
message: 'Unable create stand.',
});
}
} else {
return res
.status(403)
.send({ message: 'The user does not have enough rights.' });
}
} else {
return res
.status(401)
.send({ message: 'stand_id not found.' });
}
}
async getScheduleList(req, res) {
return res.status(200).send({});
}
async getScheduleHistory(req, res) {
const { stand_schedule_id } = req.params;
return res.status(200).send({ stand_schedule_id });
}
}
module.exports = new StandController();

View File

@@ -5,55 +5,86 @@ const authenticate = (req, res, next) => {
if (!uuid) return res.status(401).json({ error: "Unauthorized" }); if (!uuid) return res.status(401).json({ error: "Unauthorized" });
db.get(` db.get(`
SELECT sheeps.*, administrators.* FROM administrators JOIN sheeps ON sheeps.id = administrators.sheep_id WHERE administrators.uuid = ?`, SELECT
sheeps.*,
possibilities.can_add_sheeps AS can_add_sheeps,
possibilities.can_view_sheeps AS can_view_sheeps,
possibilities.can_add_territory AS can_add_territory,
possibilities.can_view_territory AS can_view_territory,
possibilities.can_manager_territory AS can_manager_territory,
possibilities.can_add_stand AS can_add_stand,
possibilities.can_view_stand AS can_view_stand,
possibilities.can_manager_stand AS can_manager_stand,
possibilities.can_add_schedule AS can_add_schedule,
possibilities.can_view_schedule AS can_view_schedule
FROM
sheeps
LEFT JOIN
possibilities ON possibilities.sheep_id = sheeps.id
WHERE
sheeps.uuid_manager = ?`,
[uuid], [uuid],
(err, administrator) => { (err, moderator) => {
if (administrator) { if (moderator) {
req.sheepId = administrator.sheep_id; req.sheepId = moderator.id;
req.sheepRole = 'administrator'; req.sheepName = moderator.name;
req.group_id = administrator.group_id; req.group_id = moderator.group_id;
req.sheepName = administrator.name; req.mode = Number(moderator.mode);
req.can_view_schedule = administrator.can_view_schedule; req.possibilities = {
req.can_view_stand = administrator.can_view_stand; can_add_sheeps: moderator.can_add_sheeps == 1 ? true : false,
req.can_view_territory = administrator.can_view_territory; can_view_sheeps: moderator.can_view_sheeps == 1 ? true : false,
can_add_territory: moderator.can_add_territory == 1 ? true : false,
can_view_territory: moderator.can_view_territory == 1 ? true : false,
can_manager_territory: moderator.can_manager_territory == 1 ? true : false,
can_add_stand: moderator.can_add_stand == 1 ? true : false,
can_view_stand: moderator.can_view_stand == 1 ? true : false,
can_manager_stand: moderator.can_manager_stand == 1 ? true : false,
can_add_schedule: moderator.can_add_schedule == 1 ? true : false,
can_view_schedule: moderator.can_view_schedule == 1 ? true : false
}
return next(); return next();
} }
db.get(` db.get(`
SELECT sheeps.*, moderators.* FROM moderators JOIN sheeps ON sheeps.id = moderators.sheep_id WHERE moderators.uuid = ?`, SELECT
sheeps.*,
possibilities.can_add_sheeps AS can_add_sheeps,
possibilities.can_view_sheeps AS can_view_sheeps,
possibilities.can_add_territory AS can_add_territory,
possibilities.can_view_territory AS can_view_territory,
possibilities.can_manager_territory AS can_manager_territory,
possibilities.can_add_stand AS can_add_stand,
possibilities.can_view_stand AS can_view_stand,
possibilities.can_manager_stand AS can_manager_stand,
possibilities.can_add_schedule AS can_add_schedule,
possibilities.can_view_schedule AS can_view_schedule
FROM
sheeps
LEFT JOIN
possibilities ON possibilities.sheep_id = sheeps.id
WHERE
sheeps.uuid = ?`,
[uuid], [uuid],
(err, moderator) => { (err, sheep) => {
if (moderator) {
req.sheepId = moderator.sheep_id;
req.sheepRole = 'moderator';
req.moderator = {
"id": moderator.moderators_id ? moderator.moderators_id : false,
"can_add_sheeps": moderator.can_add_sheeps == 1 ? true : false,
"can_add_territory": moderator.can_add_territory == 1 ? true : false,
"can_manager_territory": moderator.can_manager_territory == 1 ? true : false,
"can_add_stand": moderator.can_add_stand == 1 ? true : false,
"can_manager_stand": moderator.can_manager_stand == 1 ? true : false,
"can_add_schedule": moderator.can_add_schedule == 1 ? true : false
}
req.group_id = moderator.group_id;
req.sheepName = moderator.name;
req.can_view_schedule = moderator.can_view_schedule;
req.can_view_stand = moderator.can_view_stand;
req.can_view_territory = moderator.can_view_territory;
return next();
}
db.get(`SELECT sheeps.* FROM sheeps WHERE sheeps.uuid = ?`, [uuid], (err, sheep) => {
if (sheep) { if (sheep) {
req.sheepId = sheep.id; req.sheepId = sheep.id;
req.sheepRole = 'sheep';
req.group_id = sheep.group_id;
req.sheepName = sheep.name; req.sheepName = sheep.name;
req.can_view_schedule = sheep.can_view_schedule; req.group_id = sheep.group_id;
req.can_view_stand = sheep.can_view_stand; req.uuid_manager = null;
req.can_view_territory = sheep.can_view_territory; req.mode = 0;
req.possibilities = {
can_add_sheeps: false,
can_view_sheeps: false,
can_add_territory: false,
can_manager_territory: false,
can_add_stand: false,
can_manager_stand: false,
can_add_schedule: false,
can_view_territory: sheep.can_view_territory == 1 ? true : false,
can_view_stand: sheep.can_view_stand == 1 ? true : false,
can_view_schedule: sheep.can_view_schedule == 1 ? true : false
}
return next(); return next();
} }
@@ -63,8 +94,6 @@ const authenticate = (req, res, next) => {
); );
} }
); );
}
);
}; };
module.exports = authenticate; module.exports = authenticate;

449
api/package-lock.json generated
View File

@@ -1,39 +1,41 @@
{ {
"name": "API Sheep Service", "name": "API Sheep Service",
"version": "1.0.0", "version": "1.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "API Sheep Service", "name": "API Sheep Service",
"version": "1.0.0", "version": "1.0.1",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.0",
"express": "^4.21.0", "express": "^4.21.0",
"node-telegram-bot-api": "^0.66.0", "node-telegram-bot-api": "^0.66.0",
"puppeteer": "^24.4.0", "puppeteer": "^24.4.0",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"sqlite3": "^5.1.7" "sqlite3": "^5.1.7",
"web-push": "^3.6.7"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.26.2", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.25.9", "@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0", "js-tokens": "^4.0.0",
"picocolors": "^1.0.0" "picocolors": "^1.1.1"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.25.9", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@@ -112,9 +114,9 @@
} }
}, },
"node_modules/@emnapi/runtime": { "node_modules/@emnapi/runtime": {
"version": "1.3.1", "version": "1.4.4",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz",
"integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "^2.4.0"
@@ -493,15 +495,15 @@
} }
}, },
"node_modules/@puppeteer/browsers": { "node_modules/@puppeteer/browsers": {
"version": "2.8.0", "version": "2.10.5",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.8.0.tgz", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.5.tgz",
"integrity": "sha512-yTwt2KWRmCQAfhvbCRjebaSX8pV1//I0Y3g+A7f/eS7gf0l4eRJoUCvcYdVtboeU4CTOZQuqYbZNS8aBYb8ROQ==", "integrity": "sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==",
"dependencies": { "dependencies": {
"debug": "^4.4.0", "debug": "^4.4.1",
"extract-zip": "^2.0.1", "extract-zip": "^2.0.1",
"progress": "^2.0.3", "progress": "^2.0.3",
"proxy-agent": "^6.5.0", "proxy-agent": "^6.5.0",
"semver": "^7.7.1", "semver": "^7.7.2",
"tar-fs": "^3.0.8", "tar-fs": "^3.0.8",
"yargs": "^17.7.2" "yargs": "^17.7.2"
}, },
@@ -513,9 +515,9 @@
} }
}, },
"node_modules/@puppeteer/browsers/node_modules/debug": { "node_modules/@puppeteer/browsers/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -548,12 +550,12 @@
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.13.11", "version": "24.0.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz",
"integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"undici-types": "~6.20.0" "undici-types": "~7.8.0"
} }
}, },
"node_modules/@types/yauzl": { "node_modules/@types/yauzl": {
@@ -584,9 +586,9 @@
} }
}, },
"node_modules/agent-base": { "node_modules/agent-base": {
"version": "7.1.3", "version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
} }
@@ -757,6 +759,17 @@
"safer-buffer": "~2.1.0" "safer-buffer": "~2.1.0"
} }
}, },
"node_modules/asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"dependencies": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/assert-plus": { "node_modules/assert-plus": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
@@ -828,29 +841,37 @@
"optional": true "optional": true
}, },
"node_modules/bare-events": { "node_modules/bare-events": {
"version": "2.5.4", "version": "2.6.0",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.0.tgz",
"integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", "integrity": "sha512-EKZ5BTXYExaNqi3I3f9RtEsaI/xBSGjE0XZCZilPzFAV/goswFHuPd9jEZlPIZ/iNZJwDSao9qRiScySz7MbQg==",
"optional": true "optional": true
}, },
"node_modules/bare-fs": { "node_modules/bare-fs": {
"version": "4.0.1", "version": "4.1.6",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.6.tgz",
"integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", "integrity": "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"bare-events": "^2.0.0", "bare-events": "^2.5.4",
"bare-path": "^3.0.0", "bare-path": "^3.0.0",
"bare-stream": "^2.0.0" "bare-stream": "^2.6.4"
}, },
"engines": { "engines": {
"bare": ">=1.7.0" "bare": ">=1.16.0"
},
"peerDependencies": {
"bare-buffer": "*"
},
"peerDependenciesMeta": {
"bare-buffer": {
"optional": true
}
} }
}, },
"node_modules/bare-os": { "node_modules/bare-os": {
"version": "3.6.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.0.tgz", "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz",
"integrity": "sha512-BUrFS5TqSBdA0LwHop4OjPJwisqxGy6JsWVqV6qaFoe965qqtaKfDzHY5T2YA1gUL0ZeeQeA+4BBc1FJTcHiPw==", "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==",
"optional": true, "optional": true,
"engines": { "engines": {
"bare": ">=1.14.0" "bare": ">=1.14.0"
@@ -943,6 +964,11 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
}, },
"node_modules/bn.js": {
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.3", "version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
@@ -967,9 +993,9 @@
} }
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
@@ -1007,6 +1033,11 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -1122,9 +1153,9 @@
} }
}, },
"node_modules/chromium-bidi": { "node_modules/chromium-bidi": {
"version": "2.1.2", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-2.1.2.tgz", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-5.1.0.tgz",
"integrity": "sha512-vtRWBK2uImo5/W2oG6/cDkkHSm+2t6VHgnj+Rcwhb0pP74OoUb4GipyRX/T/y39gYQPhioP0DPShn+A7P6CHNw==", "integrity": "sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==",
"dependencies": { "dependencies": {
"mitt": "^3.0.1", "mitt": "^3.0.1",
"zod": "^3.24.1" "zod": "^3.24.1"
@@ -1472,17 +1503,28 @@
} }
}, },
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/devtools-protocol": { "node_modules/devtools-protocol": {
"version": "0.0.1413902", "version": "0.0.1464554",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1413902.tgz", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1464554.tgz",
"integrity": "sha512-yRtvFD8Oyk7C9Os3GmnFZLu53yAfsnyw1s+mLmHHUK0GQEc9zthHWvS1r67Zqzm5t7v56PILHIVZ7kmFMaL2yQ==" "integrity": "sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw=="
},
"node_modules/dotenv": {
"version": "17.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz",
"integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
}, },
"node_modules/dunder-proto": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
@@ -1511,6 +1553,14 @@
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
}, },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -1551,9 +1601,9 @@
} }
}, },
"node_modules/end-of-stream": { "node_modules/end-of-stream": {
"version": "1.4.4", "version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"dependencies": { "dependencies": {
"once": "^1.4.0" "once": "^1.4.0"
} }
@@ -1581,26 +1631,26 @@
} }
}, },
"node_modules/es-abstract": { "node_modules/es-abstract": {
"version": "1.23.9", "version": "1.24.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
"integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
"dependencies": { "dependencies": {
"array-buffer-byte-length": "^1.0.2", "array-buffer-byte-length": "^1.0.2",
"arraybuffer.prototype.slice": "^1.0.4", "arraybuffer.prototype.slice": "^1.0.4",
"available-typed-arrays": "^1.0.7", "available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"call-bound": "^1.0.3", "call-bound": "^1.0.4",
"data-view-buffer": "^1.0.2", "data-view-buffer": "^1.0.2",
"data-view-byte-length": "^1.0.2", "data-view-byte-length": "^1.0.2",
"data-view-byte-offset": "^1.0.1", "data-view-byte-offset": "^1.0.1",
"es-define-property": "^1.0.1", "es-define-property": "^1.0.1",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0", "es-object-atoms": "^1.1.1",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"es-to-primitive": "^1.3.0", "es-to-primitive": "^1.3.0",
"function.prototype.name": "^1.1.8", "function.prototype.name": "^1.1.8",
"get-intrinsic": "^1.2.7", "get-intrinsic": "^1.3.0",
"get-proto": "^1.0.0", "get-proto": "^1.0.1",
"get-symbol-description": "^1.1.0", "get-symbol-description": "^1.1.0",
"globalthis": "^1.0.4", "globalthis": "^1.0.4",
"gopd": "^1.2.0", "gopd": "^1.2.0",
@@ -1612,21 +1662,24 @@
"is-array-buffer": "^3.0.5", "is-array-buffer": "^3.0.5",
"is-callable": "^1.2.7", "is-callable": "^1.2.7",
"is-data-view": "^1.0.2", "is-data-view": "^1.0.2",
"is-negative-zero": "^2.0.3",
"is-regex": "^1.2.1", "is-regex": "^1.2.1",
"is-set": "^2.0.3",
"is-shared-array-buffer": "^1.0.4", "is-shared-array-buffer": "^1.0.4",
"is-string": "^1.1.1", "is-string": "^1.1.1",
"is-typed-array": "^1.1.15", "is-typed-array": "^1.1.15",
"is-weakref": "^1.1.0", "is-weakref": "^1.1.1",
"math-intrinsics": "^1.1.0", "math-intrinsics": "^1.1.0",
"object-inspect": "^1.13.3", "object-inspect": "^1.13.4",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.7", "object.assign": "^4.1.7",
"own-keys": "^1.0.1", "own-keys": "^1.0.1",
"regexp.prototype.flags": "^1.5.3", "regexp.prototype.flags": "^1.5.4",
"safe-array-concat": "^1.1.3", "safe-array-concat": "^1.1.3",
"safe-push-apply": "^1.0.0", "safe-push-apply": "^1.0.0",
"safe-regex-test": "^1.1.0", "safe-regex-test": "^1.1.0",
"set-proto": "^1.0.0", "set-proto": "^1.0.0",
"stop-iteration-iterator": "^1.1.0",
"string.prototype.trim": "^1.2.10", "string.prototype.trim": "^1.2.10",
"string.prototype.trimend": "^1.0.9", "string.prototype.trimend": "^1.0.9",
"string.prototype.trimstart": "^1.0.8", "string.prototype.trimstart": "^1.0.8",
@@ -1635,7 +1688,7 @@
"typed-array-byte-offset": "^1.0.4", "typed-array-byte-offset": "^1.0.4",
"typed-array-length": "^1.0.7", "typed-array-length": "^1.0.7",
"unbox-primitive": "^1.1.0", "unbox-primitive": "^1.1.0",
"which-typed-array": "^1.1.18" "which-typed-array": "^1.1.19"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -1864,9 +1917,9 @@
} }
}, },
"node_modules/extract-zip/node_modules/debug": { "node_modules/extract-zip/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -1970,13 +2023,14 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.2", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
}, },
"engines": { "engines": {
@@ -2134,9 +2188,9 @@
} }
}, },
"node_modules/get-stream/node_modules/pump": { "node_modules/get-stream/node_modules/pump": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"dependencies": { "dependencies": {
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"once": "^1.3.1" "once": "^1.3.1"
@@ -2159,9 +2213,9 @@
} }
}, },
"node_modules/get-uri": { "node_modules/get-uri": {
"version": "6.0.4", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
"integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
"dependencies": { "dependencies": {
"basic-ftp": "^5.0.2", "basic-ftp": "^5.0.2",
"data-uri-to-buffer": "^6.0.2", "data-uri-to-buffer": "^6.0.2",
@@ -2172,9 +2226,9 @@
} }
}, },
"node_modules/get-uri/node_modules/debug": { "node_modules/get-uri/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -2359,10 +2413,18 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/http_ece": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz",
"integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==",
"engines": {
"node": ">=16"
}
},
"node_modules/http-cache-semantics": { "node_modules/http-cache-semantics": {
"version": "4.1.1", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
"optional": true "optional": true
}, },
"node_modules/http-errors": { "node_modules/http-errors": {
@@ -2393,9 +2455,9 @@
} }
}, },
"node_modules/http-proxy-agent/node_modules/debug": { "node_modules/http-proxy-agent/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -2439,9 +2501,9 @@
} }
}, },
"node_modules/https-proxy-agent/node_modules/debug": { "node_modules/https-proxy-agent/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -2757,6 +2819,17 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-negative-zero": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
"integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-number-object": { "node_modules/is-number-object": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
@@ -2976,6 +3049,25 @@
"verror": "1.10.0" "verror": "1.10.0"
} }
}, },
"node_modules/jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -3034,9 +3126,9 @@
} }
}, },
"node_modules/make-fetch-happen/node_modules/debug": { "node_modules/make-fetch-happen/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@@ -3182,6 +3274,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3338,9 +3435,9 @@
} }
}, },
"node_modules/node-abi": { "node_modules/node-abi": {
"version": "3.74.0", "version": "3.75.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
"integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==",
"dependencies": { "dependencies": {
"semver": "^7.3.5" "semver": "^7.3.5"
}, },
@@ -3564,9 +3661,9 @@
} }
}, },
"node_modules/pac-proxy-agent/node_modules/debug": { "node_modules/pac-proxy-agent/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -3710,9 +3807,9 @@
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
}, },
"node_modules/prebuild-install/node_modules/pump": { "node_modules/prebuild-install/node_modules/pump": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"dependencies": { "dependencies": {
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"once": "^1.3.1" "once": "^1.3.1"
@@ -3732,9 +3829,9 @@
} }
}, },
"node_modules/prebuild-install/node_modules/tar-fs": { "node_modules/prebuild-install/node_modules/tar-fs": {
"version": "2.1.2", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
"integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
"dependencies": { "dependencies": {
"chownr": "^1.1.1", "chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2", "mkdirp-classic": "^0.5.2",
@@ -3820,9 +3917,9 @@
} }
}, },
"node_modules/proxy-agent/node_modules/debug": { "node_modules/proxy-agent/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -3874,16 +3971,16 @@
} }
}, },
"node_modules/puppeteer": { "node_modules/puppeteer": {
"version": "24.4.0", "version": "24.12.1",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.4.0.tgz", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.12.1.tgz",
"integrity": "sha512-E4JhJzjS8AAI+6N/b+Utwarhz6zWl3+MR725fal+s3UlOlX2eWdsvYYU+Q5bXMjs9eZEGkNQroLkn7j11s2k1Q==", "integrity": "sha512-+vvwl+Xo4z5uXLLHG+XW8uXnUXQ62oY6KU6bEFZJvHWLutbmv5dw9A/jcMQ0fqpQdLydHmK0Uy7/9Ilj8ufwSQ==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@puppeteer/browsers": "2.8.0", "@puppeteer/browsers": "2.10.5",
"chromium-bidi": "2.1.2", "chromium-bidi": "5.1.0",
"cosmiconfig": "^9.0.0", "cosmiconfig": "^9.0.0",
"devtools-protocol": "0.0.1413902", "devtools-protocol": "0.0.1464554",
"puppeteer-core": "24.4.0", "puppeteer-core": "24.12.1",
"typed-query-selector": "^2.12.0" "typed-query-selector": "^2.12.0"
}, },
"bin": { "bin": {
@@ -3894,25 +3991,25 @@
} }
}, },
"node_modules/puppeteer-core": { "node_modules/puppeteer-core": {
"version": "24.4.0", "version": "24.12.1",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.4.0.tgz", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.12.1.tgz",
"integrity": "sha512-eFw66gCnWo0X8Hyf9KxxJtms7a61NJVMiSaWfItsFPzFBsjsWdmcNlBdsA1WVwln6neoHhsG+uTVesKmTREn/g==", "integrity": "sha512-8odp6d3ERKBa3BAVaYWXn95UxQv3sxvP1reD+xZamaX6ed8nCykhwlOiHSaHR9t/MtmIB+rJmNencI6Zy4Gxvg==",
"dependencies": { "dependencies": {
"@puppeteer/browsers": "2.8.0", "@puppeteer/browsers": "2.10.5",
"chromium-bidi": "2.1.2", "chromium-bidi": "5.1.0",
"debug": "^4.4.0", "debug": "^4.4.1",
"devtools-protocol": "0.0.1413902", "devtools-protocol": "0.0.1464554",
"typed-query-selector": "^2.12.0", "typed-query-selector": "^2.12.0",
"ws": "^8.18.1" "ws": "^8.18.3"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/puppeteer-core/node_modules/debug": { "node_modules/puppeteer-core/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -4296,9 +4393,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -4588,9 +4685,9 @@
} }
}, },
"node_modules/socks": { "node_modules/socks": {
"version": "2.8.4", "version": "2.8.6",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz",
"integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==",
"dependencies": { "dependencies": {
"ip-address": "^9.0.5", "ip-address": "^9.0.5",
"smart-buffer": "^4.2.0" "smart-buffer": "^4.2.0"
@@ -4614,9 +4711,9 @@
} }
}, },
"node_modules/socks-proxy-agent/node_modules/debug": { "node_modules/socks-proxy-agent/node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -4728,10 +4825,22 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
"integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
"dependencies": {
"es-errors": "^1.3.0",
"internal-slot": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/streamx": { "node_modules/streamx": {
"version": "2.22.0", "version": "2.22.1",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz",
"integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==",
"dependencies": { "dependencies": {
"fast-fifo": "^1.3.2", "fast-fifo": "^1.3.2",
"text-decoder": "^1.1.0" "text-decoder": "^1.1.0"
@@ -4855,9 +4964,9 @@
} }
}, },
"node_modules/tar-fs": { "node_modules/tar-fs": {
"version": "3.0.8", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz",
"integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==",
"dependencies": { "dependencies": {
"pump": "^3.0.0", "pump": "^3.0.0",
"tar-stream": "^3.1.5" "tar-stream": "^3.1.5"
@@ -4868,9 +4977,9 @@
} }
}, },
"node_modules/tar-fs/node_modules/pump": { "node_modules/tar-fs/node_modules/pump": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"dependencies": { "dependencies": {
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"once": "^1.3.1" "once": "^1.3.1"
@@ -4903,20 +5012,20 @@
} }
}, },
"node_modules/tldts": { "node_modules/tldts": {
"version": "6.1.85", "version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
"integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==", "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
"dependencies": { "dependencies": {
"tldts-core": "^6.1.85" "tldts-core": "^6.1.86"
}, },
"bin": { "bin": {
"tldts": "bin/cli.js" "tldts": "bin/cli.js"
} }
}, },
"node_modules/tldts-core": { "node_modules/tldts-core": {
"version": "6.1.85", "version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz", "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
"integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==" "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="
}, },
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
@@ -5063,9 +5172,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.20.0", "version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"optional": true "optional": true
}, },
"node_modules/unique-filename": { "node_modules/unique-filename": {
@@ -5167,6 +5276,24 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
}, },
"node_modules/web-push": {
"version": "3.6.7",
"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz",
"integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==",
"dependencies": {
"asn1.js": "^5.3.0",
"http_ece": "1.2.0",
"https-proxy-agent": "^7.0.0",
"jws": "^4.0.0",
"minimist": "^1.2.5"
},
"bin": {
"web-push": "src/cli.js"
},
"engines": {
"node": ">= 16"
}
},
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -5299,9 +5426,9 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.18.1", "version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
@@ -5366,9 +5493,9 @@
} }
}, },
"node_modules/zod": { "node_modules/zod": {
"version": "3.24.2", "version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "API Sheep Service", "name": "API Sheep Service",
"version": "1.0.0", "version": "1.0.1",
"main": "app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node app.js" "start": "node app.js"
@@ -10,10 +10,12 @@
"description": "", "description": "",
"dependencies": { "dependencies": {
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.0",
"express": "^4.21.0", "express": "^4.21.0",
"node-telegram-bot-api": "^0.66.0", "node-telegram-bot-api": "^0.66.0",
"sqlite3": "^5.1.7",
"puppeteer": "^24.4.0", "puppeteer": "^24.4.0",
"sharp": "^0.33.5" "sharp": "^0.33.5",
"sqlite3": "^5.1.7",
"web-push": "^3.6.7"
} }
} }

View File

@@ -0,0 +1,13 @@
const express = require('express');
const router = express.Router({ mergeParams: true });
const BuildingsController = require('../controllers/buildings.controller');
const authenticate = require("../middleware/auth");
router
.route('/:id')
.get(authenticate, BuildingsController.getList)
.post(authenticate, BuildingsController.createBuildings)
.put(authenticate, BuildingsController.updateBuildings)
.delete(authenticate, BuildingsController.deleteBuildings);
module.exports = router;

View File

@@ -3,6 +3,10 @@ const router = express.Router();
const HousesController = require('../controllers/houses.controller'); const HousesController = require('../controllers/houses.controller');
const authenticate = require("../middleware/auth"); const authenticate = require("../middleware/auth");
router
.route('/list/entrances')
.get(authenticate, HousesController.getListEntrance)
router router
.route('/list') .route('/list')
.get(authenticate, HousesController.getList) .get(authenticate, HousesController.getList)

View File

@@ -6,11 +6,14 @@ const sheepsRoutes = require('./sheeps.routes');
const constructorRoutes = require('./constructor.routes'); const constructorRoutes = require('./constructor.routes');
const housesRoutes = require('./houses.routes'); const housesRoutes = require('./houses.routes');
const homesteadsRoutes = require('./homesteads.routes'); const homesteadsRoutes = require('./homesteads.routes');
const buildingsRoutes = require('./buildings.routes');
const entrancesRoutes = require('./entrances.routes'); const entrancesRoutes = require('./entrances.routes');
const apartmentsRoutes = require('./apartments.routes'); const apartmentsRoutes = require('./apartments.routes');
const historyEntranceRoutes = require('./history.entrance.routes'); const historyEntranceRoutes = require('./history.entrance.routes');
const historyHomesteadRoutes = require('./history.homestead.routes'); const historyHomesteadRoutes = require('./history.homestead.routes');
const rotationRoutes = require('./rotation.routes'); const standRoutes = require('./stand.routes');
const pushRoutes = require('./push.routes');
const generatorCardsRoutes = require('./generator.cards.routes'); const generatorCardsRoutes = require('./generator.cards.routes');
router.use('/auth', authRoutes); router.use('/auth', authRoutes);
@@ -18,12 +21,13 @@ router.use('/sheeps?', sheepsRoutes);
router.use('/constructor', constructorRoutes); router.use('/constructor', constructorRoutes);
router.use('/houses?', housesRoutes); router.use('/houses?', housesRoutes);
router.use('/homesteads?', homesteadsRoutes); router.use('/homesteads?', homesteadsRoutes);
router.use('/buildings?', buildingsRoutes);
router.use('/house/:house_id/entrances', entrancesRoutes); router.use('/house/:house_id/entrances', entrancesRoutes);
router.use('/apartments?/:entrance_id', apartmentsRoutes); router.use('/apartments?/:entrance_id', apartmentsRoutes);
router.use('/history/entrance/:entrance_id', historyEntranceRoutes); router.use('/history/entrance/:entrance_id', historyEntranceRoutes);
router.use('/history/homestead/:homestead_id', historyHomesteadRoutes); router.use('/history/homestead/:homestead_id', historyHomesteadRoutes);
router.use('/stand', standRoutes);
router.use('/rotation', rotationRoutes); router.use('/push', pushRoutes);
router.use('/generator/cards', generatorCardsRoutes); router.use('/generator/cards', generatorCardsRoutes);

18
api/routes/push.routes.js Normal file
View File

@@ -0,0 +1,18 @@
const express = require('express');
const router = express.Router();
const PushController = require('../controllers/push.controller');
const authenticate = require("../middleware/auth");
router
.route('/key')
.get(authenticate, PushController.getKey);
router
.route('/subscribe')
.post(authenticate, PushController.createSubscribe);
router
.route('/unsubscribe')
.delete(authenticate, PushController.deleteSubscribe);
module.exports = router;

View File

@@ -1,10 +0,0 @@
const express = require('express');
const router = express.Router();
const RotationController = require('../controllers/rotation.controller');
const authenticate = require("../middleware/auth");
router
.route('/')
.get(authenticate, RotationController.editTables);
module.exports = router;

View File

@@ -0,0 +1,29 @@
const express = require('express');
const router = express.Router();
const StandController = require('../controllers/stand.controller');
const authenticate = require("../middleware/auth");
router
.route('/list')
.get(authenticate, StandController.getList)
.post(authenticate, StandController.createStand);
router
.route('/:stand_id')
.get(authenticate, StandController.getStand)
.put(authenticate, StandController.updateStand)
.delete(authenticate, StandController.deleteStand);
router
.route('/schedule/list')
.get(authenticate, StandController.getScheduleList)
router
.route('/schedule/:stand_id')
.post(authenticate, StandController.createSchedule)
router
.route('/schedule/history/:stand_schedule_id')
.post(authenticate, StandController.getScheduleHistory)
module.exports = router;

View File

@@ -1,34 +1,27 @@
const db = require("../config/db"); const db = require("../config/db");
class AuthService { class AuthService {
findUserByID(id, sheepRole) { findUserByID(id, mode) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
let sql = ` let sql = `
SELECT SELECT
sheeps.*, sheeps.*,
groups.group_number AS group_id, possibilities.can_add_sheeps AS can_add_sheeps,
administrators.id AS administrators_id, possibilities.can_view_sheeps AS can_view_sheeps,
administrators.uuid AS administrators_uuid, possibilities.can_add_territory AS can_add_territory,
moderators.id AS moderators_id, possibilities.can_view_territory AS can_view_territory,
moderators.uuid AS moderators_uuid, possibilities.can_manager_territory AS can_manager_territory,
moderators.can_add_sheeps, possibilities.can_add_stand AS can_add_stand,
moderators.can_add_territory, possibilities.can_view_stand AS can_view_stand,
moderators.can_manager_territory, possibilities.can_manager_stand AS can_manager_stand,
moderators.can_add_stand, possibilities.can_add_schedule AS can_add_schedule,
moderators.can_manager_stand, possibilities.can_view_schedule AS can_view_schedule
moderators.can_add_schedule
FROM FROM
sheeps sheeps
LEFT JOIN LEFT JOIN
groups ON groups.group_number = sheeps.group_id possibilities ON possibilities.sheep_id = sheeps.id
LEFT JOIN
administrators ON administrators.sheep_id = sheeps.id
LEFT JOIN
moderators ON moderators.sheep_id = sheeps.id
WHERE WHERE
sheeps.id = ? sheeps.id = ?`;
LIMIT 1;
`
db.get(sql, [id], (err, sheep) => { db.get(sql, [id], (err, sheep) => {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
@@ -38,40 +31,36 @@ class AuthService {
return res(false); return res(false);
} else { } else {
let data = { let data = {
"id": Number(sheep.id), id: sheep.id,
"group_id": Number(sheep.group_id), group_id: sheep.group_id,
"name": sheep.name, name: sheep.name,
"icon": sheep.icon, icon: sheep.icon,
"uuid": sheep.uuid, uuid: sheep.uuid,
"appointment": sheep.appointment, uuid_manager: mode && mode == 2 ? sheep.uuid_manager : null,
"can_view_stand": sheep.can_view_stand == 0 ? false : true, appointment: sheep.appointment,
"can_view_schedule": sheep.can_view_schedule == 0 ? false : true, mode: mode ? Number(sheep.mode) : 0,
"can_view_territory": sheep.can_view_territory == 0 ? false : true, sheepRole: sheep.mode_title,
"administrator": { possibilities: {
"id": sheep.administrators_id ? sheep.administrators_id : false, can_add_sheeps: false,
"uuid": null can_view_sheeps: false,
}, can_add_territory: false,
"moderator": { can_manager_territory: false,
"id": sheep.moderators_id ? sheep.moderators_id : false, can_add_stand: false,
"uuid": null, can_manager_stand: false,
"can_add_sheeps": sheep.can_add_sheeps == 1 ? true : false, can_add_schedule: false,
"can_add_territory": sheep.can_add_territory == 1 ? true : false, can_view_schedule: sheep.can_view_schedule == 1 ? true : false,
"can_manager_territory": sheep.can_manager_territory == 1 ? true : false, can_view_stand: sheep.can_view_stand == 1 ? true : false,
"can_add_stand": sheep.can_add_stand == 1 ? true : false, can_view_territory: sheep.can_view_territory == 1 ? true : false
"can_manager_stand": sheep.can_manager_stand == 1 ? true : false,
"can_add_schedule": sheep.can_add_schedule == 1 ? true : false
} }
} }
if (mode && (mode == 1 || mode == 2)) {
if (sheepRole == "administrator") { data.possibilities.can_add_sheeps = sheep.can_add_sheeps == 1 ? true : false;
if (sheep.administrators_id) { data.possibilities.can_view_sheeps = sheep.can_view_sheeps == 1 ? true : false;
data.administrator.uuid = sheep.administrators_uuid; data.possibilities.can_add_territory = sheep.can_add_territory == 1 ? true : false;
} data.possibilities.can_manager_territory = sheep.can_manager_territory == 1 ? true : false;
} data.possibilities.can_add_stand = sheep.can_add_stand == 1 ? true : false;
if (sheepRole == "moderator") { data.possibilities.can_manager_stand = sheep.can_manager_stand == 1 ? true : false;
if (sheep.moderators_id) { data.possibilities.can_add_schedule = sheep.can_add_schedule == 1 ? true : false;
data.moderator.uuid = sheep.moderators_uuid;
}
} }
return res(data); return res(data);

View File

@@ -0,0 +1,102 @@
const db = require("../config/db");
class BuildingsService {
getList(homestead_id) {
return new Promise((res, rej) => {
let sql = `
SELECT
*
FROM
buildings
WHERE
homestead_id = '${homestead_id}'
ORDER BY
id
`;
db.all(sql, (err, rows) => {
if (err) {
console.error(err.message);
return res(false);
} else {
let data = rows.map((row) => {
return {
"id": Number(row.id),
"homestead_id": Number(row.homestead_id),
"sheep_id": Number(row.sheep_id),
"geo": JSON.parse(row.geo),
"title": row.title,
"status": Number(row.status),
"description": row.description,
"updated_at": Number(row.updated_at)
}
})
return res(data);
}
});
});
}
createBuildings(homestead_id, data) {
return new Promise((res, rej) => {
let sql = 'INSERT INTO buildings(homestead_id, title, geo) VALUES (?, ?, ?)';
db.run(sql, [
homestead_id,
data.title,
JSON.stringify(data.geo)
], function(err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "status": "ok", "id": this.lastID });
}
});
});
}
updateBuildings(data) {
return new Promise((res, rej) => {
let sql = 'UPDATE buildings SET title = ?, status = ?, description = ?, updated_at = ?, geo = ? WHERE id = ?';
db.run(sql, [
data.title,
data.status,
data.description,
Math.floor(new Date(Date.now()).getTime()),
JSON.stringify(data.geo),
data.id
], function(err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "status": "ok" });
}
});
});
}
deleteBuildings(building_id) {
return new Promise((res, rej) => {
db.run('DELETE FROM buildings WHERE id = ?', [building_id], function(err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "status": "ok"});
}
});
});
}
}
module.exports = new BuildingsService();

View File

@@ -101,17 +101,114 @@ class ConstructorService {
// }); // });
// }); // });
// } // }
// createPack(data) {
// return new Promise((res, rej) => {
// if (data.type == "house") {
// const sql = `
// INSERT INTO house (
// title, number, points, points_number, geo, osm_id, settlement, created_at
// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
// db.run(sql, [
// data.title,
// data.number,
// JSON.stringify(data.points),
// JSON.stringify(data.points_number),
// JSON.stringify(data.geo),
// JSON.stringify(data.osm_id),
// data.settlement,
// Math.floor(Date.now())
// ], function (err) {
// if (err) {
// console.error(err.message);
// return res(false);
// }
// if (this.changes === 0) {
// return res(false);
// }
// const houseId = this.lastID;
// saveCards({ center: data.geo, wayId: data.osm_id, zoom: data.zoom ?? 18, type: "house", number: houseId, address: `${data.title} ${data.number}` });
// const entranceStmt = db.prepare(`
// INSERT INTO entrance (
// house_id, entrance_number, title, points, points_number, created_at
// ) VALUES (?, ?, ?, ?, ?, ?)`);
// const apartmentStmt = db.prepare(`
// INSERT INTO apartments (
// entrance_id, apartment_number, title, floors_number
// ) VALUES (?, ?, ?, ?)`);
// const entranceIdMap = {}; // Для сопоставления editor_id → entrance_id
// let pendingEntrances = data.entrance.length;
// data.entrance.forEach((e) => {
// entranceStmt.run(
// houseId,
// Number(e.entrance_number),
// e.title,
// JSON.stringify(e.points),
// JSON.stringify(e.points_number),
// Math.floor(Date.now()),
// function (err) {
// if (err) {
// console.error(err.message);
// return;
// }
// const entranceId = this.lastID;
// entranceIdMap[e.editor_id] = entranceId;
// if (--pendingEntrances === 0) {
// insertApartments();
// }
// }
// );
// });
// function insertApartments() {
// for (const [editor_id, apartments] of Object.entries(data.apartments)) {
// const entranceId = entranceIdMap[editor_id];
// if (!entranceId) continue;
// apartments.forEach(apartment => {
// apartmentStmt.run(
// entranceId,
// Number(apartment.apartment_number),
// apartment.title,
// apartment.floors_number
// );
// });
// }
// entranceStmt.finalize();
// apartmentStmt.finalize();
// res({ "status": "ok", "id": houseId });
// }
// });
// } else if (data.type == "homestead") {
// return res(false);
// } else {
// return res(false);
// }
// });
// }
createPack(data) { createPack(data) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
if (data.type == "house") { if (data.type === "house") {
const sql = ` const sql = `
INSERT INTO house ( INSERT INTO house (
group_id, title, number, points, points_number, geo, osm_id, settlement, created_at title, number, points, points_number, geo, osm_id, settlement, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`; ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
db.run(sql, [ db.run(sql, [
Number(data.group_id),
data.title, data.title,
data.number, data.number,
JSON.stringify(data.points), JSON.stringify(data.points),
@@ -131,103 +228,132 @@ class ConstructorService {
const houseId = this.lastID; const houseId = this.lastID;
saveCards({ center: data.geo, wayId: data.osm_id, zoom: data.zoom ?? 18, type: "house", number: houseId, address: `${data.title} ${data.number}` }); // saveCards({ center: data.geo, wayId: data.osm_id, zoom: data.zoom ?? 18, type: "house", number: houseId, address: `${data.title} ${data.number}` });
const entranceStmt = db.prepare(` const entranceStmt = db.prepare(`
INSERT INTO entrance ( INSERT INTO entrance (
house_id, entrance_number, title, points, points_number, created_at house_id, entrance_number, title, created_at
) VALUES (?, ?, ?, ?, ?, ?)`); ) VALUES (?, ?, ?, ?)`);
const apartmentStmt = db.prepare(` const apartmentStmt = db.prepare(`
INSERT INTO apartments ( INSERT INTO apartments (
entrance_id, apartment_number, title, floors_number entrance_id, apartment_number, title, floors_number
) VALUES (?, ?, ?, ?)`); ) VALUES (?, ?, ?, ?)`);
const entranceIdMap = {}; // Для сопоставления editor_id → entrance_id let pendingEntrances = data.entrances.length;
const entranceIdMap = {};
let pendingEntrances = data.entrance.length; if (pendingEntrances === 0) return finalize();
data.entrance.forEach((e) => { // Вставляем подъезды
data.entrances.forEach(entrance => {
entranceStmt.run( entranceStmt.run(
houseId, houseId,
Number(e.entrance_number), Number(entrance.entrance_number),
e.title, entrance.title,
JSON.stringify(e.points),
JSON.stringify(e.points_number),
Math.floor(Date.now()), Math.floor(Date.now()),
function (err) { function (err) {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
return res(false);
}
const entranceID = this.lastID;
entranceIdMap[entrance.entrance_number] = entranceID;
// Вставляем квартиры данного подъезда
let pendingApartments = entrance.apartments.length;
if (pendingApartments === 0) {
if (--pendingEntrances === 0) finalize();
return; return;
} }
const entranceId = this.lastID;
entranceIdMap[e.editor_id] = entranceId;
if (--pendingEntrances === 0) { entrance.apartments.forEach(apartment => {
insertApartments();
}
}
);
});
function insertApartments() {
for (const [editor_id, apartments] of Object.entries(data.apartments)) {
const entranceId = entranceIdMap[editor_id];
if (!entranceId) continue;
apartments.forEach(apartment => {
apartmentStmt.run( apartmentStmt.run(
entranceId, entranceID,
Number(apartment.apartment_number), Number(apartment.apartment_number),
apartment.title, apartment.title,
apartment.floors_number Number(apartment.floors_number),
function (err) {
if (err) console.error(err.message);
if (--pendingApartments === 0) {
if (--pendingEntrances === 0) finalize();
}
}
); );
}); });
} }
);
});
function finalize() {
entranceStmt.finalize(); entranceStmt.finalize();
apartmentStmt.finalize(); apartmentStmt.finalize();
res({ "status": "ok", "id": houseId }); res({ status: "ok", id: houseId });
} }
}); });
} else if (data.type == "homestead") {
let sql = ` } else if (data.type === "homestead") {
INSERT INTO const sql = `
homestead( INSERT INTO homestead (
group_id, title, number, zoom, points, geo, osm_id, settlement, created_at
title, ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
number,
points,
point_icons,
geo,
osm_id,
settlement,
created_at
)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)
`; `;
db.run(sql, [ db.run(sql, [
Number(data.group_id),
data.title, data.title,
data.number, data.number,
Number(data.zoom),
JSON.stringify(data.points), JSON.stringify(data.points),
JSON.stringify(data.point_icons),
JSON.stringify(data.geo), JSON.stringify(data.geo),
JSON.stringify(data.osm_id), JSON.stringify(data.osm_id),
data.settlement, data.settlement,
Math.floor(new Date(Date.now()).getTime()) Math.floor(Date.now())
], function (err) { ], function (err) {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
return res(false); return res(false);
} else if (this.changes === 0) { }
if (this.changes === 0) {
return res(false); return res(false);
} else { }
saveCards({ center: data.geo, wayId: data.osm_id, zoom: data.zoom ?? 17, type: "homestead", number: this.lastID, address: `${data.title} ${data.number}` })
res({ "status": "ok", "id": this.lastID }); const homesteadId = this.lastID;
// saveCards({ center: data.geo, wayId: data.osm_id, zoom: data.zoom ?? 17, type: "homestead", number: homesteadId, address: `${data.title} ${data.number}` });
const buildingStmt = db.prepare(`
INSERT INTO buildings (
homestead_id, title, geo
) VALUES (?, ?, ?)
`);
let pendingBuildings = data.buildings.length;
if (pendingBuildings === 0) return finalize();
data.buildings.forEach(building => {
buildingStmt.run(
homesteadId,
building.title,
JSON.stringify(building.geo),
function (err) {
if (err) {
console.error(err.message);
return res(false);
}
if (--pendingBuildings === 0) finalize();
}
);
});
function finalize() {
buildingStmt.finalize();
res({ status: "ok", id: homesteadId });
} }
}); });
} else { } else {

View File

@@ -30,10 +30,6 @@ class EntrancesService {
"house_id": Number(row.house_id), "house_id": Number(row.house_id),
"entrance_number": Number(row.entrance_number), "entrance_number": Number(row.entrance_number),
"title": row.title, "title": row.title,
"points": JSON.parse(row.points),
"points_number": JSON.parse(row.points_number),
"floors_quantity": row.floors_quantity,
"apartments_quantity": row.apartments_quantity,
"description": row.description, "description": row.description,
"created_at": Number(row.created_at), "created_at": Number(row.created_at),
"updated_at": Number(row.updated_at), "updated_at": Number(row.updated_at),
@@ -65,29 +61,19 @@ class EntrancesService {
house_id, house_id,
entrance_number, entrance_number,
title, title,
points,
points_number,
floors_quantity,
apartments_quantity,
description, description,
created_at, created_at
updated_at
) )
VALUES VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) (?, ?, ?, ?, ?)
`; `;
db.run(sql, [ db.run(sql, [
house_id, house_id,
Number(data.entrance_number), Number(data.entrance_number),
data.title, data.title,
JSON.stringify(data.points),
JSON.stringify(data.points_number),
data.floors_quantity,
data.apartments_quantity,
data.description, data.description,
Math.floor(new Date(Date.now()).getTime()), Math.floor(new Date(Date.now()).getTime())
Math.floor(new Date(Date.now()).getTime()),
], function(err) { ], function(err) {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
@@ -108,10 +94,6 @@ class EntrancesService {
entrance entrance
SET SET
title = ?, title = ?,
points = ?,
points_number = ?,
floors_quantity = ?,
apartments_quantity = ?,
description = ?, description = ?,
updated_at = ? updated_at = ?
WHERE WHERE

View File

@@ -64,26 +64,121 @@ class HistoryEntranceService {
}); });
} }
updateHistoryEntrance(entrance_id) { updateHistoryEntrance(entrance_history_id) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
console.log(Number(entrance_id)); const endTime = Date.now(); // 🕒 Час завершення
const entranceHistoryId = Number(entrance_history_id);
let sql = 'UPDATE entrance_history SET date_end = ?, working = ? WHERE id = ?'; // 🔧 Оновлюємо запис в entrance_history
db.run(sql, [ db.run(
Math.floor(new Date(Date.now()).getTime()), `UPDATE entrance_history SET date_end = ?, working = 0 WHERE id = ?`,
0, [endTime, entranceHistoryId],
Number(entrance_id) function (err) {
], function(err) { if (err || this.changes === 0) {
if (err) { console.error("❌ Помилка оновлення entrance_history:", err?.message);
console.error(err.message);
return res(false); return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "update": "ok", "id": entrance_id });
} }
// 🏢 Отримуємо entrance_id
db.get(
`SELECT entrance_id FROM entrance_history WHERE id = ?`,
[entranceHistoryId],
(err, entrance) => {
if (err || !entrance) {
console.error("❌ Помилка отримання під’їзду:", err?.message);
return res(false);
}
// 🧱 Отримуємо квартири
db.all(
`SELECT * FROM apartments WHERE entrance_id = ? ORDER BY id`,
[entrance.entrance_id],
(err, apartments) => {
if (err) {
console.error("❌ Помилка отримання квартир:", err.message);
return res(false);
}
if (!apartments.length) {
return res({ update: "ok", id: entranceHistoryId, inserted: 0 });
}
const toUpdate = apartments.filter(a => ![2, 6, 0, null].includes(a.status));
// 📝 Готуємо вставку в історію
const stmtInsert = db.prepare(`
INSERT INTO apartments_history
(sheep_id, apartments_id, status, description, created_at)
VALUES (?, ?, ?, ?, ?)
`);
const stmtUpdate = db.prepare(`
UPDATE apartments
SET status = NULL, description = NULL, sheep_id = NULL, updated_at = ?
WHERE id = ?
`);
let completed = 0;
let inserted = 0;
if (!toUpdate.length) {
stmtInsert.finalize();
stmtUpdate.finalize();
return res({ update: "ok", id: entranceHistoryId, inserted: 0 });
}
for (const apt of toUpdate) {
// 🔄 Оновлюємо квартиру
stmtUpdate.run([endTime, apt.id]);
// 🪵 Вставляємо в історію
stmtInsert.run(
[apt.sheep_id, apt.id, apt.status, apt.description, endTime],
(err) => {
if (!err) inserted++;
completed++;
if (completed === toUpdate.length) {
stmtInsert.finalize();
stmtUpdate.finalize();
return res({
update: "ok",
id: entranceHistoryId,
inserted,
}); });
}
}
);
}
}
);
}
);
}
);
}); });
// return new Promise((res, rej) => {
// console.log(Number(entrance_id));
// let sql = 'UPDATE entrance_history SET date_end = ?, working = ? WHERE id = ?';
// db.run(sql, [
// Math.floor(new Date(Date.now()).getTime()),
// 0,
// Number(entrance_id)
// ], function(err) {
// if (err) {
// console.error(err.message);
// return res(false);
// } else if (this.changes === 0) {
// return res(false);
// } else {
// res({ "update": "ok", "id": entrance_id });
// }
// });
// });
} }
deleteHistoryEntrance(entrance_id) { deleteHistoryEntrance(entrance_id) {

View File

@@ -30,8 +30,16 @@ class HomesteadsService {
(SELECT homestead_history.date_end FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_date_end (SELECT homestead_history.date_end FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_date_end
FROM FROM
homestead homestead
WHERE JOIN
group_id == '${group}' homestead_history
ON
homestead.id = homestead_history.homestead_id
AND
homestead_history.working = 1
AND
homestead_history.name = 'Групова'
AND
homestead_history.group_id == '${group}'
`; `;
} }
@@ -52,12 +60,10 @@ class HomesteadsService {
homestead_history homestead_history
ON ON
homestead.id = homestead_history.homestead_id homestead.id = homestead_history.homestead_id
WHERE
homestead.group_id = '${group}'
AND AND
homestead_history.working = 1 homestead_history.working = 1
AND AND
homestead_history.name IN ('Групова', '${sheepName}'); homestead_history.name = '${sheepName}';
`; `;
} }
@@ -69,12 +75,11 @@ class HomesteadsService {
let data = rows.map((row) => { let data = rows.map((row) => {
return { return {
"id": Number(row.id), "id": Number(row.id),
"group_id": Number(row.group_id),
"title": row.title, "title": row.title,
"number": row.number, "number": row.number,
"points": JSON.parse(row.points), "points": JSON.parse(row.points),
"point_icons": JSON.parse(row.point_icons),
"geo": JSON.parse(row.geo), "geo": JSON.parse(row.geo),
"zoom": Number(row.zoom),
"osm_id": JSON.parse(row.osm_id), "osm_id": JSON.parse(row.osm_id),
"settlement": row.settlement, "settlement": row.settlement,
"description": row.description, "description": row.description,
@@ -127,12 +132,11 @@ class HomesteadsService {
} else { } else {
let data = { let data = {
"id": Number(row.id), "id": Number(row.id),
"group_id": Number(row.group_id),
"title": row.title, "title": row.title,
"number": row.number, "number": row.number,
"points": JSON.parse(row.points), "points": JSON.parse(row.points),
"point_icons": JSON.parse(row.point_icons),
"geo": JSON.parse(row.geo), "geo": JSON.parse(row.geo),
"zoom": Number(row.zoom),
"osm_id": JSON.parse(row.osm_id), "osm_id": JSON.parse(row.osm_id),
"settlement": row.settlement, "settlement": row.settlement,
"description": row.description, "description": row.description,
@@ -160,27 +164,25 @@ class HomesteadsService {
let sql = ` let sql = `
INSERT INTO INSERT INTO
homestead( homestead(
group_id,
title, title,
number, number,
points, points,
point_icons,
geo, geo,
zoom,
osm_id, osm_id,
settlement, settlement,
created_at created_at
) )
VALUES VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?) (?, ?, ?, ?, ?, ?, ?, ?)
`; `;
db.run(sql, [ db.run(sql, [
Number(data.group_id),
data.title, data.title,
data.number, data.number,
JSON.stringify(data.points), JSON.stringify(data.points),
JSON.stringify(data.point_icons),
JSON.stringify(data.geo), JSON.stringify(data.geo),
Number(data.zoom),
JSON.stringify(data.osm_id), JSON.stringify(data.osm_id),
data.settlement, data.settlement,
Math.floor(new Date(Date.now()).getTime()) Math.floor(new Date(Date.now()).getTime())
@@ -203,12 +205,11 @@ class HomesteadsService {
UPDATE UPDATE
homestead homestead
SET SET
group_id = ?,
title = ?, title = ?,
number = ?, number = ?,
points = ?, points = ?,
point_icons = ?,
geo = ?, geo = ?,
zoom = ?,
osm_id = ?, osm_id = ?,
settlement = ?, settlement = ?,
description = ?, description = ?,
@@ -217,12 +218,11 @@ class HomesteadsService {
id = ? id = ?
`; `;
db.run(sql, [ db.run(sql, [
Number(data.group_id),
data.title, data.title,
data.number, data.number,
JSON.stringify(data.points), JSON.stringify(data.points),
JSON.stringify(data.point_icons),
JSON.stringify(data.geo), JSON.stringify(data.geo),
Number(data.zoom),
JSON.stringify(data.osm_id), JSON.stringify(data.osm_id),
data.settlement, data.settlement,
data.description, data.description,

View File

@@ -1,6 +1,68 @@
const db = require("../config/db"); const db = require("../config/db");
class HousesService { class HousesService {
getListEntrance() {
return new Promise((res, rej) => {
let sql = `
SELECT
entrance.*,
COALESCE((SELECT entrance_history.working FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1), 0) AS working,
(SELECT entrance_history.name FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1) AS entrance_history_name,
(SELECT entrance_history.group_id FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1) AS entrance_history_group_id,
(SELECT entrance_history.sheep_id FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1) AS entrance_history_sheep_id,
(SELECT entrance_history.id FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1) AS entrance_history_id,
(SELECT entrance_history.date_start FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1) AS entrance_history_date_start,
(SELECT entrance_history.date_end FROM entrance_history WHERE entrance_history.entrance_id = entrance.id ORDER BY entrance_history.date_start DESC LIMIT 1) AS entrance_history_date_end,
(SELECT house.settlement FROM house WHERE house.id = entrance.house_id) AS house_settlement,
(SELECT house.title FROM house WHERE house.id = entrance.house_id) AS house_title,
(SELECT house.number FROM house WHERE house.id = entrance.house_id) AS house_number
FROM
entrance
`;
db.all(sql, (err, rows) => {
if (err) {
console.error(err.message);
return res(false);
} else {
let data = rows.map((row) => {
return {
"id": Number(row.id),
"house": {
"id": Number(row.house_id),
"title": row.house_title,
"number": row.house_number,
"settlement": row.house_settlement
},
"entrance_number": Number(row.entrance_number),
"title": row.title,
"points": JSON.parse(row.points),
"points_number": JSON.parse(row.points_number),
"floors_quantity": row.floors_quantity,
"apartments_quantity": row.apartments_quantity,
"description": row.description,
"created_at": Number(row.created_at),
"updated_at": Number(row.updated_at),
"working": Number(row.working) == 0 ? false : true,
"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,
"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,
"end": row.entrance_history_date_end ? Number(row.entrance_history_date_end) : null
}
}
}
})
return res(data);
}
});
});
}
getList(group, sheepName) { getList(group, sheepName) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
let sql = ` let sql = `
@@ -14,18 +76,25 @@ class HousesService {
if (group != "0" && !sheepName) { if (group != "0" && !sheepName) {
sql = ` sql = `
SELECT SELECT DISTINCT
house.*, house.*,
(SELECT COUNT(DISTINCT entrance_history.id) FROM entrance_history JOIN entrance ON entrance.id = entrance_history.entrance_id WHERE entrance.house_id = house.id AND entrance_history.working = 1 ORDER BY entrance_history.date_start DESC) AS working, (SELECT COUNT(DISTINCT entrance_history.id) FROM entrance_history JOIN entrance ON entrance.id = entrance_history.entrance_id WHERE entrance.house_id = house.id AND entrance_history.working = 1 ORDER BY entrance_history.date_start DESC) AS working,
(SELECT COUNT(*) FROM entrance WHERE entrance.house_id = house.id) AS entrance_quantity (SELECT COUNT(*) FROM entrance WHERE entrance.house_id = house.id) AS entrance_quantity
FROM FROM
house house
JOIN
entrance ON entrance.house_id = house.id
JOIN
entrance_history ON entrance_history.entrance_id = entrance.id
WHERE WHERE
group_id == '${group}' entrance_history.working = 1
AND
entrance_history.name = 'Групова'
AND
entrance_history.group_id == '${group}'
`; `;
} }
if (sheepName) { if (sheepName) {
sql = ` sql = `
SELECT DISTINCT SELECT DISTINCT
@@ -39,11 +108,9 @@ class HousesService {
JOIN JOIN
entrance_history ON entrance_history.entrance_id = entrance.id entrance_history ON entrance_history.entrance_id = entrance.id
WHERE WHERE
house.group_id = '${group}'
AND
entrance_history.working = 1 entrance_history.working = 1
AND AND
entrance_history.name IN ('Групова', '${sheepName}'); entrance_history.name = '${sheepName}'
`; `;
} }
@@ -55,12 +122,12 @@ class HousesService {
let data = rows.map((row) => { let data = rows.map((row) => {
return { return {
"id": Number(row.id), "id": Number(row.id),
"group_id": Number(row.group_id),
"title": row.title, "title": row.title,
"number": row.number, "number": row.number,
"points": JSON.parse(row.points), "points": JSON.parse(row.points),
"points_number": JSON.parse(row.points_number), "points_number": JSON.parse(row.points_number),
"geo": JSON.parse(row.geo), "geo": JSON.parse(row.geo),
"zoom": Number(row.zoom),
"osm_id": JSON.parse(row.osm_id), "osm_id": JSON.parse(row.osm_id),
"settlement": row.settlement, "settlement": row.settlement,
"description": row.description, "description": row.description,
@@ -102,12 +169,12 @@ class HousesService {
} else { } else {
let data = { let data = {
"id": Number(row.id), "id": Number(row.id),
"group_id": Number(row.group_id),
"title": row.title, "title": row.title,
"number": row.number, "number": row.number,
"points": JSON.parse(row.points), "points": JSON.parse(row.points),
"points_number": JSON.parse(row.points_number), "points_number": JSON.parse(row.points_number),
"geo": JSON.parse(row.geo), "geo": JSON.parse(row.geo),
"zoom": Number(row.zoom),
"osm_id": JSON.parse(row.osm_id), "osm_id": JSON.parse(row.osm_id),
"settlement": row.settlement, "settlement": row.settlement,
"description": row.description, "description": row.description,
@@ -129,12 +196,12 @@ class HousesService {
let sql = ` let sql = `
INSERT INTO INSERT INTO
house( house(
group_id,
title, title,
number, number,
points, points,
points_number, points_number,
geo, geo,
zoom
osm_id, osm_id,
settlement, settlement,
description, description,
@@ -146,12 +213,12 @@ class HousesService {
`; `;
db.run(sql, [ db.run(sql, [
Number(data.group_id),
data.title, data.title,
data.number, data.number,
JSON.stringify(data.points), JSON.stringify(data.points),
JSON.stringify(data.points_number), JSON.stringify(data.points_number),
JSON.stringify(data.geo), JSON.stringify(data.geo),
Number(data.zoom),
JSON.stringify(data.osm_id), JSON.stringify(data.osm_id),
data.settlement, data.settlement,
data.description, data.description,
@@ -176,12 +243,12 @@ class HousesService {
UPDATE UPDATE
house house
SET SET
group_id = ?,
title = ?, title = ?,
number = ?, number = ?,
points = ?, points = ?,
points_number = ?, points_number = ?,
geo = ?, geo = ?,
zoom = ?,
osm_id = ?, osm_id = ?,
settlement = ?, settlement = ?,
description = ?, description = ?,
@@ -190,12 +257,12 @@ class HousesService {
id = ? id = ?
`; `;
db.run(sql, [ db.run(sql, [
Number(data.group_id),
data.title, data.title,
data.number, data.number,
JSON.stringify(data.points), JSON.stringify(data.points),
JSON.stringify(data.points_number), JSON.stringify(data.points_number),
JSON.stringify(data.geo), JSON.stringify(data.geo),
Number(data.zoom),
JSON.stringify(data.osm_id), JSON.stringify(data.osm_id),
data.settlement, data.settlement,
data.description, data.description,

View File

@@ -0,0 +1,70 @@
const crypto = require('crypto');
const db = require("../config/db");
const webpush = require('web-push');
const VAPID_PUBLIC_KEY = process.env.VAPID_PUBLIC_KEY;
const VAPID_PRIVATE_KEY = process.env.VAPID_PRIVATE_KEY;
webpush.setVapidDetails(
'mailto:rozenrod320@gmail.com',
VAPID_PUBLIC_KEY,
VAPID_PRIVATE_KEY
);
class PushService {
getKey() {
return new Promise((res, rej) => {
return res({ "publicKey": VAPID_PUBLIC_KEY });
});
}
createSubscribe(sheep_id, data) {
return new Promise((res, rej) => {
let sql = 'INSERT INTO subscription(sheep_id, endpoint, keys, device_name, device_model, created_at) VALUES (?, ?, ?, ?, ?, ?)';
db.run(sql, [
Number(sheep_id),
data.subscription.endpoint,
JSON.stringify(data.subscription.keys),
data.device?.name || "unknown",
data.device?.model || "unknown",
Math.floor(Date.now())
], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else {
const payload = JSON.stringify(
{
"title": "Тестове повідомлення",
"body": "Ви успішно підписалися на отримання push повідомлень!",
"url": `https://${process.env.DOMAIN}`
});
webpush.sendNotification(data.subscription, payload).catch(err => {
console.error('Ошибка отправки:', err);
})
return res({ "status": "ok" });
}
});
});
}
deleteSubscribe(data) {
return new Promise((res, rej) => {
db.run('DELETE FROM subscription WHERE endpoint = ?', [data.endpoint], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "status": "ok" });
}
});
});
}
}
module.exports = new PushService();

View File

@@ -1,106 +0,0 @@
const sqlite3 = require("sqlite3").verbose();
const path = require('path');
const TelegramConfig = require('../config/telegram.config.js');
const TelegramBot = require('node-telegram-bot-api');
const fs = require('fs');
const dbPath = process.env.DATABASE_PATH || '../';
const db = new sqlite3.Database(path.join(dbPath, 'database.sqlite'));
const bot = new TelegramBot(TelegramConfig.token, { polling: false });
class RotationService {
async editTables() {
await bot.sendDocument(TelegramConfig.chatId, fs.createReadStream(path.join(dbPath, 'database.sqlite')), {
filename: "database.sqlite",
contentType: "application/x-sqlite3",
caption: "Резервна копія БД Manager Territory 📄 перед проведенням ротації територій!"
});
console.log("Резервна копія БД відправленна в Telegram");
return new Promise((resolve, reject) => {
db.serialize(() => {
db.get("SELECT MAX(group_id) AS max_id FROM homestead", (err, row) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при отриманні max_id для homestead" });
}
const maxIdHomestead = row?.max_id;
if (maxIdHomestead === null) {
return reject({ "message": "Таблиця homestead пуста або group_id відсутній" });
}
db.get("SELECT MAX(group_id) AS max_id FROM house", (err, row) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при отриманні max_id для house" });
}
const maxIdHouse = row?.max_id;
if (maxIdHouse === null) {
return reject({ "message": "Таблиця house пуста або group_id відсутній" });
}
const currentUnixTime = Math.floor(Date.now());
// Оновлюємо group_id в обох таблицах
db.run("UPDATE homestead SET updated_at = ?, group_id = group_id + 1 WHERE group_id < ? AND group_id > 0", [currentUnixTime, maxIdHomestead + 1], (err) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при оновленні значень у homestead" });
}
db.run("UPDATE homestead SET updated_at = ?, group_id = 1 WHERE group_id = ?", [currentUnixTime, maxIdHomestead + 1], (err) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при встановленні group_id = 1 у homestead" });
}
db.run("UPDATE house SET updated_at = ?, group_id = group_id + 1 WHERE group_id < ? AND group_id > 0", [currentUnixTime, maxIdHouse + 1], (err) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при оновленні значень у house" });
}
db.run("UPDATE house SET updated_at = ?, group_id = 1 WHERE group_id = ?", [currentUnixTime, maxIdHouse + 1], (err) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при встановленні group_id = 1 у house" });
}
console.log("Ротація homestead та house завершилась успішно");
// Після оновлення homestead та house ми оновлюємо homestead_history та entrance_history
db.run("UPDATE homestead_history SET date_end = ?, working = 0 WHERE working = 1", [currentUnixTime], (err) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при оновленні homestead_history" });
}
db.run("UPDATE entrance_history SET date_end = ?, working = 0 WHERE working = 1", [currentUnixTime], (err) => {
if (err) {
console.error(err.message);
return reject({ "message": "Помилка при оновленні entrance_history" });
}
console.log("Ротація homestead_history та entrance_history завершилась успішно");
resolve({ "message": "Ротація завершилась успішно" });
});
});
});
});
});
});
});
});
});
}).catch(error => {
console.error('Помилка при проведенні ротації:', error.message);
return error;
});
}
}
module.exports = new RotationService();

View File

@@ -2,392 +2,272 @@ const crypto = require('crypto');
const db = require("../config/db"); const db = require("../config/db");
class SheepService { class SheepService {
getSheep(uuid, sheepRole) { getSheep(id, mode) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
let sql = ` const sql = `
SELECT SELECT
sheeps.*, sheeps.*,
groups.group_number AS group_id, possibilities.can_add_sheeps,
administrators.id AS administrators_id, possibilities.can_view_sheeps,
administrators.uuid AS administrators_uuid, possibilities.can_add_territory,
moderators.id AS moderators_id, possibilities.can_view_territory,
moderators.uuid AS moderators_uuid, possibilities.can_manager_territory,
moderators.can_add_sheeps, possibilities.can_add_stand,
moderators.can_add_territory, possibilities.can_view_stand,
moderators.can_manager_territory, possibilities.can_manager_stand,
moderators.can_add_stand, possibilities.can_add_schedule,
moderators.can_manager_stand, possibilities.can_view_schedule
moderators.can_add_schedule
FROM FROM
sheeps sheeps
LEFT JOIN LEFT JOIN
groups ON groups.group_number = sheeps.group_id possibilities ON possibilities.sheep_id = sheeps.id
LEFT JOIN
administrators ON administrators.sheep_id = sheeps.id
LEFT JOIN
moderators ON moderators.sheep_id = sheeps.id
WHERE WHERE
sheeps.uuid = ? sheeps.id = ?
LIMIT 1; `;
`
db.get(sql, [uuid], (err, sheep) => { db.get(sql, [id], (err, sheep) => {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
return res(false); return res(false);
} else if (!sheep) {
console.log({ "error": "uuid not found" });
return res(false);
} else {
let data = {
"id": Number(sheep.id),
"group_id": Number(sheep.group_id),
"name": sheep.name,
"icon": sheep.icon,
"uuid": sheep.uuid,
"appointment": sheep.appointment,
"can_view_stand": sheep.can_view_stand == 0 ? false : true,
"can_view_schedule": sheep.can_view_schedule == 0 ? false : true,
"can_view_territory": sheep.can_view_territory == 0 ? false : true,
"administrator": {
"id": sheep.administrators_id ? sheep.administrators_id : false,
"uuid": null
},
"moderator": {
"id": sheep.moderators_id ? sheep.moderators_id : false,
"uuid": null,
"can_add_sheeps": sheep.can_add_sheeps == 1 ? true : false,
"can_add_territory": sheep.can_add_territory == 1 ? true : false,
"can_manager_territory": sheep.can_manager_territory == 1 ? true : false,
"can_add_stand": sheep.can_add_stand == 1 ? true : false,
"can_manager_stand": sheep.can_manager_stand == 1 ? true : false,
"can_add_schedule": sheep.can_add_schedule == 1 ? true : false
} }
if (!sheep) {
console.log({ error: "id not found" });
return res(false);
} }
if (sheepRole == "administrator") { const fields = [
if (sheep.administrators_id) { "can_add_sheeps",
data.administrator.uuid = sheep.administrators_uuid; "can_view_sheeps",
} "can_add_territory",
if (sheep.moderators_id) { "can_view_territory",
data.moderator.uuid = sheep.moderators_uuid; "can_manager_territory",
} "can_add_stand",
"can_view_stand",
"can_manager_stand",
"can_add_schedule",
"can_view_schedule"
];
const data = {
id: sheep.id,
group_id: sheep.group_id,
name: sheep.name,
icon: sheep.icon,
uuid: sheep.uuid,
uuid_manager: mode && mode === 2 ? sheep.uuid_manager : null,
appointment: sheep.appointment,
mode: mode ? Number(sheep.mode) : 0,
mode_title: sheep.mode_title,
possibilities: {}
};
fields.forEach(f => {
data.possibilities[f] = false;
});
if (mode && (mode === 1 || mode === 2)) {
fields.forEach(f => {
data.possibilities[f] = !!sheep[f];
});
} }
return res(data); return res(data);
}
}); });
}); });
} }
getList(sheepRole) { getList(mode) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
let sql = ` const sql = `
SELECT SELECT
sheeps.*, sheeps.*,
groups.group_number AS group_id, possibilities.can_add_sheeps,
administrators.id AS administrators_id, possibilities.can_view_sheeps,
administrators.uuid AS administrators_uuid, possibilities.can_add_territory,
moderators.id AS moderators_id, possibilities.can_view_territory,
moderators.uuid AS moderators_uuid, possibilities.can_manager_territory,
moderators.can_add_sheeps, possibilities.can_add_stand,
moderators.can_add_territory, possibilities.can_view_stand,
moderators.can_manager_territory, possibilities.can_manager_stand,
moderators.can_add_stand, possibilities.can_add_schedule,
moderators.can_manager_stand, possibilities.can_view_schedule
moderators.can_add_schedule
FROM FROM
sheeps sheeps
LEFT JOIN LEFT JOIN
groups ON groups.group_number = sheeps.group_id possibilities ON possibilities.sheep_id = sheeps.id
LEFT JOIN
administrators ON administrators.sheep_id = sheeps.id
LEFT JOIN
moderators ON moderators.sheep_id = sheeps.id
ORDER BY ORDER BY
id sheeps.group_id
`; `;
db.all(sql, (err, sheeps) => { db.all(sql, (err, rows) => {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
return res(false); return res(false);
} else {
let result = sheeps.map((sheep) => {
let data = {
"id": Number(sheep.id),
"group_id": Number(sheep.group_id),
"name": sheep.name,
"icon": sheep.icon,
"uuid": sheep.uuid,
"appointment": sheep.appointment,
"can_view_stand": sheep.can_view_stand == 0 ? false : true,
"can_view_schedule": sheep.can_view_schedule == 0 ? false : true,
"can_view_territory": sheep.can_view_territory == 0 ? false : true,
"administrator": {
"id": sheep.administrators_id ? sheep.administrators_id : false,
"uuid": null
},
"moderator": {
"id": sheep.moderators_id ? sheep.moderators_id : false,
"uuid": null,
"can_add_sheeps": sheep.can_add_sheeps == 1 ? true : false,
"can_add_territory": sheep.can_add_territory == 1 ? true : false,
"can_manager_territory": sheep.can_manager_territory == 1 ? true : false,
"can_add_stand": sheep.can_add_stand == 1 ? true : false,
"can_manager_stand": sheep.can_manager_stand == 1 ? true : false,
"can_add_schedule": sheep.can_add_schedule == 1 ? true : false
}
} }
if (sheepRole == "administrator") { const fields = [
if (sheep.administrators_id) { "can_add_sheeps",
data.administrator.uuid = sheep.administrators_uuid; "can_view_sheeps",
} "can_add_territory",
if (sheep.moderators_id) { "can_view_territory",
data.moderator.uuid = sheep.moderators_uuid; "can_manager_territory",
} "can_add_stand",
"can_view_stand",
"can_manager_stand",
"can_add_schedule",
"can_view_schedule"
];
const result = rows.map(sheep => {
const data = {
id: sheep.id,
group_id: sheep.group_id,
name: sheep.name,
icon: sheep.icon,
uuid: sheep.uuid,
uuid_manager: (mode && mode == 2) ? sheep.uuid_manager : null,
appointment: sheep.appointment,
mode: mode ? Number(sheep.mode) : 0,
mode_title: sheep.mode_title,
possibilities: {}
};
fields.forEach(f => {
data.possibilities[f] = false;
});
if (mode && (mode == 1 || mode == 2)) {
fields.forEach(f => {
data.possibilities[f] = !!sheep[f];
});
} }
return data; return data;
}) });
return res(result); res(result);
}
});
});
}
getAdministrator(uuid) {
return new Promise((res, rej) => {
let sql = `
SELECT
sheeps.*,
groups.group_number AS group_id,
administrators.id AS administrators_id,
administrators.uuid AS administrators_uuid
FROM
sheeps
JOIN
administrators ON sheeps.id = administrators.sheep_id
LEFT JOIN
groups ON groups.group_number = sheeps.group_id
WHERE
administrators.uuid = ?
LIMIT 1;
`
db.get(sql, [uuid], (err, sheep) => {
if (err) {
console.error(err.message);
return res(false);
} else if (!sheep) {
console.log({ "error": "uuid not found" });
return res(false);
} else {
let data = {
"id": Number(sheep.id),
"group_id": Number(sheep.group_id),
"name": sheep.name,
"icon": sheep.icon,
"uuid": sheep.uuid,
"appointment": sheep.appointment,
"can_view_stand": sheep.can_view_stand == 0 ? false : true,
"can_view_schedule": sheep.can_view_schedule == 0 ? false : true,
"can_view_territory": sheep.can_view_territory == 0 ? false : true,
"administrator": {
"id": sheep.administrators_id,
"uuid": sheep.administrators_uuid
},
"moderator": false
}
return res(data);
}
});
});
}
getModerator(uuid) {
return new Promise((res, rej) => {
let sql = `
SELECT
sheeps.*,
groups.group_number AS group_id,
moderators.id AS moderators_id,
moderators.uuid AS moderators_uuid,
moderators.can_add_sheeps AS can_add_sheeps,
moderators.can_add_territory AS can_add_territory,
moderators.can_manager_territory AS can_manager_territory,
moderators.can_add_stand AS can_add_stand,
moderators.can_manager_stand AS can_manager_stand,
moderators.can_add_schedule AS can_add_schedule
FROM
sheeps
JOIN
moderators ON sheeps.id = moderators.sheep_id
LEFT JOIN
groups ON groups.group_number = sheeps.group_id
WHERE
moderators.uuid = ?
LIMIT 1;
`
db.get(sql, [uuid], (err, sheep) => {
if (err) {
console.error(err.message);
return res(false);
} else if (!sheep) {
console.log({ "error": "uuid not found" });
return res(false);
} else {
let data = {
"id": Number(sheep.id),
"group_id": Number(sheep.group_id),
"name": sheep.name,
"icon": sheep.icon,
"uuid": sheep.uuid,
"appointment": sheep.appointment,
"can_view_stand": sheep.can_view_stand == 0 ? false : true,
"can_view_schedule": sheep.can_view_schedule == 0 ? false : true,
"can_view_territory": sheep.can_view_territory == 0 ? false : true,
"administrator": false,
"moderator": {
"id": sheep.moderators_id,
"uuid": sheep.moderators_uuid,
"can_add_sheeps": sheep.can_add_sheeps == 0 ? false : true,
"can_add_territory": sheep.can_add_territory == 0 ? false : true,
"can_manager_territory": sheep.can_manager_territory == 0 ? false : true,
"can_add_stand": sheep.can_add_stand == 0 ? false : true,
"can_manager_stand": sheep.can_manager_stand == 0 ? false : true,
"can_add_schedule": sheep.can_add_schedule == 0 ? false : true
}
}
return res(data);
}
}); });
}); });
} }
createSheep(data) { createSheep(data) {
return new Promise((res, rej) => { const stmt1 = db.prepare('INSERT INTO sheeps(name, group_id, appointment, uuid) VALUES (?, ?, ?, ?)');
let sql = 'INSERT INTO sheeps(name, group_id, appointment, uuid) VALUES (?, ?, ?, ?)'; const stmt2 = db.prepare('INSERT INTO possibilities(can_view_territory, sheep_id) VALUES (?, ?)');
return new Promise((res, rej) => {
db.serialize(() => {
let uuid = crypto.randomUUID(); let uuid = crypto.randomUUID();
db.run(sql, [ stmt1.run([
data.name, data.name,
Number(data.group_id), Number(data.group_id),
data.appointment, data.appointment,
uuid uuid
], function (err) { ], function (err) {
if (err) { if (err) return rej(err);
console.error(err.message);
return res(false); const newSheepId = this.lastID;
} else if (this.changes === 0) {
return res(false); stmt2.run([
} else { 1,
res({ "status": "ok", "id": this.lastID, "uuid": uuid }); newSheepId
} ], (err2) => {
if (err2) return rej(err2);
res({ status: "ok", id: newSheepId, uuid: uuid });
});
});
}); });
}); });
} }
updateSheep(data) { updateSheep(data) {
return new Promise(async (res, rej) => { const stmt1 = db.prepare(`
try { UPDATE
let sql = ` sheeps
UPDATE sheeps SET
SET name = ?, group_id = ?, appointment = ?, name = ?,
can_view_stand = ?, can_view_schedule = ?, can_view_territory = ? group_id = ?,
WHERE uuid = ? appointment = ?,
`; mode = ?,
mode_title = ?,
uuid_manager = ?
WHERE
uuid = ?
`);
await db.run(sql, [ const stmt2 = db.prepare(`
UPDATE
possibilities
SET
can_add_sheeps = ?,
can_view_sheeps = ?,
can_add_territory = ?,
can_view_territory = ?,
can_manager_territory = ?,
can_add_stand = ?,
can_view_stand = ?,
can_manager_stand = ?,
can_add_schedule = ?,
can_view_schedule = ?
WHERE
sheep_id = (SELECT id FROM sheeps WHERE uuid = ? LIMIT 1)
`);
return new Promise((res, rej) => {
db.serialize(() => {
let uuid_manager = crypto.randomUUID();
stmt1.run([
data.name, data.name,
Number(data.group_id), Number(data.group_id),
data.appointment, data.appointment,
data.can_view_stand ? 1 : 0, Number(data.mode),
data.can_view_schedule ? 1 : 0, data.mode_title,
data.can_view_territory ? 1 : 0, Number(data.mode) == 0 ? null : (data.uuid_manager ? data.uuid_manager : uuid_manager),
data.uuid data.uuid
]); ], (err) => {
if (err) return rej(err);
if (data.role === "administrator") {
if (!data.administrator?.id) {
await db.run(
'INSERT INTO administrators(sheep_id, uuid) VALUES (?, ?)',
[data.id, crypto.randomUUID()]
);
console.log({ insert: "ok" });
}
if (data.moderator?.id) {
await db.run('DELETE FROM moderators WHERE id = ?', [data.moderator.id]);
console.log({ delete: "ok" });
}
} else if (data.role === "moderator") {
if (!data.moderator?.id) {
await db.run(
`INSERT INTO moderators(sheep_id, can_add_sheeps, can_add_territory,
can_manager_territory, can_add_stand, can_manager_stand,
can_add_schedule, uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[
data.id,
data.moderator.can_add_sheeps ? 1 : 0,
data.moderator.can_add_territory ? 1 : 0,
data.moderator.can_manager_territory ? 1 : 0,
data.moderator.can_add_stand ? 1 : 0,
data.moderator.can_manager_stand ? 1 : 0,
data.moderator.can_add_schedule ? 1 : 0,
crypto.randomUUID()
]
);
console.log({ insert: "ok" });
} else {
await db.run(
`UPDATE moderators
SET can_add_sheeps = ?, can_add_territory = ?,
can_manager_territory = ?, can_add_stand = ?,
can_manager_stand = ?, can_add_schedule = ?
WHERE id = ?`,
[
data.moderator.can_add_sheeps ? 1 : 0,
data.moderator.can_add_territory ? 1 : 0,
data.moderator.can_manager_territory ? 1 : 0,
data.moderator.can_add_stand ? 1 : 0,
data.moderator.can_manager_stand ? 1 : 0,
data.moderator.can_add_schedule ? 1 : 0,
data.moderator.id
]
);
console.log({ update: "ok" });
}
if (data.administrator?.id) {
await db.run('DELETE FROM administrators WHERE id = ?', [data.administrator.id]);
console.log({ delete: "ok" });
}
} else if (data.role === "sheep") {
if (data.moderator?.id) {
await db.run('DELETE FROM moderators WHERE id = ?', [data.moderator.id]);
console.log({ delete: "ok" });
}
if (data.administrator?.id) {
await db.run('DELETE FROM administrators WHERE id = ?', [data.administrator.id]);
console.log({ delete: "ok" });
}
}
stmt2.run([
data.possibilities.can_add_sheeps,
data.possibilities.can_view_sheeps,
data.possibilities.can_add_territory,
data.possibilities.can_view_territory,
data.possibilities.can_manager_territory,
data.possibilities.can_add_stand,
data.possibilities.can_view_stand,
data.possibilities.can_manager_stand,
data.possibilities.can_add_schedule,
data.possibilities.can_view_schedule,
data.uuid
], (err2) => {
if (err2) return rej(err2);
res({ status: "ok", id: data.id }); res({ status: "ok", id: data.id });
} catch (err) { });
console.error(err.message); });
rej(false); });
}
}); });
} }
deleteSheep(data) { deleteSheep(data) {
const stmtSelect = db.prepare('SELECT id FROM sheeps WHERE uuid = ?');
const stmtDeletePoss = db.prepare('DELETE FROM possibilities WHERE sheep_id = ?');
const stmtDeleteSheep = db.prepare('DELETE FROM sheeps WHERE uuid = ?');
return new Promise((res, rej) => { return new Promise((res, rej) => {
db.run('DELETE FROM sheeps WHERE uuid = ?', [data.uuid], function (err) { db.serialize(() => {
if (err) { stmtSelect.get([data.uuid], (err, row) => {
console.error(err.message); if (err) return rej(err);
return res(false); if (!row) return rej(new Error("Sheep not found"));
} else if (this.changes === 0) {
return res(false); const sheepId = row.id;
} else {
res({ "dellete": "ok" }); stmtDeletePoss.run([sheepId], (err2) => {
} if (err2) return rej(err2);
stmtDeleteSheep.run([data.uuid], (err3) => {
if (err3) return rej(err3);
res({ status: "ok", deletedSheepId: sheepId });
});
});
});
}); });
}); });
} }

View File

@@ -0,0 +1,230 @@
const crypto = require('crypto');
const db = require("../config/db");
class StandService {
getStand(id) {
return new Promise((res, rej) => {
return res({ id });
});
}
getList() {
return new Promise((res, rej) => {
const sql = `
SELECT
*
FROM
stand_list
ORDER BY
id
`;
db.all(sql, (err, rows) => {
if (err) {
console.error(err.message);
return res(false);
} else {
let data = rows.map((row) => {
return {
"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);
}
});
});
}
createStand(data) {
return new Promise((res, rej) => {
let sql = 'INSERT INTO stand_list(title, geo, hour_start, hour_end, quantity_sheep, week_days, processing_time, status, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)';
db.run(sql, [
data.title,
JSON.stringify(data.geo || []),
Number(data.hour_start) || 9,
Number(data.hour_end) || 18,
Number(data.quantity_sheep) || 2,
JSON.stringify(data.week_days || [1]),
Number(data.processing_time) || 1,
1,
Math.floor(Date.now())
], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else {
res({ "status": "ok", "id": this.lastID });
}
});
});
}
updateStand(stand_id, data) {
return new Promise((res, rej) => {
const sql = `
UPDATE stand_list SET
title = COALESCE(?, title),
geo = COALESCE(?, geo),
hour_start = COALESCE(?, hour_start),
hour_end = COALESCE(?, hour_end),
quantity_sheep = COALESCE(?, quantity_sheep),
week_days = COALESCE(?, week_days),
status = COALESCE(?, status),
updated_at = ?
WHERE id = ?
`;
db.run(sql, [
data.title ?? null,
data.geo !== undefined ? JSON.stringify(data.geo) : null,
data.hour_start !== undefined ? Number(data.hour_start) : null,
data.hour_end !== undefined ? Number(data.hour_end) : null,
data.quantity_sheep !== undefined ? Number(data.quantity_sheep) : null,
data.week_days !== undefined ? JSON.stringify(data.week_days) : null,
data.status !== undefined ? (data.status ? 1 : 0) : null,
Date.now(),
stand_id
], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ status: "ok", id: stand_id });
}
});
});
}
deleteStand(stand_id) {
return new Promise((res, rej) => {
db.run('DELETE FROM stand_list WHERE id = ?', [stand_id], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "status": "ok", "id": stand_id });
}
});
});
}
createSchedule(stand_id) {
return new Promise((res, rej) => {
let date_start;
let getNextMonday = (ts) => {
let date = new Date(ts);
// следующий день после max_date
date.setDate(date.getDate() + 1);
// пока не понедельник добавляем дни
while (date.getDay() !== 1) {
date.setDate(date.getDate() + 1);
}
return date.getTime();
}
// 1. Получаем стенд
db.get(`SELECT * FROM stand_list WHERE id = ?`, [stand_id], (err, stand) => {
if (err) {
console.error(err.message);
return res(false);
}
if (!stand) {
console.log({ error: "id not found" });
return res(false);
}
stand.geo = JSON.parse(stand.geo);
stand.week_days = JSON.parse(stand.week_days)
// 2. Берём последний date из расписания
db.get(
`SELECT MAX(date) AS max_date FROM stand_schedule WHERE stand_id = ?`,
[stand.id],
(err, row) => {
if (err) {
console.error(err.message);
return res(false);
}
if (row && row.max_date) {
date_start = getNextMonday(row.max_date); // заменить начальную дату
} else {
date_start = getNextMonday(Date.now()); // заменить начальную дату
}
// 3. Генерация новых записей
const stand_length = (stand.hour_end - stand.hour_start) / stand.processing_time;
const timestamp = Math.floor(Date.now());
const list = [];
for (const dayOffset of stand.week_days) {
const stand_date = date_start + (dayOffset * 24 * 60 * 60 * 1000);
for (let i = 0; i < stand_length; i++) {
for (let q = 0; q < stand.quantity_sheep; q++) {
list.push([
stand.hour_start + (stand.processing_time * i),
q,
stand_date,
stand.id,
timestamp
]);
}
}
}
if (list.length === 0) {
return res({ status: "empty" });
}
// 4. Массовая вставка
const placeholders = list.map(() => "(?, ?, ?, ?, ?)").join(",");
const values = list.flat();
const insertSQL = `
INSERT INTO stand_schedule(hour, number_sheep, date, stand_id, updated_at)
VALUES ${placeholders}
`;
db.run(insertSQL, values, function (err) {
if (err) {
console.error(err.message);
return res(false);
}
res({ status: "ok", inserted: list.length });
});
}
);
});
});
}
getScheduleList(data) {
return new Promise((res, rej) => {
return res({ data });
});
}
getScheduleHistory(id) {
return new Promise((res, rej) => {
return res({ id });
});
}
}
module.exports = new StandService();

37
backup.py Normal file
View File

@@ -0,0 +1,37 @@
import os
import requests
from datetime import datetime
from zipfile import ZipFile, ZIP_DEFLATED
from dotenv import load_dotenv
# Загрузка переменных из .env
load_dotenv()
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
CHAT_ID = os.getenv('CHAT_ID')
DB_PATH = os.path.join(os.getenv('DB_PATH'), 'database.sqlite')
def send_document(filename, caption):
url = f'https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendDocument'
with open(filename, 'rb') as f:
response = requests.post(
url,
data={'chat_id': CHAT_ID, 'caption': caption},
files={'document': f}
)
print(response.json())
def main():
if not TELEGRAM_TOKEN or not CHAT_ID or not DB_PATH:
print("Помилка: TELEGRAM_TOKEN, CHAT_ID або DB_PATH не задано в .env.")
return
if os.path.exists(DB_PATH):
timestamp = datetime.now().strftime("%d.%m.%Y %H:%M")
caption = f"Backup Sheep Service DB - {timestamp}"
send_document(DB_PATH, caption)
else:
print("ZIP file not created")
if __name__ == "__main__":
main()

BIN
cards/cache/T80.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

BIN
cards/homestead/H1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
cards/homestead/H10.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
cards/homestead/H11.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
cards/homestead/H12.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
cards/homestead/H13.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
cards/homestead/H14.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
cards/homestead/H15.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
cards/homestead/H16.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
cards/homestead/H17.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
cards/homestead/H18.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
cards/homestead/H19.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
cards/homestead/H2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
cards/homestead/H20.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
cards/homestead/H21.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
cards/homestead/H22.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
cards/homestead/H23.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
cards/homestead/H24.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
cards/homestead/H25.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
cards/homestead/H26.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
cards/homestead/H27.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
cards/homestead/H28.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
cards/homestead/H29.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
cards/homestead/H3.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
cards/homestead/H30.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
cards/homestead/H31.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
cards/homestead/H32.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
cards/homestead/H33.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
cards/homestead/H34.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
cards/homestead/H35.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
cards/homestead/H36.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
cards/homestead/H37.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
cards/homestead/H38.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
cards/homestead/H39.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
cards/homestead/H4.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
cards/homestead/H40.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
cards/homestead/H41.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
cards/homestead/H42.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
cards/homestead/H43.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
cards/homestead/H44.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
cards/homestead/H45.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
cards/homestead/H46.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
cards/homestead/H47.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
cards/homestead/H48.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
cards/homestead/H49.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
cards/homestead/H5.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
cards/homestead/H50.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
cards/homestead/H51.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
cards/homestead/H52.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
cards/homestead/H6.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
cards/homestead/H7.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
cards/homestead/H8.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
cards/homestead/H9.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
cards/house/T1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
cards/house/T10.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
cards/house/T11.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
cards/house/T12.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
cards/house/T13.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
cards/house/T14.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
cards/house/T15.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
cards/house/T16.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Some files were not shown because too many files have changed in this diff Show More