v0.0.1
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
92
.gitignore
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
firebase-debug.log*
|
||||
firebase-debug.*.log*
|
||||
|
||||
# Firebase cache
|
||||
.firebase/
|
||||
|
||||
# Firebase config
|
||||
|
||||
# Uncomment this if you'd like others to create their own Firebase project.
|
||||
# For a team working on the same Firebase project(s), it is recommended to leave
|
||||
# it commented so all members can deploy to the same project(s) in .firebaserc.
|
||||
# .firebaserc
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
node_modules
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
.ftppass
|
||||
|
||||
*.sqlite
|
||||
261
README.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# Start
|
||||
|
||||
```
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
```
|
||||
sudo apt upgrade -y
|
||||
```
|
||||
|
||||
```
|
||||
sudo apt install -y curl gnupg2 ca-certificates lsb-release
|
||||
```
|
||||
|
||||
```
|
||||
sudo apt install zip
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# Docker Install
|
||||
|
||||
```
|
||||
sudo apt update
|
||||
sudo apt install -y docker.io
|
||||
sudo systemctl enable --now docker
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# Docker Compose Install
|
||||
|
||||
```
|
||||
sudo apt install -y docker-compose
|
||||
```
|
||||
|
||||
```
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# Start Sheep-Service
|
||||
|
||||
```
|
||||
cd /home/rozenrod/webapps/sheep-service.com
|
||||
```
|
||||
|
||||
```
|
||||
docker-compose pull && docker-compose -p Sheep-Service up --build -d
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# NGINX Install
|
||||
|
||||
```
|
||||
sudo apt install nginx
|
||||
```
|
||||
|
||||
```
|
||||
sudo systemctl status nginx
|
||||
```
|
||||
|
||||
```
|
||||
sudo systemctl enable nginx
|
||||
```
|
||||
|
||||
```
|
||||
sudo nano /etc/nginx/nginx.conf
|
||||
```
|
||||
|
||||
```
|
||||
proxy_cache_path /etc/nginx/cache levels=1:2 keys_zone=all:5m inactive=10m max_size=2g;
|
||||
|
||||
limit_req_zone $binary_remote_addr zone=one:5m rate=30r/s;
|
||||
client_max_body_size 20M;
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
```
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 2400:cb00::/32;
|
||||
set_real_ip_from 2606:4700::/32;
|
||||
set_real_ip_from 2803:f800::/32;
|
||||
set_real_ip_from 2405:b500::/32;
|
||||
set_real_ip_from 2405:8100::/32;
|
||||
set_real_ip_from 2a06:98c0::/29;
|
||||
set_real_ip_from 2c0f:f248::/32;
|
||||
|
||||
#real_ip_header CF-Connecting-IP;
|
||||
real_ip_header X-Forwarded-For;
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# NGINX Settings WEB
|
||||
|
||||
```
|
||||
sudo nano /etc/nginx/sites-available/sheep-service.com
|
||||
```
|
||||
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name sheep-service.com www.sheep-service.com;
|
||||
|
||||
error_log /home/rozenrod/webapps/log/sheep-service.com.error.log error;
|
||||
access_log /home/rozenrod/webapps/log/sheep-service.com.access.log;
|
||||
|
||||
root /home/rozenrod/webapps/sheep-service.com;
|
||||
|
||||
index index.html;
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
location ~ /\.git {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ /\.env {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:4000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
if ($request_method = 'PUT') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||
}
|
||||
if ($request_method = 'POST') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||
}
|
||||
if ($request_method = 'GET') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||
}
|
||||
if ($request_method = 'DELETE') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||
}
|
||||
}
|
||||
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:4001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:4002;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
if ($request_method = 'GET') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
sudo ln -s /etc/nginx/sites-available/sheep-service.com /etc/nginx/sites-enabled/sheep-service.com
|
||||
```
|
||||
|
||||
```
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
```
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
# Certbot Install
|
||||
|
||||
```
|
||||
sudo python3 -m venv /opt/certbot/
|
||||
```
|
||||
|
||||
```
|
||||
sudo /opt/certbot/bin/pip install --upgrade pip
|
||||
```
|
||||
|
||||
```
|
||||
sudo /opt/certbot/bin/pip install certbot certbot-nginx
|
||||
```
|
||||
|
||||
```
|
||||
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
|
||||
```
|
||||
|
||||
```
|
||||
sudo certbot --nginx -d sheep-service.com
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null
|
||||
```
|
||||
|
||||
```
|
||||
sudo /opt/certbot/bin/pip install --upgrade certbot certbot-nginx
|
||||
```
|
||||
15
api/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM node:20.18
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
RUN apt-get update && apt-get install -y chromium
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
CMD npm start
|
||||
16
api/app.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const routes = require('./routes/index');
|
||||
// const cors = require('cors');
|
||||
|
||||
const port = 4000;
|
||||
|
||||
// app.use(cors())
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
app.use(express.urlencoded({ limit: '50mb' }));
|
||||
|
||||
app.use('/api', routes);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`);
|
||||
});
|
||||
34
api/config/db.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const sqlite3 = require("sqlite3").verbose();
|
||||
const path = require('path');
|
||||
|
||||
const dbPath = process.env.DATABASE_PATH || '../';
|
||||
const db = new sqlite3.Database(path.join(dbPath, 'database.sqlite'));
|
||||
|
||||
// db.serialize(() => {
|
||||
// db.run(`
|
||||
// CREATE TABLE IF NOT EXISTS sheeps (
|
||||
// id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
// uuid TEXT UNIQUE
|
||||
// )
|
||||
// `);
|
||||
|
||||
// db.run(`
|
||||
// CREATE TABLE IF NOT EXISTS administrators (
|
||||
// sheep_id INTEGER PRIMARY KEY,
|
||||
// can_view_sheeps INTEGER DEFAULT 0,
|
||||
// FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
|
||||
// )
|
||||
// `);
|
||||
|
||||
// db.run(`
|
||||
// CREATE TABLE IF NOT EXISTS sessions (
|
||||
// session_id TEXT PRIMARY KEY,
|
||||
// sheep_id INTEGER,
|
||||
// role TEXT DEFAULT 'sheep',
|
||||
// expires_at INTEGER,
|
||||
// FOREIGN KEY (sheep_id) REFERENCES sheeps(id)
|
||||
// )
|
||||
// `);
|
||||
// });
|
||||
|
||||
module.exports = db;
|
||||
6
api/config/telegram.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const TelegramConfig = {
|
||||
token: "7855966674:AAEw9l_EF0GcpjrkSFzt0aLukEfJxBA2gcY",
|
||||
chatId: "224538769"
|
||||
}
|
||||
|
||||
module.exports = TelegramConfig;
|
||||
112
api/controllers/apartments.controller.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const ApartmentsService = require('../services/apartments.service');
|
||||
|
||||
class ApartmentsController {
|
||||
async getApartments(req, res) {
|
||||
const { entrance_id } = req.params;
|
||||
|
||||
if (entrance_id) {
|
||||
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) {
|
||||
let result = await ApartmentsService.getApartments(entrance_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: 'Entrance not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createApartments(req, res) {
|
||||
const { entrance_id } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
if (entrance_id && data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await ApartmentsService.createApartments(
|
||||
entrance_id,
|
||||
data
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create apartment.',
|
||||
});
|
||||
}
|
||||
} 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 updateApartments(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await ApartmentsService.updateApartments(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update history apartment.',
|
||||
});
|
||||
}
|
||||
} 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 deleteApartments(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await ApartmentsService.deleteApartments(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete history apartment.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The user does not have enough rights.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(401)
|
||||
.send({ message: 'Data not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ApartmentsController();
|
||||
22
api/controllers/auth.controller.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const AuthService = require('../services/auth.service');
|
||||
|
||||
class AuthController {
|
||||
async login(req, res) {
|
||||
if (req.sheepId && req.sheepRole) {
|
||||
const result = await AuthService.findUserByID(req.sheepId, req.sheepRole);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(404).send({
|
||||
message: 'Sheep not found.'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The sheep does not have enough rights.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AuthController();
|
||||
31
api/controllers/constructor.controller.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const ConstructorService = require('../services/constructor.service');
|
||||
|
||||
class ConstructorController {
|
||||
async createPack(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await ConstructorService.createPack(data);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create pack.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The user does not have enough rights.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(401)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ConstructorController();
|
||||
112
api/controllers/entrances.controller.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const EntrancesService = require('../services/entrances.service');
|
||||
|
||||
class EntrancesController {
|
||||
async getEntrances(req, res) {
|
||||
const { house_id } = req.params;
|
||||
|
||||
if (house_id) {
|
||||
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) {
|
||||
let result = await EntrancesService.getEntrances(house_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(404)
|
||||
.send({ message: 'House not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createEntrance(req, res) {
|
||||
const { house_id } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
if (house_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await EntrancesService.createEntrance(
|
||||
house_id,
|
||||
data
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create entrance.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The user does not have enough rights.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'House not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateEntrance(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await EntrancesService.updateEntrance(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update entrance.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The user does not have enough rights.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Data not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteEntrance(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await EntrancesService.deleteEntrance(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete entrance.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The user does not have enough rights.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Data not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new EntrancesController();
|
||||
29
api/controllers/generator.cards.controller.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const saveCards = require("../middleware/genCards");
|
||||
|
||||
class GeneratorCardsController {
|
||||
async getScreen(req, res) {
|
||||
const { lat, lng, type, wayId, zoom, id, address, number } = req.query;
|
||||
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await saveCards({ center: {lat:lat,lng:lng}, wayId: wayId, zoom: zoom ?? 18, type: type, number: id, address: `${address} ${number}` });
|
||||
|
||||
console.log(result);
|
||||
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Image creation error.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The user does not have enough rights.' });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new GeneratorCardsController();
|
||||
112
api/controllers/history.entrance.controller.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const HistoryEntranceService = require('../services/history.entrance.service');
|
||||
|
||||
class HistoryEntranceController {
|
||||
async getHistoryEntrance(req, res) {
|
||||
const { entrance_id } = req.params;
|
||||
|
||||
if (entrance_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryEntranceService.getHistoryEntrance(entrance_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(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createHistoryEntrance(req, res) {
|
||||
const { entrance_id } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
if (entrance_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryEntranceService.createHistoryEntrance(
|
||||
entrance_id,
|
||||
data
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create history entrance.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateHistoryEntrance(req, res) {
|
||||
const { entrance_id } = req.params;
|
||||
|
||||
if (entrance_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryEntranceService.updateHistoryEntrance(entrance_id);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update history entrance.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteHistoryEntrance(req, res) {
|
||||
const { entrance_id } = req.params;
|
||||
|
||||
if (entrance_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryEntranceService.deleteHistoryEntrance(entrance_id);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete history entrance.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HistoryEntranceController();
|
||||
113
api/controllers/history.homestead.controller.js
Normal file
@@ -0,0 +1,113 @@
|
||||
const HistoryHomesteadService = require('../services/history.homestead.service');
|
||||
|
||||
class HistoryHomesteadController {
|
||||
async getHistoryHomestead(req, res) {
|
||||
const { homestead_id } = req.params;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryHomesteadService.getHistoryHomestead(homestead_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(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createHistoryHomestead(req, res) {
|
||||
const { homestead_id } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryHomesteadService.createHistoryHomestead(
|
||||
homestead_id,
|
||||
data
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create history homestead.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateHistoryHomestead(req, res) {
|
||||
const { uuid } = req.query;
|
||||
const { homestead_id } = req.params;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryHomesteadService.updateHistoryHomestead(homestead_id);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update history homestead.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteHistoryHomestead(req, res) {
|
||||
const { homestead_id } = req.params;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HistoryHomesteadService.deleteHistoryHomestead(homestead_id);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete history homestead.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HistoryHomesteadController();
|
||||
143
api/controllers/homesteads.controller.js
Normal file
@@ -0,0 +1,143 @@
|
||||
const HomesteadsService = require('../services/homesteads.service');
|
||||
|
||||
class HomesteadsController {
|
||||
async getList(req, res) {
|
||||
const { mode } = req.query;
|
||||
|
||||
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) {
|
||||
let group_id = 0;
|
||||
let sheepName = false;
|
||||
|
||||
if (req.sheepRole == "administrator") {
|
||||
group_id = 0;
|
||||
} else if (req.sheepRole == "moderator") {
|
||||
group_id = req.group_id;
|
||||
}
|
||||
|
||||
if (mode == "sheep") {
|
||||
group_id = req.group_id;
|
||||
sheepName = req.sheepName;
|
||||
}
|
||||
|
||||
let result = await HomesteadsService.getList(group_id, sheepName);
|
||||
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 getHomestead(req, res) {
|
||||
const { homestead_id } = req.params;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) {
|
||||
let result = await HomesteadsService.getHomestead(homestead_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(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createHomestead(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (data) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await HomesteadsService.createHomestead(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create homestead.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateHomestead(req, res) {
|
||||
const { homestead_id } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HomesteadsService.updateHomestead(homestead_id, data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update homestead.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteHomestead(req, res) {
|
||||
const { homestead_id } = req.params;
|
||||
|
||||
if (homestead_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await HomesteadsService.deleteHomestead(homestead_id);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete homestead.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HomesteadsController();
|
||||
137
api/controllers/houses.controller.js
Normal file
@@ -0,0 +1,137 @@
|
||||
const HousesService = require('../services/houses.service');
|
||||
|
||||
class HousesController {
|
||||
async getList(req, res) {
|
||||
const { mode } = req.query;
|
||||
|
||||
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) {
|
||||
let group_id = 0;
|
||||
let sheepName = false;
|
||||
|
||||
if (req.sheepRole == "administrator") {
|
||||
group_id = 0;
|
||||
} else if (req.sheepRole == "moderator") {
|
||||
group_id = req.group_id;
|
||||
}
|
||||
|
||||
if (mode == "sheep") {
|
||||
group_id = req.group_id;
|
||||
sheepName = req.sheepName;
|
||||
}
|
||||
|
||||
let result = await HousesService.getList(group_id, sheepName);
|
||||
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 getHouse(req, res) {
|
||||
const { house_id } = req.params;
|
||||
|
||||
if (house_id) {
|
||||
if (req.sheepRole == "administrator" || (req.sheepRole == "moderator" && req.moderator.can_manager_territory) || req.can_view_territory) {
|
||||
let result = await HousesService.getHouse(house_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(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createHouse(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await HousesService.createHouse(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create house.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateHouse(req, res) {
|
||||
const { house_id } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
if (house_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_manager_territory) {
|
||||
let result = await HousesService.updateHouse(house_id, data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update house.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteHouse(req, res) {
|
||||
const { house_id } = req.params;
|
||||
|
||||
if (house_id) {
|
||||
if (req.sheepRole == "administrator" || req.moderator.can_add_territory) {
|
||||
let result = await HousesService.deleteHouse(house_id);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete house.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HousesController();
|
||||
24
api/controllers/rotation.controller.js
Normal file
@@ -0,0 +1,24 @@
|
||||
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();
|
||||
110
api/controllers/sheeps.controller.js
Normal file
@@ -0,0 +1,110 @@
|
||||
const SheepsService = require('../services/sheeps.service');
|
||||
|
||||
class SheepsController {
|
||||
async getSheep(req, res) {
|
||||
const { uuid } = req.query;
|
||||
|
||||
if (uuid) {
|
||||
if (req.sheepRole) {
|
||||
const result = await SheepsService.getSheep(uuid, req.sheepRole);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(404).send({
|
||||
message: 'Sheep not found.'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The sheep does not have enough rights.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(401)
|
||||
.send({ message: 'Sheeps not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async getList(req, res) {
|
||||
if (req.sheepRole) {
|
||||
const result = await SheepsService.getList(req.sheepRole);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'User not found.' });
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ message: 'Users not found.' });
|
||||
}
|
||||
}
|
||||
|
||||
async createSheep(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (req.sheepRole && (req.sheepRole == "administrator" || req.moderator.can_add_sheeps)) {
|
||||
let result = await SheepsService.createSheep(data);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable create sheep.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'The sheep does not have enough rights.' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateSheep(req, res) {
|
||||
const { uuid } = req.query;
|
||||
const data = req.body;
|
||||
console.log("data", data);
|
||||
|
||||
|
||||
if (req.sheepRole == "administrator") {
|
||||
let result = await SheepsService.updateSheep(data);
|
||||
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable update sheep.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'Sheep not foundThe sheep does not have enough rights.' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteSheep(req, res) {
|
||||
const data = req.body;
|
||||
|
||||
if (req.sheepRole == "administrator") {
|
||||
let result = await SheepsService.deleteSheep(data);
|
||||
if (result) {
|
||||
return res.status(200).send(result);
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
message: 'Unable delete sheep.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'Sheep not foundThe sheep does not have enough rights.' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new SheepsController();
|
||||
70
api/middleware/auth.js
Normal file
@@ -0,0 +1,70 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
const authenticate = (req, res, next) => {
|
||||
const uuid = req.headers["authorization"];
|
||||
if (!uuid) return res.status(401).json({ error: "Unauthorized" });
|
||||
|
||||
db.get(`
|
||||
SELECT sheeps.*, administrators.* FROM administrators JOIN sheeps ON sheeps.id = administrators.sheep_id WHERE administrators.uuid = ?`,
|
||||
[uuid],
|
||||
(err, administrator) => {
|
||||
if (administrator) {
|
||||
req.sheepId = administrator.sheep_id;
|
||||
req.sheepRole = 'administrator';
|
||||
req.group_id = administrator.group_id;
|
||||
req.sheepName = administrator.name;
|
||||
req.can_view_schedule = administrator.can_view_schedule;
|
||||
req.can_view_stand = administrator.can_view_stand;
|
||||
req.can_view_territory = administrator.can_view_territory;
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
db.get(`
|
||||
SELECT sheeps.*, moderators.* FROM moderators JOIN sheeps ON sheeps.id = moderators.sheep_id WHERE moderators.uuid = ?`,
|
||||
[uuid],
|
||||
(err, moderator) => {
|
||||
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) {
|
||||
req.sheepId = sheep.id;
|
||||
req.sheepRole = 'sheep';
|
||||
req.group_id = sheep.group_id;
|
||||
req.sheepName = sheep.name;
|
||||
req.can_view_schedule = sheep.can_view_schedule;
|
||||
req.can_view_stand = sheep.can_view_stand;
|
||||
req.can_view_territory = sheep.can_view_territory;
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
return res.status(401).json({ error: "UUID not found" });
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = authenticate;
|
||||
77
api/middleware/genCards.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const puppeteer = require('puppeteer');
|
||||
const sharp = require('sharp');
|
||||
|
||||
const DIR = process.env.CARDS_PATH || '../cards';
|
||||
|
||||
async function genCards({ center, type, wayId, zoom, number, address }) {
|
||||
const browser = await puppeteer.launch({
|
||||
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/chromium-browser',
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
|
||||
|
||||
let latlng = center && center.lat && center.lng ? `lat=${center.lat}&lng=${center.lng}&` : '';
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1144, height: (750 + 140) })
|
||||
await page.goto(`https://sheep-service.com/screenshot.html?${latlng}type=${type}&wayId=${wayId}&zoom=${zoom}&address=${address}&number=${number}`, { timeout: 0 });
|
||||
await page.waitForSelector('.leaflet-tile-loaded', { timeout: 30000 });
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
let name = () => {
|
||||
if (type == "house") return `T${number}.png`
|
||||
else if (type == "entrance") return `E${number}.png`
|
||||
else if (type == "homestead") return `H${number}.png`
|
||||
return `${Date.now()}.png`
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(DIR, 'cache'))) {
|
||||
fs.mkdirSync(path.join(DIR, 'cache'), { recursive: true })
|
||||
}
|
||||
|
||||
await page.screenshot({ path: path.join(DIR, "cache", name()) });
|
||||
|
||||
await browser.close();
|
||||
|
||||
return name();
|
||||
}
|
||||
|
||||
async function saveCards({ center, type, wayId, zoom, number, address }) {
|
||||
let name = await genCards({ center, type, wayId, zoom, number, address });
|
||||
|
||||
if (!fs.existsSync(path.join(DIR, type))) {
|
||||
fs.mkdirSync(path.join(DIR, type), { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const metadata = await sharp(path.join(DIR, 'cache', name)).metadata();
|
||||
const width = metadata.width;
|
||||
const height = metadata.height;
|
||||
|
||||
if (width > 20 && height > 140) {
|
||||
const outputPath = path.join(DIR, type, name.replace(path.extname(name), '.webp'));
|
||||
|
||||
await sharp(path.join(DIR, 'cache', name))
|
||||
.extract({
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: width,
|
||||
height: height - 140
|
||||
})
|
||||
.webp()
|
||||
.toFile(outputPath);
|
||||
|
||||
return fs.existsSync(outputPath);
|
||||
} else {
|
||||
console.error('Изображение слишком маленькое для обрезки!');
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Ошибка при обработке изображения:', err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = saveCards;
|
||||
5377
api/package-lock.json
generated
Normal file
19
api/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "API Sheep Service",
|
||||
"version": "1.0.0",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node app.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.0",
|
||||
"node-telegram-bot-api": "^0.66.0",
|
||||
"sqlite3": "^5.1.7",
|
||||
"puppeteer": "^24.4.0",
|
||||
"sharp": "^0.33.5"
|
||||
}
|
||||
}
|
||||
13
api/routes/apartments.routes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router({ mergeParams: true });
|
||||
const ApartmentsController = require('../controllers/apartments.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, ApartmentsController.getApartments)
|
||||
.post(authenticate, ApartmentsController.createApartments)
|
||||
.put(authenticate, ApartmentsController.updateApartments)
|
||||
.delete(authenticate, ApartmentsController.deleteApartments);
|
||||
|
||||
module.exports = router;
|
||||
10
api/routes/auth.routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const AuthController = require('../controllers/auth.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, AuthController.login);
|
||||
|
||||
module.exports = router;
|
||||
10
api/routes/constructor.routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const ConstructorController = require('../controllers/constructor.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.post(authenticate, ConstructorController.createPack)
|
||||
|
||||
module.exports = router;
|
||||
13
api/routes/entrances.routes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router({ mergeParams: true });
|
||||
const EntranceController = require('../controllers/entrances.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, EntranceController.getEntrances)
|
||||
.post(authenticate, EntranceController.createEntrance)
|
||||
.put(authenticate, EntranceController.updateEntrance)
|
||||
.delete(authenticate, EntranceController.deleteEntrance);
|
||||
|
||||
module.exports = router;
|
||||
10
api/routes/generator.cards.routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const GeneratorCardsController = require('../controllers/generator.cards.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, GeneratorCardsController.getScreen);
|
||||
|
||||
module.exports = router;
|
||||
13
api/routes/history.entrance.routes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router({ mergeParams: true });
|
||||
const HistoryEntranceController = require('../controllers/history.entrance.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, HistoryEntranceController.getHistoryEntrance)
|
||||
.post(authenticate, HistoryEntranceController.createHistoryEntrance)
|
||||
.put(authenticate, HistoryEntranceController.updateHistoryEntrance)
|
||||
.delete(authenticate, HistoryEntranceController.deleteHistoryEntrance);
|
||||
|
||||
module.exports = router;
|
||||
13
api/routes/history.homestead.routes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router({ mergeParams: true });
|
||||
const HistoryHomesteadController = require('../controllers/history.homestead.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, HistoryHomesteadController.getHistoryHomestead)
|
||||
.post(authenticate, HistoryHomesteadController.createHistoryHomestead)
|
||||
.put(authenticate, HistoryHomesteadController.updateHistoryHomestead)
|
||||
.delete(authenticate, HistoryHomesteadController.deleteHistoryHomestead);
|
||||
|
||||
module.exports = router;
|
||||
20
api/routes/homesteads.routes.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const HomesteadController = require('../controllers/homesteads.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/list')
|
||||
.get(authenticate, HomesteadController.getList)
|
||||
|
||||
router
|
||||
.route('/:homestead_id')
|
||||
.get(authenticate, HomesteadController.getHomestead)
|
||||
.put(authenticate, HomesteadController.updateHomestead)
|
||||
.delete(authenticate, HomesteadController.deleteHomestead);
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.post(authenticate, HomesteadController.createHomestead);
|
||||
|
||||
module.exports = router;
|
||||
20
api/routes/houses.routes.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const HousesController = require('../controllers/houses.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/list')
|
||||
.get(authenticate, HousesController.getList)
|
||||
|
||||
router
|
||||
.route('/:house_id')
|
||||
.get(authenticate, HousesController.getHouse)
|
||||
.put(authenticate, HousesController.updateHouse)
|
||||
.delete(authenticate, HousesController.deleteHouse);
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.post(authenticate, HousesController.createHouse);
|
||||
|
||||
module.exports = router;
|
||||
30
api/routes/index.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const authRoutes = require('./auth.routes');
|
||||
const sheepsRoutes = require('./sheeps.routes');
|
||||
const constructorRoutes = require('./constructor.routes');
|
||||
const housesRoutes = require('./houses.routes');
|
||||
const homesteadsRoutes = require('./homesteads.routes');
|
||||
const entrancesRoutes = require('./entrances.routes');
|
||||
const apartmentsRoutes = require('./apartments.routes');
|
||||
const historyEntranceRoutes = require('./history.entrance.routes');
|
||||
const historyHomesteadRoutes = require('./history.homestead.routes');
|
||||
const rotationRoutes = require('./rotation.routes');
|
||||
const generatorCardsRoutes = require('./generator.cards.routes');
|
||||
|
||||
router.use('/auth', authRoutes);
|
||||
router.use('/sheeps?', sheepsRoutes);
|
||||
router.use('/constructor', constructorRoutes);
|
||||
router.use('/houses?', housesRoutes);
|
||||
router.use('/homesteads?', homesteadsRoutes);
|
||||
router.use('/house/:house_id/entrances', entrancesRoutes);
|
||||
router.use('/apartments?/:entrance_id', apartmentsRoutes);
|
||||
router.use('/history/entrance/:entrance_id', historyEntranceRoutes);
|
||||
router.use('/history/homestead/:homestead_id', historyHomesteadRoutes);
|
||||
|
||||
router.use('/rotation', rotationRoutes);
|
||||
|
||||
router.use('/generator/cards', generatorCardsRoutes);
|
||||
|
||||
module.exports = router;
|
||||
10
api/routes/rotation.routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
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;
|
||||
18
api/routes/sheeps.routes.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const SheepsController = require('../controllers/sheeps.controller');
|
||||
const authenticate = require("../middleware/auth");
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(authenticate, SheepsController.getSheep)
|
||||
.post(authenticate, SheepsController.createSheep)
|
||||
.put(authenticate, SheepsController.updateSheep)
|
||||
.delete(authenticate, SheepsController.deleteSheep);
|
||||
|
||||
|
||||
router
|
||||
.route('/list')
|
||||
.get(authenticate, SheepsController.getList);
|
||||
|
||||
module.exports = router;
|
||||
102
api/services/apartments.service.js
Normal file
@@ -0,0 +1,102 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
|
||||
class ApartmentsService {
|
||||
getApartments(entrance_id) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
apartments
|
||||
WHERE
|
||||
entrance_id = '${entrance_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),
|
||||
"entrance_id": Number(row.entrance_id),
|
||||
"apartment_number": row.apartment_number,
|
||||
"title": row.title,
|
||||
"floors_number": Number(row.floors_number),
|
||||
"status": Number(row.status),
|
||||
"description": row.description,
|
||||
"updated_at": Number(row.updated_at)
|
||||
}
|
||||
})
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createApartments(entrance_id, data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = 'INSERT INTO apartments(entrance_id, apartment_number, title, floors_number) VALUES (?, ?, ?, ?)';
|
||||
|
||||
db.run(sql, [
|
||||
entrance_id,
|
||||
Number(data.apartment_number),
|
||||
data.title,
|
||||
data.floors_number
|
||||
], 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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateApartments(data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = 'UPDATE apartments SET title = ?, status = ?, description = ?, updated_at = ? WHERE id = ?';
|
||||
db.run(sql, [
|
||||
data.title,
|
||||
data.status,
|
||||
data.description,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
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", "id": data.id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteApartments(data) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM apartments WHERE id = ?', [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", "id": data.id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ApartmentsService();
|
||||
84
api/services/auth.service.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
class AuthService {
|
||||
findUserByID(id, sheepRole) {
|
||||
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,
|
||||
moderators.id AS moderators_id,
|
||||
moderators.uuid AS moderators_uuid,
|
||||
moderators.can_add_sheeps,
|
||||
moderators.can_add_territory,
|
||||
moderators.can_manager_territory,
|
||||
moderators.can_add_stand,
|
||||
moderators.can_manager_stand,
|
||||
moderators.can_add_schedule
|
||||
FROM
|
||||
sheeps
|
||||
LEFT JOIN
|
||||
groups ON groups.group_number = sheeps.group_id
|
||||
LEFT JOIN
|
||||
administrators ON administrators.sheep_id = sheeps.id
|
||||
LEFT JOIN
|
||||
moderators ON moderators.sheep_id = sheeps.id
|
||||
WHERE
|
||||
sheeps.id = ?
|
||||
LIMIT 1;
|
||||
`
|
||||
db.get(sql, [id], (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 ? 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") {
|
||||
if (sheep.administrators_id) {
|
||||
data.administrator.uuid = sheep.administrators_uuid;
|
||||
}
|
||||
}
|
||||
if (sheepRole == "moderator") {
|
||||
if (sheep.moderators_id) {
|
||||
data.moderator.uuid = sheep.moderators_uuid;
|
||||
}
|
||||
}
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AuthService();
|
||||
240
api/services/constructor.service.js
Normal file
@@ -0,0 +1,240 @@
|
||||
const db = require("../config/db");
|
||||
const saveCards = require("../middleware/genCards");
|
||||
|
||||
class ConstructorService {
|
||||
// createPack(data) {
|
||||
// return new Promise((res, rej) => {
|
||||
// let sql = `
|
||||
// INSERT INTO
|
||||
// house(
|
||||
// group_id,
|
||||
// title,
|
||||
// number,
|
||||
// points,
|
||||
// points_number,
|
||||
// geo,
|
||||
// osm_id,
|
||||
// settlement,
|
||||
// created_at
|
||||
// )
|
||||
// VALUES
|
||||
// (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
// `;
|
||||
|
||||
// db.run(sql, [
|
||||
// Number(data.house.group_id),
|
||||
// data.house.title,
|
||||
// data.house.number,
|
||||
// JSON.stringify(data.house.points),
|
||||
// JSON.stringify(data.house.points_number),
|
||||
// JSON.stringify(data.house.geo),
|
||||
// JSON.stringify(data.house.osm_id),
|
||||
// data.house.settlement,
|
||||
// Math.floor(Date.now())
|
||||
// ], function (err) {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// return res(false);
|
||||
// } else if (this.changes === 0) {
|
||||
// return res(false);
|
||||
// } else {
|
||||
// const houseId = this.lastID;
|
||||
|
||||
// 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,
|
||||
// floors_number,
|
||||
// updated_at
|
||||
// )
|
||||
// VALUES
|
||||
// (?, ?, ?, ?)`);
|
||||
|
||||
// data.entrance.forEach((e, index) => {
|
||||
// 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;
|
||||
|
||||
// if (data.apartments[e.editor_id]) {
|
||||
// data.apartments[e.editor_id].forEach(apartment => {
|
||||
// apartmentStmt.run(
|
||||
// entranceId,
|
||||
// apartment.apartment_number,
|
||||
// apartment.floors_number,
|
||||
// Math.floor(Date.now())
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
|
||||
// entranceStmt.finalize();
|
||||
// apartmentStmt.finalize();
|
||||
|
||||
// // res({ "status": "ok", "id": houseId });
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
createPack(data) {
|
||||
return new Promise((res, rej) => {
|
||||
|
||||
if (data.type == "house") {
|
||||
const sql = `
|
||||
INSERT INTO house (
|
||||
group_id, title, number, points, points_number, geo, osm_id, settlement, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(sql, [
|
||||
Number(data.group_id),
|
||||
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") {
|
||||
let sql = `
|
||||
INSERT INTO
|
||||
homestead(
|
||||
group_id,
|
||||
title,
|
||||
number,
|
||||
points,
|
||||
point_icons,
|
||||
geo,
|
||||
osm_id,
|
||||
settlement,
|
||||
created_at
|
||||
)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
db.run(sql, [
|
||||
Number(data.group_id),
|
||||
data.title,
|
||||
data.number,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.point_icons),
|
||||
JSON.stringify(data.geo),
|
||||
JSON.stringify(data.osm_id),
|
||||
data.settlement,
|
||||
Math.floor(new Date(Date.now()).getTime())
|
||||
], function (err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (this.changes === 0) {
|
||||
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 });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return res(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ConstructorService();
|
||||
158
api/services/entrances.service.js
Normal file
@@ -0,0 +1,158 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
class EntrancesService {
|
||||
getEntrances(house_id) {
|
||||
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
|
||||
FROM
|
||||
entrance
|
||||
WHERE
|
||||
entrance.house_id = '${house_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),
|
||||
"house_id": Number(row.house_id),
|
||||
"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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createEntrance(house_id, data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
INSERT INTO
|
||||
entrance(
|
||||
house_id,
|
||||
entrance_number,
|
||||
title,
|
||||
points,
|
||||
points_number,
|
||||
floors_quantity,
|
||||
apartments_quantity,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
db.run(sql, [
|
||||
house_id,
|
||||
Number(data.entrance_number),
|
||||
data.title,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.points_number),
|
||||
data.floors_quantity,
|
||||
data.apartments_quantity,
|
||||
data.description,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
], 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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateEntrance(data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
UPDATE
|
||||
entrance
|
||||
SET
|
||||
title = ?,
|
||||
points = ?,
|
||||
points_number = ?,
|
||||
floors_quantity = ?,
|
||||
apartments_quantity = ?,
|
||||
description = ?,
|
||||
updated_at = ?
|
||||
WHERE
|
||||
id = ?
|
||||
`;
|
||||
db.run(sql, [
|
||||
data.title,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.points_number),
|
||||
data.floors_quantity,
|
||||
data.apartments_quantity,
|
||||
data.description,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
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", "id": data.id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteEntrance(data) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM entrance WHERE id = ?', [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", "id": data.id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new EntrancesService();
|
||||
105
api/services/history.entrance.service.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
class HistoryEntranceService {
|
||||
getHistoryEntrance(entrance_id) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
entrance_history
|
||||
WHERE
|
||||
entrance_history.entrance_id = '${entrance_id}'
|
||||
ORDER BY
|
||||
entrance_history.date_start
|
||||
`;
|
||||
|
||||
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),
|
||||
"entrance_id": Number(row.entrance_id),
|
||||
"name": row.name,
|
||||
"group_id": Number(row.group_id),
|
||||
"sheep_id": Number(row.sheep_id),
|
||||
"working": Number(row.working) == 0 ? false : true,
|
||||
"date": {
|
||||
"start": Number(row.date_start),
|
||||
"end": row.date_end ? Number(row.date_end) : null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createHistoryEntrance(entrance_id, data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = 'INSERT INTO entrance_history(entrance_id, name, date_start, group_id, sheep_id, working) VALUES (?, ?, ?, ?, ?, ?)';
|
||||
|
||||
db.run(sql, [
|
||||
entrance_id,
|
||||
data.name,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
Number(data.group_id),
|
||||
Number(data.sheep_id),
|
||||
1
|
||||
], function(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (this.changes === 0) {
|
||||
return res(false);
|
||||
} else {
|
||||
res({ "create": "ok", "id": this.lastID });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateHistoryEntrance(entrance_id) {
|
||||
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) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM entrance_history WHERE id = ?', [Number(entrance_id)], function(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (this.changes === 0) {
|
||||
return res(false);
|
||||
} else {
|
||||
res({ "delete": "ok", "id": entrance_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HistoryEntranceService();
|
||||
103
api/services/history.homestead.service.js
Normal file
@@ -0,0 +1,103 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
class HistoryHomesteadService {
|
||||
getHistoryHomestead(homestead_id) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
homestead_history
|
||||
WHERE
|
||||
homestead_history.homestead_id = '${homestead_id}'
|
||||
ORDER BY
|
||||
homestead_history.date_start
|
||||
`;
|
||||
|
||||
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),
|
||||
"name": row.name,
|
||||
"group_id": Number(row.group_id),
|
||||
"sheep_id": Number(row.sheep_id),
|
||||
"working": Number(row.working) == 0 ? false : true,
|
||||
"date": {
|
||||
"start": Number(row.date_start),
|
||||
"end": row.date_end ? Number(row.date_end) : null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createHistoryHomestead(homestead_id, data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = 'INSERT INTO homestead_history(homestead_id, name, date_start, group_id, sheep_id, working) VALUES (?, ?, ?, ?, ?, ?)';
|
||||
|
||||
db.run(sql, [
|
||||
homestead_id,
|
||||
data.name,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
Number(data.group_id),
|
||||
Number(data.sheep_id),
|
||||
1
|
||||
], function(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (this.changes === 0) {
|
||||
return res(false);
|
||||
} else {
|
||||
res({ "create": "ok", "id": this.lastID });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateHistoryHomestead(homestead_id) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = 'UPDATE homestead_history SET date_end = ?, working = ? WHERE id = ?';
|
||||
db.run(sql, [
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
0,
|
||||
Number(homestead_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": homestead_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteHistoryHomestead(data) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM homestead_history WHERE id = ?', [Number(homestead_id)], function(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (this.changes === 0) {
|
||||
return res(false);
|
||||
} else {
|
||||
res({ "delete": "ok", "id": homestead_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HistoryHomesteadService();
|
||||
260
api/services/homesteads.service.js
Normal file
@@ -0,0 +1,260 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
class HomesteadsService {
|
||||
getList(group, sheepName) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
homestead.*,
|
||||
COALESCE((SELECT homestead_history.working FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1), 0) AS working,
|
||||
(SELECT homestead_history.name FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_name,
|
||||
(SELECT homestead_history.group_id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_group_id,
|
||||
(SELECT homestead_history.sheep_id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_sheep_id,
|
||||
(SELECT homestead_history.id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_id,
|
||||
(SELECT homestead_history.date_start FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_date_start,
|
||||
(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
|
||||
homestead
|
||||
`;
|
||||
|
||||
if (group != "0" && !sheepName) {
|
||||
sql = `
|
||||
SELECT
|
||||
homestead.*,
|
||||
COALESCE((SELECT homestead_history.working FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1), 0) AS working,
|
||||
(SELECT homestead_history.name FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_name,
|
||||
(SELECT homestead_history.group_id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_group_id,
|
||||
(SELECT homestead_history.sheep_id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_sheep_id,
|
||||
(SELECT homestead_history.id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_id,
|
||||
(SELECT homestead_history.date_start FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_date_start,
|
||||
(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
|
||||
homestead
|
||||
WHERE
|
||||
group_id == '${group}'
|
||||
`;
|
||||
}
|
||||
|
||||
if (sheepName) {
|
||||
sql = `
|
||||
SELECT
|
||||
homestead.*,
|
||||
homestead_history.homestead_id,
|
||||
homestead_history.name AS homestead_history_name,
|
||||
homestead_history.group_id AS homestead_history_group_id,
|
||||
homestead_history.sheep_id AS homestead_history_sheep_id,
|
||||
homestead_history.id AS homestead_history_id,
|
||||
homestead_history.date_start AS homestead_history_date_start,
|
||||
homestead_history.date_end AS homestead_history_date_end
|
||||
FROM
|
||||
homestead
|
||||
JOIN
|
||||
homestead_history
|
||||
ON
|
||||
homestead.id = homestead_history.homestead_id
|
||||
WHERE
|
||||
homestead.group_id = '${group}'
|
||||
AND
|
||||
homestead_history.working = 1
|
||||
AND
|
||||
homestead_history.name IN ('Групова', '${sheepName}');
|
||||
`;
|
||||
}
|
||||
|
||||
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),
|
||||
"group_id": Number(row.group_id),
|
||||
"title": row.title,
|
||||
"number": row.number,
|
||||
"points": JSON.parse(row.points),
|
||||
"point_icons": JSON.parse(row.point_icons),
|
||||
"geo": JSON.parse(row.geo),
|
||||
"osm_id": JSON.parse(row.osm_id),
|
||||
"settlement": row.settlement,
|
||||
"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.homestead_history_id ? Number(row.homestead_history_id) : null,
|
||||
"name": row.homestead_history_name,
|
||||
"group_id": row.homestead_history_group_id ? Number(row.homestead_history_group_id) : null,
|
||||
"sheep_id": row.entrance_history_sheep_id ? Number(row.entrance_history_sheep_id) : null,
|
||||
"date": {
|
||||
"start": row.homestead_history_date_start ? Number(row.homestead_history_date_start) : null,
|
||||
"end": row.homestead_history_date_end ? Number(row.homestead_history_date_end) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getHomestead(homestead_id) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
homestead.*,
|
||||
COALESCE((SELECT homestead_history.working FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1), 0) AS working,
|
||||
(SELECT homestead_history.name FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_name,
|
||||
(SELECT homestead_history.group_id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_group_id,
|
||||
(SELECT homestead_history.id FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_id,
|
||||
(SELECT homestead_history.date_start FROM homestead_history WHERE homestead_history.homestead_id = homestead.id ORDER BY homestead_history.date_start DESC LIMIT 1) AS homestead_history_date_start,
|
||||
(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
|
||||
homestead
|
||||
WHERE
|
||||
homestead.id = '${homestead_id}'
|
||||
`;
|
||||
|
||||
db.get(sql, (err, row) => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (!row) {
|
||||
console.log({ "error": "house not found" });
|
||||
return res(false);
|
||||
} else {
|
||||
let data = {
|
||||
"id": Number(row.id),
|
||||
"group_id": Number(row.group_id),
|
||||
"title": row.title,
|
||||
"number": row.number,
|
||||
"points": JSON.parse(row.points),
|
||||
"point_icons": JSON.parse(row.point_icons),
|
||||
"geo": JSON.parse(row.geo),
|
||||
"osm_id": JSON.parse(row.osm_id),
|
||||
"settlement": row.settlement,
|
||||
"description": row.description,
|
||||
"updated_at": Number(row.updated_at),
|
||||
"working": Number(row.working) == 0 ? false : true,
|
||||
"history": {
|
||||
"id": row.homestead_history_id ? Number(row.homestead_history_id) : null,
|
||||
"name": row.homestead_history_name,
|
||||
"group_id": row.homestead_history_group_id ? Number(row.homestead_history_group_id) : null,
|
||||
"date": {
|
||||
"start": row.homestead_history_date_start ? Number(row.homestead_history_date_start) : null,
|
||||
"end": row.homestead_history_date_end ? Number(row.homestead_history_date_end) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createHomestead(data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
INSERT INTO
|
||||
homestead(
|
||||
group_id,
|
||||
title,
|
||||
number,
|
||||
points,
|
||||
point_icons,
|
||||
geo,
|
||||
osm_id,
|
||||
settlement,
|
||||
created_at
|
||||
)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
db.run(sql, [
|
||||
Number(data.group_id),
|
||||
data.title,
|
||||
data.number,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.point_icons),
|
||||
JSON.stringify(data.geo),
|
||||
JSON.stringify(data.osm_id),
|
||||
data.settlement,
|
||||
Math.floor(new Date(Date.now()).getTime())
|
||||
], 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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateHomestead(homestead_id, data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
UPDATE
|
||||
homestead
|
||||
SET
|
||||
group_id = ?,
|
||||
title = ?,
|
||||
number = ?,
|
||||
points = ?,
|
||||
point_icons = ?,
|
||||
geo = ?,
|
||||
osm_id = ?,
|
||||
settlement = ?,
|
||||
description = ?,
|
||||
updated_at = ?
|
||||
WHERE
|
||||
id = ?
|
||||
`;
|
||||
db.run(sql, [
|
||||
Number(data.group_id),
|
||||
data.title,
|
||||
data.number,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.point_icons),
|
||||
JSON.stringify(data.geo),
|
||||
JSON.stringify(data.osm_id),
|
||||
data.settlement,
|
||||
data.description,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
homestead_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": homestead_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteHomestead(homestead_id) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM homestead WHERE id = ?', [homestead_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": homestead_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HomesteadsService();
|
||||
233
api/services/houses.service.js
Normal file
@@ -0,0 +1,233 @@
|
||||
const db = require("../config/db");
|
||||
|
||||
class HousesService {
|
||||
getList(group, sheepName) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
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(*) FROM entrance WHERE entrance.house_id = house.id) AS entrance_quantity
|
||||
FROM
|
||||
house
|
||||
`;
|
||||
|
||||
if (group != "0" && !sheepName) {
|
||||
sql = `
|
||||
SELECT
|
||||
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(*) FROM entrance WHERE entrance.house_id = house.id) AS entrance_quantity
|
||||
FROM
|
||||
house
|
||||
WHERE
|
||||
group_id == '${group}'
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
if (sheepName) {
|
||||
sql = `
|
||||
SELECT DISTINCT
|
||||
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(*) FROM entrance WHERE entrance.house_id = house.id) AS entrance_quantity
|
||||
FROM
|
||||
house
|
||||
JOIN
|
||||
entrance ON entrance.house_id = house.id
|
||||
JOIN
|
||||
entrance_history ON entrance_history.entrance_id = entrance.id
|
||||
WHERE
|
||||
house.group_id = '${group}'
|
||||
AND
|
||||
entrance_history.working = 1
|
||||
AND
|
||||
entrance_history.name IN ('Групова', '${sheepName}');
|
||||
`;
|
||||
}
|
||||
|
||||
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),
|
||||
"group_id": Number(row.group_id),
|
||||
"title": row.title,
|
||||
"number": row.number,
|
||||
"points": JSON.parse(row.points),
|
||||
"points_number": JSON.parse(row.points_number),
|
||||
"geo": JSON.parse(row.geo),
|
||||
"osm_id": JSON.parse(row.osm_id),
|
||||
"settlement": row.settlement,
|
||||
"description": row.description,
|
||||
"created_at": Number(row.created_at),
|
||||
"updated_at": Number(row.updated_at),
|
||||
"entrance": {
|
||||
"quantity": Number(row.entrance_quantity),
|
||||
"working": Number(row.working)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getHouse(house_id) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
SELECT
|
||||
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(*) FROM entrance WHERE entrance.house_id = house.id) AS entrance_quantity
|
||||
FROM
|
||||
house
|
||||
WHERE
|
||||
house.id = '${house_id}'
|
||||
`;
|
||||
|
||||
db.get(sql, (err, row) => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (!row) {
|
||||
console.log({ "error": "house not found" });
|
||||
return res(false);
|
||||
} else {
|
||||
let data = {
|
||||
"id": Number(row.id),
|
||||
"group_id": Number(row.group_id),
|
||||
"title": row.title,
|
||||
"number": row.number,
|
||||
"points": JSON.parse(row.points),
|
||||
"points_number": JSON.parse(row.points_number),
|
||||
"geo": JSON.parse(row.geo),
|
||||
"osm_id": JSON.parse(row.osm_id),
|
||||
"settlement": row.settlement,
|
||||
"description": row.description,
|
||||
"updated_at": Number(row.updated_at),
|
||||
"entrance": {
|
||||
"quantity": Number(row.entrance_quantity),
|
||||
"working": Number(row.working) == 0 ? false : true
|
||||
}
|
||||
}
|
||||
|
||||
res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createHouse(data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
INSERT INTO
|
||||
house(
|
||||
group_id,
|
||||
title,
|
||||
number,
|
||||
points,
|
||||
points_number,
|
||||
geo,
|
||||
osm_id,
|
||||
settlement,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
db.run(sql, [
|
||||
Number(data.group_id),
|
||||
data.title,
|
||||
data.number,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.points_number),
|
||||
JSON.stringify(data.geo),
|
||||
JSON.stringify(data.osm_id),
|
||||
data.settlement,
|
||||
data.description,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
], 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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateHouse(house_id, data) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = `
|
||||
UPDATE
|
||||
house
|
||||
SET
|
||||
group_id = ?,
|
||||
title = ?,
|
||||
number = ?,
|
||||
points = ?,
|
||||
points_number = ?,
|
||||
geo = ?,
|
||||
osm_id = ?,
|
||||
settlement = ?,
|
||||
description = ?,
|
||||
updated_at = ?
|
||||
WHERE
|
||||
id = ?
|
||||
`;
|
||||
db.run(sql, [
|
||||
Number(data.group_id),
|
||||
data.title,
|
||||
data.number,
|
||||
JSON.stringify(data.points),
|
||||
JSON.stringify(data.points_number),
|
||||
JSON.stringify(data.geo),
|
||||
JSON.stringify(data.osm_id),
|
||||
data.settlement,
|
||||
data.description,
|
||||
Math.floor(new Date(Date.now()).getTime()),
|
||||
house_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": house_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteHouse(house_id) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM house WHERE id = ?', [house_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": house_id });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new HousesService();
|
||||
106
api/services/rotation.service.js
Normal file
@@ -0,0 +1,106 @@
|
||||
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();
|
||||
396
api/services/sheeps.service.js
Normal file
@@ -0,0 +1,396 @@
|
||||
const crypto = require('crypto');
|
||||
const db = require("../config/db");
|
||||
|
||||
class SheepService {
|
||||
getSheep(uuid, sheepRole) {
|
||||
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,
|
||||
moderators.id AS moderators_id,
|
||||
moderators.uuid AS moderators_uuid,
|
||||
moderators.can_add_sheeps,
|
||||
moderators.can_add_territory,
|
||||
moderators.can_manager_territory,
|
||||
moderators.can_add_stand,
|
||||
moderators.can_manager_stand,
|
||||
moderators.can_add_schedule
|
||||
FROM
|
||||
sheeps
|
||||
LEFT JOIN
|
||||
groups ON groups.group_number = sheeps.group_id
|
||||
LEFT JOIN
|
||||
administrators ON administrators.sheep_id = sheeps.id
|
||||
LEFT JOIN
|
||||
moderators ON moderators.sheep_id = sheeps.id
|
||||
WHERE
|
||||
sheeps.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 ? 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") {
|
||||
if (sheep.administrators_id) {
|
||||
data.administrator.uuid = sheep.administrators_uuid;
|
||||
}
|
||||
if (sheep.moderators_id) {
|
||||
data.moderator.uuid = sheep.moderators_uuid;
|
||||
}
|
||||
}
|
||||
|
||||
return res(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getList(sheepRole) {
|
||||
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,
|
||||
moderators.id AS moderators_id,
|
||||
moderators.uuid AS moderators_uuid,
|
||||
moderators.can_add_sheeps,
|
||||
moderators.can_add_territory,
|
||||
moderators.can_manager_territory,
|
||||
moderators.can_add_stand,
|
||||
moderators.can_manager_stand,
|
||||
moderators.can_add_schedule
|
||||
FROM
|
||||
sheeps
|
||||
LEFT JOIN
|
||||
groups ON groups.group_number = sheeps.group_id
|
||||
LEFT JOIN
|
||||
administrators ON administrators.sheep_id = sheeps.id
|
||||
LEFT JOIN
|
||||
moderators ON moderators.sheep_id = sheeps.id
|
||||
ORDER BY
|
||||
id
|
||||
`;
|
||||
|
||||
db.all(sql, (err, sheeps) => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
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") {
|
||||
if (sheep.administrators_id) {
|
||||
data.administrator.uuid = sheep.administrators_uuid;
|
||||
}
|
||||
if (sheep.moderators_id) {
|
||||
data.moderator.uuid = sheep.moderators_uuid;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
})
|
||||
|
||||
return 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) {
|
||||
return new Promise((res, rej) => {
|
||||
let sql = 'INSERT INTO sheeps(name, group_id, appointment, uuid) VALUES (?, ?, ?, ?)';
|
||||
|
||||
let uuid = crypto.randomUUID();
|
||||
|
||||
db.run(sql, [
|
||||
data.name,
|
||||
Number(data.group_id),
|
||||
data.appointment,
|
||||
uuid
|
||||
], 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, "uuid": uuid });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
updateSheep(data) {
|
||||
return new Promise(async (res, rej) => {
|
||||
try {
|
||||
let sql = `
|
||||
UPDATE sheeps
|
||||
SET name = ?, group_id = ?, appointment = ?,
|
||||
can_view_stand = ?, can_view_schedule = ?, can_view_territory = ?
|
||||
WHERE uuid = ?
|
||||
`;
|
||||
|
||||
await db.run(sql, [
|
||||
data.name,
|
||||
Number(data.group_id),
|
||||
data.appointment,
|
||||
data.can_view_stand ? 1 : 0,
|
||||
data.can_view_schedule ? 1 : 0,
|
||||
data.can_view_territory ? 1 : 0,
|
||||
data.uuid
|
||||
]);
|
||||
|
||||
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" });
|
||||
}
|
||||
}
|
||||
|
||||
res({ status: "ok", id: data.id });
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
rej(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
deleteSheep(data) {
|
||||
return new Promise((res, rej) => {
|
||||
db.run('DELETE FROM sheeps WHERE uuid = ?', [data.uuid], function (err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
return res(false);
|
||||
} else if (this.changes === 0) {
|
||||
return res(false);
|
||||
} else {
|
||||
res({ "dellete": "ok" });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new SheepService();
|
||||
186
dock/Sheep-Service.dbml
Normal file
@@ -0,0 +1,186 @@
|
||||
// Use DBML to define your database structure
|
||||
// Docs: https://dbml.dbdiagram.io/docs
|
||||
|
||||
Table sheeps [note: 'Таблиця вісників'] {
|
||||
id integer [primary key]
|
||||
group_id integer [note: 'ID групи']
|
||||
name text [note: 'Імʼя вісника']
|
||||
icon text [note: 'Піктограмка вісника']
|
||||
uuid text [note: 'Код доступа']
|
||||
appointment text [default: 'lamb', note: 'Вид призначення']
|
||||
can_view_stand integer [default: 0, note: 'Доступ до перегляду графіку стендів']
|
||||
can_view_schedule integer [default: 0, note: 'Доступ до перегляду графіку зібрань']
|
||||
can_view_territory integer [default: 0, note: 'Доступ до перегляду особистих та групових територій']
|
||||
}
|
||||
|
||||
Table administrators [note: 'Таблиця адміністраторів'] {
|
||||
id integer [primary key]
|
||||
sheep_id integer [note: 'ID вісника']
|
||||
uuid text [note: 'Код доступа']
|
||||
}
|
||||
|
||||
Table moderators [note: 'Таблиця модераторів'] {
|
||||
id integer [primary key]
|
||||
sheep_id integer [note: 'ID вісника']
|
||||
uuid text [note: 'Код доступа']
|
||||
can_add_sheeps integer [default: 0, note: 'Доступ до додавання вісників']
|
||||
can_add_territory integer [default: 0, note: 'Доступ до створення територій']
|
||||
can_manager_territory integer [default: 0, note: 'Доступ до призначання територій']
|
||||
can_add_stand integer [default: 0, note: 'Доступ до створення стендів']
|
||||
can_manager_stand integer [default: 0, note: 'Доступ до редагування графіку стендів']
|
||||
can_add_schedule integer [default: 0, note: 'Доступ до створення графіку зібрань']
|
||||
}
|
||||
|
||||
Table groups [note: 'Таблиця теократичних груп'] {
|
||||
id integer [primary key]
|
||||
group_number integer [note: 'Номер групи']
|
||||
share_hash text [note: 'Код доступа для посилання спільного доступу до групових території']
|
||||
}
|
||||
|
||||
Table subscription [note: 'Таблиця токенів вісників для повідомлень'] {
|
||||
id integer [primary key]
|
||||
sheep_id integer [note: 'ID вісника']
|
||||
token text [note: 'Токен пристрою']
|
||||
}
|
||||
|
||||
Table house [note: 'Таблиця багатоповерхових будинків'] {
|
||||
id integer [primary key]
|
||||
group_id integer [note: 'ID групи']
|
||||
title text [note: 'Вулиця будинку']
|
||||
number text [note: 'Номер будинку']
|
||||
points text [default: '[]', note: 'Масив точок будинку OSM']
|
||||
points_number text [default: '[]', note: 'Масив точок будинку OSM']
|
||||
geo text [default: '[]', note: 'Точка будинку на мапі']
|
||||
osm_id text [default: '[]', note: 'Список ID будинків в БД OSM']
|
||||
settlement text [note: 'Місто роздашування']
|
||||
description text [note: 'Коментар до будинку']
|
||||
created_at timestamp [note: 'Дата створення будинку']
|
||||
updated_at timestamp [note: 'Дата зміни будинку']
|
||||
}
|
||||
|
||||
Table entrance [note: 'Таблиця підїздів багатоповерхових будинків'] {
|
||||
id integer [primary key]
|
||||
house_id integer [note: 'ID багатоповерхового будинку']
|
||||
entrance_number integer [note: 'Номер підїзду']
|
||||
title text [note: 'Назва підїзду']
|
||||
points text [default: '[]', note: 'Масив точок підїзду OSM']
|
||||
points_number text [default: '[]', note: 'Масив точок підїзду OSM']
|
||||
floors_quantity text [note: 'Кількість поверхів в підїзді']
|
||||
apartments_quantity text [note: 'Кількість квартир в підїзді']
|
||||
description text [note: 'Коментар до підїзду']
|
||||
created_at timestamp [note: 'Дата створення підїзду']
|
||||
updated_at timestamp [note: 'Дата зміни підїзду']
|
||||
}
|
||||
|
||||
Table entrance_history [note: 'Таблиця історії вісників які опрацьовували багатоповерхові будинки'] {
|
||||
id integer [primary key]
|
||||
entrance_id integer [note: 'ID підїзду']
|
||||
name text [note: 'Хто опрацовував (імʼя)']
|
||||
date_start timestamp [note: 'Початок опрацювання']
|
||||
date_end timestamp [note: 'Кінець опрацювання']
|
||||
group_id integer [note: 'Група яка опрацювувала']
|
||||
sheep_id text [note: 'ID вісника що зробив зміни']
|
||||
working integer [default: 0, note: 'Статус опрацювання']
|
||||
}
|
||||
|
||||
Table apartments [note: 'Таблиця квартир'] {
|
||||
id integer [primary key]
|
||||
entrance_id integer [note: 'ID підїзду']
|
||||
apartment_number integer [note: 'Номер квартири']
|
||||
title text [note: 'Назва квартири']
|
||||
floors_number integer [note: 'Номер поверху']
|
||||
status integer [note: 'Статус квартири']
|
||||
description text [note: 'Коментар до квартири']
|
||||
sheep_id text [note: 'ID вісника що зробив зміни']
|
||||
updated_at timestamp [note: 'Дата зміни історії квартири']
|
||||
}
|
||||
|
||||
Table apartments_history [note: 'Таблиця історії опрацьовування квартир'] {
|
||||
id integer [primary key]
|
||||
sheep_id text [note: 'ID вісника що зробив зміни']
|
||||
apartments_id integer [note: 'ID квартири']
|
||||
status integer [note: 'Статус квартири']
|
||||
description text [note: 'Коментар до квартири']
|
||||
created_at timestamp [note: 'Дата зміни історії квартири']
|
||||
}
|
||||
|
||||
Table homestead [note: 'Таблиця житлових районів'] {
|
||||
id integer [primary key]
|
||||
group_id integer [note: 'ID групи']
|
||||
title text [note: 'Житловий район']
|
||||
number text [note: 'Номер житловогу району']
|
||||
points text [default: '[]', note: 'Масив точок житловогу району OSM']
|
||||
point_icons text [default: '[]', note: 'Масив точок піктограм житловогу району']
|
||||
geo text [default: '[]', note: 'Точка житловогу району на мапі']
|
||||
osm_id text [default: '[]', note: 'Список ID житловоих районів в БД OSM']
|
||||
settlement text [note: 'Місто роздашування']
|
||||
description text [note: 'Коментар до житловогу району']
|
||||
created_at timestamp [note: 'Дата створення житловогу району']
|
||||
updated_at timestamp [note: 'Дата зміни житловогу району']
|
||||
}
|
||||
|
||||
Table homestead_history [note: 'Таблиця історії вісників які опрацьовували житлові райони'] {
|
||||
id integer [primary key]
|
||||
homestead_id integer [note: 'ID підїзду']
|
||||
name text [note: 'Хто опрацовував (імʼя)']
|
||||
date_start timestamp [note: 'Початок опрацювання']
|
||||
date_end timestamp [note: 'Кінець опрацювання']
|
||||
group_id integer [note: 'Група яка опрацювувала']
|
||||
sheep_id text [note: 'ID вісника що зробив зміни']
|
||||
working integer [default: 0, note: 'Статус опрацювання']
|
||||
}
|
||||
|
||||
Table meetings_schedule [note: 'Таблиця розкладу зібрань'] {
|
||||
id integer [primary key]
|
||||
date timestamp [note: 'Дата зібрання']
|
||||
type integer [note: 'Тип зібрання']
|
||||
name text [note: 'Імʼя вісника що має завдання']
|
||||
sheep_id text [note: 'ID вісника що має завдання']
|
||||
title text [note: 'Номер пісні або назва промови']
|
||||
number text [note: 'Номер пункту графіка']
|
||||
}
|
||||
|
||||
Table stand_list [note: 'Таблиця місць розташування стенду та його налаштування'] {
|
||||
id integer [primary key]
|
||||
title text [note: 'Назва місця розташування стенду']
|
||||
hour_start integer [default: 10, note: 'Година початку служіння']
|
||||
hour_end integer [default: 16, note: 'Година закінчення служіння']
|
||||
quantity_sheep integer [default: 2, note: 'Кількість вісників, що можуть стояти одночасно']
|
||||
week_days text [default: '[0, 1, 2, 3, 4, 5, 6]', note: 'Дні тижня, на яких стоїть стенд']
|
||||
}
|
||||
|
||||
Table stand_schedule [note: 'Таблиця записів служіння зі стендом'] {
|
||||
id integer [primary key]
|
||||
stand integer [note: 'ID стенду']
|
||||
date timestamp [note: 'Дата служіння зі стендом']
|
||||
hour integer [note: 'Година запису']
|
||||
sheep_id text [note: 'ID вісника']
|
||||
number_sheep text [note: 'Номер вісника, що одночасно стоїть']
|
||||
updated_at timestamp [note: 'Дата зміни запису']
|
||||
}
|
||||
|
||||
|
||||
Ref: sheeps.id - administrators.sheep_id // one-to-one
|
||||
Ref: sheeps.id - moderators.sheep_id // one-to-one
|
||||
Ref: sheeps.id < apartments_history.sheep_id // one-to-many
|
||||
Ref: sheeps.id < apartments.sheep_id // one-to-many
|
||||
Ref: sheeps.id < subscription.sheep_id // one-to-many
|
||||
Ref: sheeps.id < homestead_history.sheep_id // one-to-many
|
||||
Ref: sheeps.id < entrance_history.sheep_id // one-to-many
|
||||
Ref: sheeps.id < meetings_schedule.sheep_id // one-to-many
|
||||
Ref: sheeps.id < stand_schedule.sheep_id // one-to-many
|
||||
|
||||
Ref: groups.group_number < sheeps.group_id // one-to-many
|
||||
Ref: groups.group_number < house.group_id // one-to-many
|
||||
Ref: groups.group_number < homestead.group_id // one-to-many
|
||||
|
||||
Ref: house.id < entrance.house_id // one-to-many
|
||||
|
||||
Ref: homestead.id < homestead_history.homestead_id // one-to-many
|
||||
|
||||
Ref: entrance.id < entrance_history.entrance_id // one-to-many
|
||||
Ref: entrance.id < apartments.entrance_id // one-to-many
|
||||
|
||||
Ref: apartments.id < apartments_history.apartments_id // one-to-many
|
||||
|
||||
Ref: stand_list.id < stand_schedule.stand // one-to-many
|
||||
16305
dock/Sheep-Service.pdf
Normal file
BIN
dock/Sheep-Service.png
Normal file
|
After Width: | Height: | Size: 287 KiB |
43
docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
services:
|
||||
api:
|
||||
container_name: sheep-service-api
|
||||
image: sheep-service/api
|
||||
build: ./api
|
||||
restart: always
|
||||
ports:
|
||||
- "${api:-4000}:${api:-4000}"
|
||||
volumes:
|
||||
- "${DB_PATH:-./data}:/app/data"
|
||||
- "${CARDS_PATH:-./data}:/app/data/cards"
|
||||
environment:
|
||||
- DATABASE_PATH=/app/data/
|
||||
- CARDS_PATH=/app/data/cards/
|
||||
- PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
|
||||
shm_size: '1gb'
|
||||
|
||||
ws:
|
||||
container_name: sheep-service-ws
|
||||
image: sheep-service/ws
|
||||
build: ./ws
|
||||
restart: always
|
||||
ports:
|
||||
- "${ws:-4001}:${ws:-4001}"
|
||||
volumes:
|
||||
- "${DB_PATH:-./data}:/app/data"
|
||||
environment:
|
||||
- DATABASE_PATH=/app/data/
|
||||
|
||||
web:
|
||||
container_name: sheep-service-web
|
||||
image: sheep-service/web
|
||||
build: ./web
|
||||
restart: always
|
||||
ports:
|
||||
- "${web:-4002}:${web:-4002}"
|
||||
volumes:
|
||||
- "${CARDS_PATH:-./data}:/app/data/cards"
|
||||
environment:
|
||||
- CARDS_PATH=/app/data/cards/
|
||||
|
||||
volumes:
|
||||
data:
|
||||
170
scripts/import.js
Normal file
@@ -0,0 +1,170 @@
|
||||
const sqlite3 = require('sqlite3');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Данные для записи
|
||||
const data = [
|
||||
{ "name": "Богданова Л.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Ботюк Л.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Ботюк М.", "group": "1", "status": "elder" },
|
||||
{ "name": "Венгер С.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Гловюк С.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Гнатюк П.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Дуньковська Г.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Корінь О.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Медецька Л.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Меладзе А.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Меладзе М.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Нуждіна Н.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Сидорчук І.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Сидорчук О.", "group": "1", "status": "elder" },
|
||||
{ "name": "Смірнов Б.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Смірнова В.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Смірнов Л.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Ткаченко Н.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Медецький Р.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Заболотний В.", "group": "1", "status": "lamb" },
|
||||
{ "name": "Власюк Т.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Демків В.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Іващенко А.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Кіналь Г.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Кіналь М.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Кіналь Т.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Ковчук Д.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Кушнірук Н.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Муц М.", "group": "2", "status": "elder" },
|
||||
{ "name": "Муц Н.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Муц О.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Подвірна О.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Позовиков М.", "group": "2", "status": "elder" },
|
||||
{ "name": "Позовиков О.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Позовикова Е.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Позовикова Л.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Позовикова М.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Резніченко А.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Резніченко В.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Хоптій Л.", "group": "2", "status": "lamb" },
|
||||
{ "name": "Авдєєва В.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Авдєєв П.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Гречило Л.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Гушатей З.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Дворянська Н.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Дворянський М.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Дворянська М.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Дуньковська Ол.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Дуньковський В.", "group": "3", "status": "elder" },
|
||||
{ "name": "Дуньковська Л.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Кавюк Н.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Ковалюк Е.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Ковалюк С.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Липа А.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Липа Н.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Музика С.", "group": "3", "status": "elder" },
|
||||
{ "name": "Музика Св.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Філь Н.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Червенко Л.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Майка М.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Петрович І.", "group": "3", "status": "lamb" },
|
||||
{ "name": "Буняк Н.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Буярська А.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Буярська Н.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Буярський М.", "group": "4", "status": "elder" },
|
||||
{ "name": "Грищук Т.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Карелін І.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Кареліна Ір.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Кареліна С.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Ковальчук Н.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Ковальчук Р.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Ковчук Р.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Резніченко Т.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Сергієнко О.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Сергієнко С.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Стойкевич М.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Чапайло Г.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Шептицька В.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Шептицький І.", "group": "4", "status": "lamb" },
|
||||
{ "name": "Благов Я.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Благова П.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Богів Г.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Богів Т.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Галка А.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Домбрович О.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Кавюк А.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Кавюк В.", "group": "5", "status": "elder" },
|
||||
{ "name": "Кузнєцова Н.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Луців І.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Луців О.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Сиротюк В.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Сиротюк О.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Солонинка С.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Ульянич І.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Ульянич О.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Шмигельська С.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Сидорчук Т.", "group": "5", "status": "lamb" },
|
||||
{ "name": "Білоліпецький І.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Винниченко Г.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Воронцов Д.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Дуньковська О.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Загурська О.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Іваненко К.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Кравчук Н.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Кузюк В.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Кузюк С.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Маняхіна А.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Мельник Н.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Мінтенко М.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Мінтенко Н.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Наворинська Н.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Носевич І.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Носевич Т.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Страшок М.", "group": "6", "status": "elder" },
|
||||
{ "name": "Страшок О.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Мельник О.", "group": "6", "status": "lamb" },
|
||||
{ "name": "Бугайов Д.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Гергель Л.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Гергель О.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Горун А.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Ковчук Л.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Ковчук Н.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Михайлів С.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Музика І.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Музика О.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Наворинський Р.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Парила І.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Резніченко А.", "group": "7", "status": "elder" },
|
||||
{ "name": "Резніченко В.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Семчишин Ф.", "group": "7", "status": "elder" },
|
||||
{ "name": "Сувалко В.", "group": "7", "status": "elder" },
|
||||
{ "name": "Сувалко Н.", "group": "7", "status": "lamb" },
|
||||
{ "name": "Якубович Л.", "group": "7", "status": "lamb" }
|
||||
];
|
||||
|
||||
// Подключение к базе данных (или создание, если не существует)
|
||||
const db = new sqlite3.Database('../database.sqlite', (err) => {
|
||||
if (err) {
|
||||
console.error('Ошибка при подключении к БД:', err.message);
|
||||
} else {
|
||||
console.log('Подключение к SQLite успешно');
|
||||
}
|
||||
});
|
||||
|
||||
// Вставка данных
|
||||
const insertData = () => {
|
||||
const stmt = db.prepare("INSERT INTO sheep (name, group_id, appointment, hash) VALUES (?, ?, ?, ?)");
|
||||
data.forEach(user => {
|
||||
stmt.run(user.name, user.group, user.status, crypto.randomUUID());
|
||||
});
|
||||
stmt.finalize();
|
||||
console.log('Данные успешно записаны');
|
||||
};
|
||||
|
||||
// Запуск вставки данных после создания таблицы
|
||||
insertData();
|
||||
|
||||
// Закрытие соединения с БД
|
||||
db.close((err) => {
|
||||
if (err) {
|
||||
console.error('Ошибка при закрытии БД:', err.message);
|
||||
} else {
|
||||
console.log('Соединение с БД закрыто');
|
||||
}
|
||||
});
|
||||
161
scripts/migrator.js
Normal file
@@ -0,0 +1,161 @@
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
|
||||
const db = new sqlite3.Database('database.sqlite');
|
||||
const db_old = new sqlite3.Database('old_db.sqlite');
|
||||
|
||||
|
||||
// Створення підїздів
|
||||
// const sql_1 = `SELECT * FROM areas`;
|
||||
// db_old.all(sql_1, [], (err, areas) => {
|
||||
// if (err) {
|
||||
// throw err;
|
||||
// }
|
||||
|
||||
// for (let i = 0; i < areas.length; i++) {
|
||||
// const area = areas[i];
|
||||
|
||||
// if(area.type == "house"){
|
||||
// // console.log(JSON.parse(area.entrance));
|
||||
|
||||
// db.get('SELECT * FROM house WHERE title = ? AND number = ?', [area.address_title, area.address_number], (err, house) => {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// let entrances = JSON.parse(area.entrance);
|
||||
// let entrance_numbers = JSON.parse(area.entrance_number);
|
||||
|
||||
// for (let q = 0; q < entrances.length; q++) {
|
||||
// const entrance = entrances[q];
|
||||
// const number = entrance_numbers[q];
|
||||
// console.log(entrance, number);
|
||||
|
||||
// db.run(`INSERT INTO entrance(house_id, entrance_number, title, points, points_number) VALUES(?, ?, ?, ?, ?)`,
|
||||
// [
|
||||
// house.id,
|
||||
// q,
|
||||
// `Під'їзд ${q+1}`,
|
||||
// JSON.stringify(entrance),
|
||||
// JSON.stringify(number)
|
||||
// ],
|
||||
// function (err) {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// console.log("New user entrance added with id " + this.lastID);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
// // console.log(JSON.parse(area.entrance));
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
|
||||
// Міграція історії з старої БД в нову
|
||||
// const sql_1 = `SELECT * FROM history ORDER BY date_start`;
|
||||
// db_old.all(sql_1, [], (err, historys) => {
|
||||
// if (err) {
|
||||
// throw err;
|
||||
// }
|
||||
|
||||
// for (let i = 0; i < historys.length; i++) {
|
||||
// const history = historys[i];
|
||||
|
||||
// db_old.get('SELECT * FROM territory WHERE number = ?', [history.territory_number], (err, territory) => {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// // console.log(territory);
|
||||
// let areas_id = JSON.parse(territory.areas_id)
|
||||
|
||||
// for (let index = 0; index < areas_id.length; index++) {
|
||||
// const element = areas_id[index];
|
||||
|
||||
// db_old.get('SELECT * FROM areas WHERE id = ?', [element[0]], (err, area) => {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// if (area.type == "house") {
|
||||
// console.log(area.address_title, area.address_number);
|
||||
|
||||
// db.get('SELECT * FROM house WHERE title = ? AND number = ?', [area.address_title, area.address_number], (err, house) => {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// console.log(area.address_title, area.address_number);
|
||||
// console.log(house.id, element[1]);
|
||||
|
||||
// db.get('SELECT * FROM entrance WHERE house_id = ? AND entrance_number = ?', [house.id, element[1]], (err, entrance) => {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// console.log(house.id, element[1]);
|
||||
|
||||
// console.log(entrance.id, house.title, house.number, entrance.title);
|
||||
|
||||
// db.run(`INSERT INTO entrance_history(entrance_id, name, date_start, date_end, group_number, working) VALUES(?, ?, ?, ?, ?, ?)`,
|
||||
// [
|
||||
// entrance.id,
|
||||
// history.name,
|
||||
// history.date_start,
|
||||
// history.date_end,
|
||||
// history.group_number,
|
||||
// history.working,
|
||||
// ],
|
||||
// function (err) {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// console.log("New user history added with id " + this.lastID);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
|
||||
// } else {
|
||||
// db.get('SELECT * FROM homestead WHERE title = ? AND number = ?', [area.address_title, area.address_number], (err, homestead) => {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// // console.log(house.id, house.title, house.number);
|
||||
|
||||
// db.run(`INSERT INTO homestead_history(homestead_id, name, date_start, date_end, group_number, working) VALUES(?, ?, ?, ?, ?, ?)`,
|
||||
// [
|
||||
// homestead.id,
|
||||
// history.name,
|
||||
// history.date_start,
|
||||
// history.date_end,
|
||||
// history.group_number,
|
||||
// history.working,
|
||||
// ],
|
||||
// function (err) {
|
||||
// if (err) {
|
||||
// console.error(err.message);
|
||||
// } else {
|
||||
// console.log("New user history added with id " + this.lastID);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// });
|
||||
|
||||
14
scripts/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "import",
|
||||
"version": "1.0.0",
|
||||
"main": "import.js",
|
||||
"scripts": {
|
||||
"start": "node import.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"sqlite3": "^5.1.7"
|
||||
}
|
||||
}
|
||||
181
scripts/updateDB.py
Normal file
@@ -0,0 +1,181 @@
|
||||
import sqlite3
|
||||
|
||||
def table_exists(cursor, table_name):
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?;", (table_name,))
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
def create_tables():
|
||||
conn = sqlite3.connect("database.sqlite")
|
||||
cursor = conn.cursor()
|
||||
|
||||
tables = {
|
||||
"sheep": """
|
||||
CREATE TABLE IF NOT EXISTS sheep (
|
||||
id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER,
|
||||
name TEXT,
|
||||
icon TEXT,
|
||||
hash TEXT,
|
||||
appointment TEXT DEFAULT 'lamb',
|
||||
mode INTEGER DEFAULT 0,
|
||||
cu_access INTEGER DEFAULT 0,
|
||||
mt_access INTEGER DEFAULT 0,
|
||||
ct_access INTEGER DEFAULT 0,
|
||||
ms_access INTEGER DEFAULT 0,
|
||||
sm_access INTEGER DEFAULT 0,
|
||||
FOREIGN KEY (group_id) REFERENCES groups(id)
|
||||
);""",
|
||||
"groups": """
|
||||
CREATE TABLE IF NOT EXISTS groups (
|
||||
id INTEGER PRIMARY KEY,
|
||||
group_number INTEGER,
|
||||
share_hash TEXT
|
||||
);""",
|
||||
"subscription": """
|
||||
CREATE TABLE IF NOT EXISTS subscription (
|
||||
id INTEGER PRIMARY KEY,
|
||||
sheep_id INTEGER,
|
||||
token TEXT,
|
||||
FOREIGN KEY (sheep_id) REFERENCES sheep(id)
|
||||
);""",
|
||||
"house": """
|
||||
CREATE TABLE IF NOT EXISTS house (
|
||||
id INTEGER PRIMARY KEY,
|
||||
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(id)
|
||||
);""",
|
||||
"entrance": """
|
||||
CREATE TABLE IF NOT EXISTS entrance (
|
||||
id INTEGER PRIMARY KEY,
|
||||
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)
|
||||
);""",
|
||||
"entrance_history": """
|
||||
CREATE TABLE IF NOT EXISTS entrance_history (
|
||||
id INTEGER PRIMARY KEY,
|
||||
entrance_id INTEGER,
|
||||
name TEXT,
|
||||
date_start TIMESTAMP,
|
||||
date_end TIMESTAMP,
|
||||
group_number INTEGER,
|
||||
sheep_id TEXT,
|
||||
working INTEGER DEFAULT 0,
|
||||
FOREIGN KEY (entrance_id) REFERENCES entrance(id),
|
||||
FOREIGN KEY (sheep_id) REFERENCES sheep(id)
|
||||
);""",
|
||||
"apartments": """
|
||||
CREATE TABLE IF NOT EXISTS apartments (
|
||||
id INTEGER PRIMARY KEY,
|
||||
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),
|
||||
FOREIGN KEY (sheep_id) REFERENCES sheep(id)
|
||||
);""",
|
||||
"apartments_history": """
|
||||
CREATE TABLE IF NOT EXISTS apartments_history (
|
||||
id INTEGER PRIMARY KEY,
|
||||
apartments_id INTEGER,
|
||||
status INTEGER,
|
||||
description TEXT,
|
||||
sheep_id TEXT,
|
||||
created_at TIMESTAMP,
|
||||
FOREIGN KEY (apartments_id) REFERENCES apartments(id)
|
||||
);""",
|
||||
"homestead": """
|
||||
CREATE TABLE IF NOT EXISTS homestead (
|
||||
id INTEGER PRIMARY KEY,
|
||||
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(id)
|
||||
);""",
|
||||
"homestead_history": """
|
||||
CREATE TABLE IF NOT EXISTS homestead_history (
|
||||
id INTEGER PRIMARY KEY,
|
||||
homestead_id INTEGER,
|
||||
name TEXT,
|
||||
date_start TIMESTAMP,
|
||||
date_end TIMESTAMP,
|
||||
group_number INTEGER,
|
||||
sheep_id TEXT,
|
||||
working INTEGER DEFAULT 0,
|
||||
FOREIGN KEY (homestead_id) REFERENCES homestead(id),
|
||||
FOREIGN KEY (sheep_id) REFERENCES sheep(id)
|
||||
);""",
|
||||
"meetings_schedule": """
|
||||
CREATE TABLE IF NOT EXISTS meetings_schedule (
|
||||
id INTEGER PRIMARY KEY,
|
||||
date TIMESTAMP,
|
||||
type INTEGER,
|
||||
name TEXT,
|
||||
sheep_id TEXT,
|
||||
title TEXT,
|
||||
number TEXT,
|
||||
FOREIGN KEY (sheep_id) REFERENCES sheep(id)
|
||||
);""",
|
||||
"stand_list": """
|
||||
CREATE TABLE IF NOT EXISTS stand_list (
|
||||
id INTEGER PRIMARY KEY,
|
||||
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]'
|
||||
);""",
|
||||
"stand_schedule": """
|
||||
CREATE TABLE IF NOT EXISTS stand_schedule (
|
||||
id INTEGER PRIMARY KEY,
|
||||
stand INTEGER,
|
||||
date TIMESTAMP,
|
||||
hour INTEGER,
|
||||
sheep_id TEXT,
|
||||
number_sheep TEXT,
|
||||
updated_at TIMESTAMP,
|
||||
FOREIGN KEY (sheep_id) REFERENCES sheep(id),
|
||||
FOREIGN KEY (stand) REFERENCES stand_list(id)
|
||||
);"""
|
||||
}
|
||||
|
||||
for name, sql in tables.items():
|
||||
if not table_exists(cursor, name):
|
||||
cursor.execute(sql)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_tables()
|
||||
print("Database setup complete.")
|
||||
13
web/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM node:20.18
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 4002
|
||||
|
||||
CMD npm start
|
||||
4
web/config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const CONFIG = {
|
||||
"api": "https://sheep-service.com/api/",
|
||||
"wss": "wss://sheep-service.com/ws"
|
||||
}
|
||||
551
web/css/main.css
Normal file
@@ -0,0 +1,551 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
/* PrimaryColor */
|
||||
--PrimaryColor: #28a55a;
|
||||
--PrimaryColor: #f2bd53;
|
||||
--PrimaryColorText: #2e2e2e;
|
||||
/* BGColor */
|
||||
--ColorThemes0: #fbfbfb;
|
||||
--ColorThemes1: #f3f3f3;
|
||||
--ColorThemes2: #e5e5df;
|
||||
/* TextColor */
|
||||
--ColorThemes3: #313131;
|
||||
|
||||
--ColorAnimation: linear-gradient(90deg, #f3f3f3, #efefef, #f3f3f3);
|
||||
|
||||
--shadow-l1: 0px 2px 4px rgba(0, 0, 0, 0.02), 0px 0px 2px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
|
||||
--border-radius: 15px;
|
||||
|
||||
|
||||
--CardAnimation: linear-gradient(to right, #fbfbfb 0%, #fbfbfb 30%, #d8d8d8 45%, #d8d8d8 50%, #fbfbfb 60%, #fbfbfb 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
/* PrimaryColor */
|
||||
--PrimaryColor: #28a55a;
|
||||
--PrimaryColor: #cb9e44;
|
||||
--PrimaryColorText: #2e2e2e;
|
||||
/* BGColor */
|
||||
--ColorThemes0: #1c1c19;
|
||||
--ColorThemes1: #21221d;
|
||||
--ColorThemes2: #3a3a39;
|
||||
/* TextColor */
|
||||
--ColorThemes3: #f3f3f3;
|
||||
|
||||
--ColorAnimation: linear-gradient(90deg, #21221d, #242520, #21221d);
|
||||
|
||||
--shadow-l1: 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04);
|
||||
--border-radius: 15px;
|
||||
|
||||
|
||||
|
||||
--CardAnimation: linear-gradient(to right, #1c1c19 0%, #1c1c19 30%, #252525 45%, #252525 50%, #1c1c19 60%, #1c1c19 100%);
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
margin: 0;
|
||||
font-weight: 300;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
*:disabled {
|
||||
opacity: 0.6 !important;
|
||||
cursor: no-drop !important;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
* {
|
||||
scroll-snap-type: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (pointer:fine) {
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-color: #f9f9fd;
|
||||
border-radius: var(--border-radius);
|
||||
background: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--PrimaryColor);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--ColorThemes2);
|
||||
color: var(--ColorThemes3);
|
||||
/* transition: .3s ease; */
|
||||
}
|
||||
|
||||
/* Стили для анимации обновления страници свайпом */
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
appearance: none;
|
||||
background-image: url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23F2BD53%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E);
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.7rem top 50%;
|
||||
background-size: 0.65rem auto;
|
||||
}
|
||||
|
||||
.hold-button{
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
.custom-checkbox {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.custom-checkbox+label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.custom-checkbox+label::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
border: 1px solid #adb5bd;
|
||||
border-radius: 10px;
|
||||
margin-right: 0.5em;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 50% 50%;
|
||||
}
|
||||
|
||||
.custom-checkbox:checked+label::before {
|
||||
border-color: var(--PrimaryColor);
|
||||
background-color: var(--PrimaryColor);
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.custom-checkbox:not(:disabled):not(:checked)+label:hover::before {
|
||||
border-color: var(--PrimaryColor);
|
||||
}
|
||||
|
||||
.custom-checkbox:not(:disabled):active+label::before {
|
||||
background-color: var(--PrimaryColor);
|
||||
border-color: var(--PrimaryColor);
|
||||
}
|
||||
|
||||
.custom-checkbox:focus:not(:checked)+label::before {
|
||||
border-color: var(--PrimaryColor);
|
||||
}
|
||||
|
||||
.custom-checkbox:disabled+label::before {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
#swipe_updater {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#swipe_block {
|
||||
width: calc(100% - 252px);
|
||||
margin-left: 252px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#swipe_icon {
|
||||
width: 20px;
|
||||
fill: var(--ColorThemes3);
|
||||
transform: rotate(0deg);
|
||||
position: absolute;
|
||||
margin-top: -45px;
|
||||
top: -45px;
|
||||
background: var(--ColorThemes2);
|
||||
border: 2px solid var(--ColorThemes3);
|
||||
border-radius: 50%;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
transition: height 0ms 400ms, opacity 400ms 0ms;
|
||||
}
|
||||
|
||||
#swipe_icon[data-state="active"] {
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
transition: height 0ms 0ms, opacity 400ms 0ms;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
#swipe_block {
|
||||
width: calc(100% - 122px);
|
||||
margin-left: 122px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
#swipe_block {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Уведомление и кнопка обновления приложения */
|
||||
#update_banner {
|
||||
height: 55px;
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
#update_banner .content {
|
||||
margin: 0 auto;
|
||||
max-width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#update_banner .headline {
|
||||
font-weight: 800;
|
||||
font-size: 15px;
|
||||
color: var(--PrimaryColorText);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
#update_banner .subhead {
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
color: var(--PrimaryColorText);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
#update_banner[data-state="noupdate"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#update_banner[data-state="updateavailable"] {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: var(--PrimaryColor);
|
||||
color: var(--PrimaryColorText);
|
||||
transition: .3s ease;
|
||||
opacity: 0.95;
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
border-radius: 25px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#update_banner_icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#update_banner_icon svg {
|
||||
padding: 0px 50px;
|
||||
width: 25px;
|
||||
margin: -4px;
|
||||
fill: var(--PrimaryColorText);
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
#update_banner[data-state="updateavailable"] {
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Стили для меню */
|
||||
#navigation {
|
||||
position: fixed;
|
||||
width: 230px;
|
||||
height: calc(100vh - 60px);
|
||||
min-height: 510px;
|
||||
background: var(--ColorThemes2);
|
||||
padding: 36px 10px;
|
||||
-webkit-transition: width .2s ease 0s;
|
||||
-o-transition: width .2s ease 0s;
|
||||
transition: width .2s ease 0s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#navigation>nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 290px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
#navigation>nav>li {
|
||||
width: 180px;
|
||||
height: 50px;
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
-webkit-transition: all .2s ease 0s;
|
||||
-o-transition: all .2s ease 0s;
|
||||
transition: all .2s ease 0s;
|
||||
z-index: 1;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
height: 50px;
|
||||
padding: 0 10px;
|
||||
border-radius: var(--border-radius);
|
||||
-webkit-transition: all .2s ease 0s;
|
||||
-o-transition: all .2s ease 0s;
|
||||
transition: all .2s ease 0s;
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
border: 2px;
|
||||
border: 2px solid var(--ColorThemes2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div[data-state="active"] {
|
||||
background: var(--ColorThemes3);
|
||||
border: 2px solid var(--ColorThemes3);
|
||||
box-shadow: var(--shadow-l1);
|
||||
}
|
||||
|
||||
#navigation>nav>li:has(div[data-state="active"]) {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div>svg {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
min-width: 25px;
|
||||
min-height: 25px;
|
||||
fill: var(--ColorThemes3);
|
||||
}
|
||||
|
||||
#navigation>nav>li>div[data-state="active"] svg {
|
||||
fill: var(--ColorThemes2);
|
||||
}
|
||||
|
||||
#navigation>nav>li>div>b {
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
color: var(--ColorThemes3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div[data-state="active"] b {
|
||||
color: var(--ColorThemes2);
|
||||
}
|
||||
|
||||
#navigation>nav>li>a {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
|
||||
@media (hover: hover) {
|
||||
#navigation>nav>li:hover {
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
#navigation>nav>li:hover>div {
|
||||
border: 2px solid var(--ColorThemes3);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
#navigation {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
#navigation>nav>li {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div {
|
||||
width: 30px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div>b {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#app {
|
||||
background: var(--ColorThemes0);
|
||||
position: absolute;
|
||||
margin-top: 40px;
|
||||
margin-left: 250px;
|
||||
padding: 5px;
|
||||
width: calc(100% - 250px - 10px);
|
||||
min-height: calc(100% - 50px);
|
||||
border-radius: 20px 0 0 0;
|
||||
-webkit-transition: all .2sease 0s;
|
||||
-o-transition: all .2s ease 0s;
|
||||
transition: all .2sease 0s;
|
||||
box-shadow: var(--shadow-l1);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
#app {
|
||||
margin-left: 120px;
|
||||
width: calc(100% - 130px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px),
|
||||
(max-height: 540px) {
|
||||
body {
|
||||
background: var(--ColorThemes0);
|
||||
}
|
||||
|
||||
#navigation {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
min-height: 60px;
|
||||
padding: 0;
|
||||
z-index: 99999990;
|
||||
bottom: -1px;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
box-shadow: 0px 24px 32px rgba(0, 0, 0, .09), 0px 16px 24px rgba(0, 0, 0, .09), 0px 4px 8px rgba(0, 0, 0, .09), 0px 0px 1px rgba(0, 0, 0, .09);
|
||||
}
|
||||
|
||||
#navigation>nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#navigation>nav>li {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#navigation>nav>li:hover {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
|
||||
#navigation>nav>li:hover>div {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div>span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div[data-state="active"] {
|
||||
background: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#navigation>nav>li>div[data-state="active"]>svg {
|
||||
fill: var(--PrimaryColor);
|
||||
}
|
||||
|
||||
#navigation>nav>li>button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#navigation[data-state="ios"] {
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
#navigation[data-state="ios"]>nav {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#app {
|
||||
margin-left: 0px;
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
margin-top: 0;
|
||||
padding-bottom: 80px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
94
web/fonts/PT_Mono/OFL.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Copyright (c) 2011, ParaType Ltd. (http://www.paratype.com/public),
|
||||
with Reserved Font Names "PT Sans", "PT Serif", "PT Mono" and "ParaType".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
web/fonts/PT_Mono/PTMono-Regular.ttf
Normal file
96
web/fonts/UbuntuMono/UFL.txt
Normal file
@@ -0,0 +1,96 @@
|
||||
-------------------------------
|
||||
UBUNTU FONT LICENCE Version 1.0
|
||||
-------------------------------
|
||||
|
||||
PREAMBLE
|
||||
This licence allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely. The fonts, including any derivative works, can be
|
||||
bundled, embedded, and redistributed provided the terms of this licence
|
||||
are met. The fonts and derivatives, however, cannot be released under
|
||||
any other licence. The requirement for fonts to remain under this
|
||||
licence does not require any document created using the fonts or their
|
||||
derivatives to be published under this licence, as long as the primary
|
||||
purpose of the document is not to be a vehicle for the distribution of
|
||||
the fonts.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this licence and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Original Version" refers to the collection of Font Software components
|
||||
as received under this licence.
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to
|
||||
a new environment.
|
||||
|
||||
"Copyright Holder(s)" refers to all individuals and companies who have a
|
||||
copyright ownership of the Font Software.
|
||||
|
||||
"Substantially Changed" refers to Modified Versions which can be easily
|
||||
identified as dissimilar to the Font Software by users of the Font
|
||||
Software comparing the Original Version with the Modified Version.
|
||||
|
||||
To "Propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification and with or without charging
|
||||
a redistribution fee), making available to the public, and in some
|
||||
countries other activities as well.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
This licence does not grant any rights under trademark law and all such
|
||||
rights are reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of the Font Software, to propagate the Font Software, subject to
|
||||
the below conditions:
|
||||
|
||||
1) Each copy of the Font Software must contain the above copyright
|
||||
notice and this licence. These can be included either as stand-alone
|
||||
text files, human-readable headers or in the appropriate machine-
|
||||
readable metadata fields within text or binary files as long as those
|
||||
fields can be easily viewed by the user.
|
||||
|
||||
2) The font name complies with the following:
|
||||
(a) The Original Version must retain its name, unmodified.
|
||||
(b) Modified Versions which are Substantially Changed must be renamed to
|
||||
avoid use of the name of the Original Version or similar names entirely.
|
||||
(c) Modified Versions which are not Substantially Changed must be
|
||||
renamed to both (i) retain the name of the Original Version and (ii) add
|
||||
additional naming elements to distinguish the Modified Version from the
|
||||
Original Version. The name of such Modified Versions must be the name of
|
||||
the Original Version, with "derivative X" where X represents the name of
|
||||
the new work, appended to that name.
|
||||
|
||||
3) The name(s) of the Copyright Holder(s) and any contributor to the
|
||||
Font Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except (i) as required by this licence, (ii) to
|
||||
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
|
||||
their explicit written permission.
|
||||
|
||||
4) The Font Software, modified or unmodified, in part or in whole, must
|
||||
be distributed entirely under this licence, and must not be distributed
|
||||
under any other licence. The requirement for fonts to remain under this
|
||||
licence does not affect any document created using the Font Software,
|
||||
except any version of the Font Software extracted from a document
|
||||
created using the Font Software may only be distributed under this
|
||||
licence.
|
||||
|
||||
TERMINATION
|
||||
This licence becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
|
||||
DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
web/fonts/UbuntuMono/UbuntuMono-Bold.ttf
Normal file
BIN
web/fonts/UbuntuMono/UbuntuMono-BoldItalic.ttf
Normal file
BIN
web/fonts/UbuntuMono/UbuntuMono-Italic.ttf
Normal file
BIN
web/fonts/UbuntuMono/UbuntuMono-Regular.ttf
Normal file
41
web/img/0.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" inkscape:export-ydpi="96" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" inkscape:export-xdpi="96" width="48" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" height="48" xmlns:svg="http://www.w3.org/2000/svg" style="fill:none;stroke:none;" viewBox="0 0 48 48" version="1.1">
|
||||
<defs/>
|
||||
<sodipodi:namedview pagecolor="#ffffff" inkscape:pagecheckerboard="true" borderlayer="true" bordercolor="#666666" inkscape:document-units="px"/>
|
||||
<title>synchronize-cloud</title>
|
||||
<g inkscape:groupmode="layer" id="MainComposition_a4ec5bcbf1d44168962622fcea8276bf" inkscape:label="synchronize-cloud">
|
||||
<g inkscape:groupmode="layer" transform="matrix(1, 0, 0, 1, 0, 0)" opacity="1" id="Layer_9907dd6ff6004617bd3a525195c313f2" inkscape:label="arrows">
|
||||
<g transform="translate(24 29)">
|
||||
<g transform="rotate(0)">
|
||||
<g transform="scale(1 1)">
|
||||
<g transform="translate(-24 -29)">
|
||||
<g opacity="1" id="Group_2cb26585a6274aff82b52d6629f47ff3" inkscape:label="Group 1">
|
||||
<g transform="matrix(1, 0, 0, 1, 18.772, 33.991)" opacity="1" id="Stroke_eac34415491147c99ffc4335fed73f62" stroke="#000000" stroke-width="3" stroke-opacity="1" inkscape:label="Group 4">
|
||||
<path sodipodi:nodetypes="ccc" d="M -2.708,2.491 C -2.708,2.491 -2.273,-2.49 -2.273,-2.49 -2.273,-2.49 2.708,-2.055 2.708,-2.055" style="fill:none;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round;">
|
||||
<animate calcMode="spline" repeatCount="indefinite" attributeName="d" dur="1.166667" begin="0.000000" keyTimes="0; 0.107143; 0.178571; 0.821429; 0.892857; 1" keySplines="0 0 1 1; 0.333000 0.000000 0.667000 1.000000; 0.167000 0.167000 0.833000 0.833000; 0.333000 0.000000 0.667000 1.000000; 0.000000 0.000000 0.000000 0.000000" values="M -2.708,2.491 C -2.708,2.491 -2.273,-2.49 -2.273,-2.49 -2.273,-2.49 2.708,-2.055 2.708,-2.055; M -2.708,2.491 C -2.708,2.491 -2.273,-2.49 -2.273,-2.49 -2.273,-2.49 2.708,-2.055 2.708,-2.055; M -1.771,-2.28 C -1.771,-2.28 -1.761,-2.279 -1.761,-2.279 -1.761,-2.279 -1.78,-2.283 -1.78,-2.283; M -1.768,-2.281 C -1.768,-2.281 -1.758,-2.28 -1.758,-2.28 -1.758,-2.28 -1.778,-2.284 -1.778,-2.284; M -2.708,2.491 C -2.708,2.491 -2.273,-2.49 -2.273,-2.49 -2.273,-2.49 2.708,-2.055 2.708,-2.055; M -2.708,2.491 C -2.708,2.491 -2.273,-2.49 -2.273,-2.49 -2.273,-2.49 2.708,-2.055 2.708,-2.055"/>
|
||||
</path>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, 24.037, 34)" opacity="1" id="Stroke_808239d21b7e4a859d5a99d0baf98e17" stroke="#000000" stroke-width="3" stroke-opacity="1" inkscape:label="Group 3">
|
||||
<path sodipodi:nodetypes="ccc" d="M 7.036,-2.5 C 6.006,0.413 3.228,2.5 -0.036,2.5 -3.227,2.5 -5.953,0.507 -7.036,-2.303" style="fill:none;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round;"/>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, 29.227, 24.009)" opacity="1" id="Stroke_c1f02aae6dea4564b6abe25c038848fe" stroke="#000000" stroke-width="3" stroke-opacity="1" inkscape:label="Group 2">
|
||||
<path sodipodi:nodetypes="ccc" d="M 2.708,-2.49 C 2.708,-2.49 2.273,2.491 2.273,2.491 2.273,2.491 -2.708,2.055 -2.708,2.055" style="fill:none;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round;">
|
||||
<animate calcMode="spline" repeatCount="indefinite" attributeName="d" dur="1.166667" begin="0.000000" keyTimes="0; 0.107143; 0.178571; 0.821429; 0.892857; 1" keySplines="0 0 1 1; 0.333000 0.000000 0.667000 1.000000; 0.167000 0.167000 0.833000 0.833000; 0.333000 0.000000 0.667000 1.000000; 0.000000 0.000000 0.000000 0.000000" values="M 2.708,-2.49 C 2.708,-2.49 2.273,2.491 2.273,2.491 2.273,2.491 -2.708,2.055 -2.708,2.055; M 2.708,-2.49 C 2.708,-2.49 2.273,2.491 2.273,2.491 2.273,2.491 -2.708,2.055 -2.708,2.055; M 1.775,2.301 C 1.775,2.301 1.775,2.298 1.775,2.298 1.775,2.298 1.777,2.29 1.777,2.29; M 1.767,2.292 C 1.767,2.292 1.767,2.289 1.767,2.289 1.767,2.289 1.768,2.281 1.768,2.281; M 2.708,-2.49 C 2.708,-2.49 2.273,2.491 2.273,2.491 2.273,2.491 -2.708,2.055 -2.708,2.055; M 2.708,-2.49 C 2.708,-2.49 2.273,2.491 2.273,2.491 2.273,2.491 -2.708,2.055 -2.708,2.055"/>
|
||||
</path>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, 23.963, 24)" opacity="1" id="Stroke_b8202dc468474015b6784aeccf30c7fe" stroke="#000000" stroke-width="3" stroke-opacity="1" inkscape:label="Group 1">
|
||||
<path sodipodi:nodetypes="ccc" d="M -7.036,2.5 C -6.006,-0.413 -3.229,-2.5 0.036,-2.5 3.228,-2.5 5.953,-0.507 7.036,2.303" style="fill:none;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<animateTransform calcMode="spline" repeatCount="indefinite" attributeName="transform" dur="1.166667" begin="0.000000" type="rotate" keyTimes="0; 0.107143; 0.821429; 0.892857; 1" keySplines="0 0 1 1; 0.333000 0.000000 0.667000 1.000000; 0.333000 0.000000 0.667000 1.000000; 0.000000 0.000000 0.000000 0.000000" values="0; 0; 380; 360; 360"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g inkscape:groupmode="layer" transform="matrix(1, 0, 0, 1, 0, 0)" opacity="1" id="Layer_27939f0fe5ec41fa8de333c2fda4c3f8" inkscape:label="cloud">
|
||||
<g fill-opacity="1" transform="matrix(1, 0, 0, 1, 24, 22.5)" opacity="1" id="Fill_2eda0ab38e6f42adb64be815c093c02c" fill="#000000" inkscape:label="Group 1">
|
||||
<path sodipodi:nodetypes="ccccccccccccccccccccccccc" d="M 15,-2.5 C 15,-2.5 14.42,-2.5 14.42,-2.5 13.67,-9.79 7.49,-15.5 0,-15.5 -7.49,-15.5 -13.67,-9.79 -14.42,-2.5 -14.42,-2.5 -15,-2.5 -15,-2.5 -19.96,-2.5 -24,1.54 -24,6.5 -24,11.46 -19.96,15.5 -15,15.5 -15,15.5 -12.17,15.5 -12.17,15.5 -12.38,14.91 -12.48,14.26 -12.42,13.59 -12.42,13.59 -12.32,12.5 -12.32,12.5 -12.32,12.5 -15,12.5 -15,12.5 -18.31,12.5 -21,9.81 -21,6.5 -21,3.19 -18.31,0.5 -15,0.5 -15,0.5 -13,0.5 -13,0.5 -12.17,0.5 -11.5,-0.17 -11.5,-1 -11.5,-7.34 -6.34,-12.5 0,-12.5 6.34,-12.5 11.5,-7.34 11.5,-1 11.5,-0.17 12.17,0.5 13,0.5 13,0.5 15,0.5 15,0.5 18.31,0.5 21,3.19 21,6.5 21,9.81 18.31,12.5 15,12.5 15,12.5 10.38,12.5 10.38,12.5 9.73,13.64 8.89,14.66 7.92,15.5 7.92,15.5 15,15.5 15,15.5 19.96,15.5 24,11.46 24,6.5 24,1.54 19.96,-2.5 15,-2.5 Z" style=""/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.9 KiB |
BIN
web/img/1.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
1
web/img/1.json
Normal file
6
web/img/1.svg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
web/img/2.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
web/img/IconKitchen.zip
Normal file
BIN
web/img/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
web/img/badge.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
web/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
web/img/icon-192-maskable.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
web/img/icon-192.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
web/img/icon-512-maskable.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
web/img/icon-512.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
web/img/icons/info.gif
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
1
web/img/icons/info.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#1A1A1A" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="100px" height="100px"><path d="M 24 4 C 12.972066 4 4 12.972074 4 24 C 4 35.027926 12.972066 44 24 44 C 35.027934 44 44 35.027926 44 24 C 44 12.972074 35.027934 4 24 4 z M 24 7 C 33.406615 7 41 14.593391 41 24 C 41 33.406609 33.406615 41 24 41 C 14.593385 41 7 33.406609 7 24 C 7 14.593391 14.593385 7 24 7 z M 24 14 A 2 2 0 0 0 24 18 A 2 2 0 0 0 24 14 z M 23.976562 20.978516 A 1.50015 1.50015 0 0 0 22.5 22.5 L 22.5 33.5 A 1.50015 1.50015 0 1 0 25.5 33.5 L 25.5 22.5 A 1.50015 1.50015 0 0 0 23.976562 20.978516 z"/></svg>
|
||||
|
After Width: | Height: | Size: 606 B |
BIN
web/img/map/1.jpg
Normal file
|
After Width: | Height: | Size: 110 KiB |
3
web/img/sheep.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path fill="#f2bd53" d="M 13 5 C 11.828237 5 10.793477 5.4713326 9.9394531 6.1503906 C 9.6361713 6.0843037 9.3462112 6 9 6 C 6.2504839 6 4 8.2504839 4 11 C 4 11.08059 4.0124881 11.1121 4.0175781 11.181641 C 2.9210459 12.039325 2.1098117 13.257229 2.0175781 14.726562 L 1.1054688 16.552734 A 1.0001165 1.0001165 0 0 0 2.7675781 17.640625 C 3.2526418 18.411838 3.9424236 19.03683 4.7597656 19.449219 L 5 19.675781 L 5 25 A 1.0001 1.0001 0 1 0 7 25 L 7 19.246094 A 1.0001 1.0001 0 0 0 6.6875 18.519531 L 6.046875 17.914062 A 1.0001 1.0001 0 0 0 5.7714844 17.728516 C 4.7470246 17.266071 4.0294077 16.259842 4.0039062 15.064453 A 1.0001 1.0001 0 0 0 4.0039062 14.939453 C 4.0261116 13.839378 4.6387768 12.906535 5.5429688 12.398438 A 1.0001 1.0001 0 0 0 6.0449219 11.394531 C 6.0183372 11.193412 6 11.068889 6 11 C 6 9.3315161 7.3315161 8 9 8 C 9.2766049 8 9.5584058 8.0531018 9.8652344 8.1464844 A 1.0001 1.0001 0 0 0 10.867188 7.8925781 C 11.414334 7.3394013 12.160136 7 13 7 C 13.943745 7 14.765257 7.4389147 15.322266 8.1289062 A 1.0001 1.0001 0 0 0 16.578125 8.3789062 C 17.019891 8.1381558 17.492971 8 18 8 C 19.125679 8 20.081212 8.6201548 20.599609 9.5390625 A 1.0001 1.0001 0 0 0 21.353516 10.039062 C 22.531272 10.179159 23.485346 10.980923 23.841797 12.068359 C 22.824708 11.859395 21.724766 12.146328 20.935547 12.935547 C 19.687547 14.183547 19.687547 16.207078 20.935547 17.455078 C 21.055851 17.575382 21.216262 17.686732 21.376953 17.796875 C 20.836332 18.524365 19.985264 19 19 19 C 18.492971 19 18.019891 18.861844 17.578125 18.621094 A 1.0001 1.0001 0 0 0 16.322266 18.871094 C 15.765471 19.56158 14.944275 20 14 20 C 13.291552 20 12.65587 19.746025 12.128906 19.320312 A 1.0001 1.0001 0 0 0 10.871094 19.320312 C 10.34413 19.746025 9.7084483 20 9 20 A 1.0001 1.0001 0 1 0 9 22 L 9 25 A 1.0001 1.0001 0 1 0 11 25 L 11 21.505859 C 11.170848 21.422563 11.337991 21.33349 11.5 21.236328 C 12.238737 21.67937 13.068046 22 14 22 C 14.730164 22 15.395058 21.803876 16 21.498047 L 16 25 A 1.0001 1.0001 0 1 0 18 25 L 18 20.806641 C 18.316031 20.913616 18.640829 21 19 21 C 19.343981 21 19.67546 20.953638 20 20.886719 L 20 25 A 1.0001 1.0001 0 1 0 22 25 L 22 20 A 1.0001 1.0001 0 0 0 22 19.974609 C 22.486448 19.603854 22.905789 19.152819 23.228516 18.630859 C 24.642494 19.048643 26.176371 19.140035 26.658203 18.658203 C 27.339744 17.976663 26.884402 15.198742 25.958984 13.625 A 1.0001 1.0001 0 0 0 25.966797 13.570312 C 25.987917 13.383949 26 13.19371 26 13 C 26 10.589178 24.248068 8.6335533 21.970703 8.171875 C 21.067822 6.9086944 19.672766 6 18 6 C 17.386577 6 16.866243 6.2341923 16.335938 6.4355469 C 15.448182 5.6006824 14.319642 5 13 5 z M 17.421875 12.279297 C 17.32925 12.296094 17.237609 12.342875 17.162109 12.421875 C 16.614109 12.997875 16 13.891 16 15 C 16 15.567 16.471922 16.023047 17.044922 15.998047 C 17.590922 15.974047 18 15.481547 18 14.935547 L 18 12.763672 C 18 12.435922 17.69975 12.228906 17.421875 12.279297 z M 23 14 C 23.552 14 24 14.448 24 15 C 24 15.552 23.552 16 23 16 C 22.448 16 22 15.552 22 15 C 22 14.448 22.448 14 23 14 z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
223
web/index.html
Normal file
@@ -0,0 +1,223 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
|
||||
<meta
|
||||
name="viewport"
|
||||
content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0"
|
||||
/>
|
||||
|
||||
<!-- Start Single Page Apps for GitHub Pages -->
|
||||
<script>
|
||||
(function(){
|
||||
var redirect = sessionStorage.redirect;
|
||||
delete sessionStorage.redirect;
|
||||
if (redirect && redirect != location.href) {
|
||||
history.replaceState(null, null, redirect);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<!-- End Single Page Apps for GitHub Pages -->
|
||||
|
||||
<title>Sheep Service</title>
|
||||
|
||||
<link rel="icon" href="/img/favicon.ico" sizes="any" />
|
||||
<link rel="apple-touch-icon" href="/img/apple-touch-icon.png" />
|
||||
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#e5e5df"
|
||||
media="(prefers-color-scheme: light)"
|
||||
/>
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#252523"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
/>
|
||||
|
||||
<!-- Конфигурация SW -->
|
||||
<script src="/sw.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/css/main.css" />
|
||||
|
||||
<script src="/config.js" defer></script>
|
||||
|
||||
<script src="/lib/router/router.js" defer></script>
|
||||
<script src="/lib/router/routes.js" defer></script>
|
||||
|
||||
<!-- Подключение стилей Leaflet.js -->
|
||||
<link rel="stylesheet" href="/lib/components/leaflet/leaflet.css" />
|
||||
<script src="/lib/components/leaflet/leaflet.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/lib/components/geoman/leaflet-geoman.css" />
|
||||
<script src="/lib/components/geoman/leaflet-geoman.min.js"></script>
|
||||
|
||||
<script src="/lib/components/turf.min.js" defer></script>
|
||||
|
||||
<script src="/lib/components/clipboard.js" defer></script>
|
||||
<script src="/lib/components/colorGroup.js" defer></script>
|
||||
<script src="/lib/components/makeid.js" defer></script>
|
||||
<script src="/lib/components/swipeUpdater.js" defer></script>
|
||||
<script src="/lib/components/detectBrowser.js" defer></script>
|
||||
<script src="/lib/components/detectOS.js" defer></script>
|
||||
<script src="/lib/components/formattedDate.js" defer></script>
|
||||
|
||||
<script src="/lib/pages/home/script.js" defer></script>
|
||||
<link href="/lib/pages/home/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/territory/script.js" defer></script>
|
||||
<link href="/lib/pages/territory/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/territory_manager/script.js" defer></script>
|
||||
<link href="/lib/pages/territory_manager/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/card/script.js" defer></script>
|
||||
<link href="/lib/pages/card/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/options/script.js" defer></script>
|
||||
<link href="/lib/pages/options/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/sheeps/script.js" defer></script>
|
||||
<link href="/lib/pages/sheeps/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/constructor/script.js" defer></script>
|
||||
<link href="/lib/pages/constructor/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/editor/script.js" defer></script>
|
||||
<link href="/lib/pages/editor/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/stand/script.js" defer></script>
|
||||
<link href="/lib/pages/stand/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/app.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Кнопка сповіщення та оновлення додатків -->
|
||||
<div id="update_banner" data-state="noupdate">
|
||||
<div class="content">
|
||||
<div class="headline"></div>
|
||||
<div class="subhead"></div>
|
||||
<div id="update_banner_icon">
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.0"
|
||||
width="64px"
|
||||
height="64px"
|
||||
viewBox="0 0 128 128"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M64 9.75A54.25 54.25 0 0 0 9.75 64H0a64 64 0 0 1 128 0h-9.75A54.25 54.25 0 0 0 64 9.75z"
|
||||
></path>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 64 64"
|
||||
to="360 64 64"
|
||||
dur="1400ms"
|
||||
repeatCount="indefinite"
|
||||
></animateTransform>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Анімація оновлення сторінки свайпом -->
|
||||
<div id="swipe_updater">
|
||||
<div id="swipe_block">
|
||||
<svg
|
||||
id="swipe_icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
data-state="active"
|
||||
>
|
||||
<path
|
||||
d="M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Меню застосунку -->
|
||||
<div id="navigation">
|
||||
<nav>
|
||||
<li>
|
||||
<div id="nav-home">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M9 2H4C2.897 2 2 2.897 2 4v7c0 1.103.897 2 2 2h5c1.103 0 2-.897 2-2V4C11 2.897 10.103 2 9 2zM20 2h-5c-1.103 0-2 .897-2 2v3c0 1.103.897 2 2 2h5c1.103 0 2-.897 2-2V4C22 2.897 21.103 2 20 2zM9 15H4c-1.103 0-2 .897-2 2v3c0 1.103.897 2 2 2h5c1.103 0 2-.897 2-2v-3C11 15.897 10.103 15 9 15zM20 11h-5c-1.103 0-2 .897-2 2v7c0 1.103.897 2 2 2h5c1.103 0 2-.897 2-2v-7C22 11.897 21.103 11 20 11z"
|
||||
/>
|
||||
</svg>
|
||||
<b>Головна</b>
|
||||
</div>
|
||||
<a href="/" data-route></a>
|
||||
</li>
|
||||
<li id="li-territory" style="display: none;">
|
||||
<div id="nav-territory">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path
|
||||
d="M24 2H14c-.55 0-1 .45-1 1v4l3.6 2.7c.25.19.4.49.4.8V14h8V3C25 2.45 24.55 2 24 2zM15.5 7C15.22 7 15 6.78 15 6.5v-2C15 4.22 15.22 4 15.5 4h2C17.78 4 18 4.22 18 4.5v2C18 6.78 17.78 7 17.5 7h-1.17H15.5zM23 4.5v2C23 6.78 22.78 7 22.5 7h-2C20.22 7 20 6.78 20 6.5v-2C20 4.22 20.22 4 20.5 4h2C22.78 4 23 4.22 23 4.5zM22.5 12h-2c-.28 0-.5-.22-.5-.5v-2C20 9.22 20.22 9 20.5 9h2C22.78 9 23 9.22 23 9.5v2C23 11.78 22.78 12 22.5 12zM1 11.51V27c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V11.51c0-.32-.16-.62-.42-.81l-6-4.28C8.41 6.29 8.2 6.23 8 6.23S7.59 6.29 7.42 6.42l-6 4.28C1.16 10.89 1 11.19 1 11.51zM6.5 20h-2C4.22 20 4 19.78 4 19.5v-2C4 17.22 4.22 17 4.5 17h2C6.78 17 7 17.22 7 17.5v2C7 19.78 6.78 20 6.5 20zM7 22.5v2C7 24.78 6.78 25 6.5 25h-2C4.22 25 4 24.78 4 24.5v-2C4 22.22 4.22 22 4.5 22h2C6.78 22 7 22.22 7 22.5zM6.5 15h-2C4.22 15 4 14.78 4 14.5v-2C4 12.22 4.22 12 4.5 12h2C6.78 12 7 12.22 7 12.5v2C7 14.78 6.78 15 6.5 15zM9.5 17h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2C9.22 20 9 19.78 9 19.5v-2C9 17.22 9.22 17 9.5 17zM9 14.5v-2C9 12.22 9.22 12 9.5 12h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2C9.22 15 9 14.78 9 14.5zM9.5 22h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2C9.22 25 9 24.78 9 24.5v-2C9 22.22 9.22 22 9.5 22zM17 17v10c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V17c0-.55-.45-1-1-1H18C17.45 16 17 16.45 17 17zM19.5 18h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5v-2C19 18.22 19.22 18 19.5 18zM27 18.5v2c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5v-2c0-.28.22-.5.5-.5h2C26.78 18 27 18.22 27 18.5zM26.5 26h-2c-.28 0-.5-.22-.5-.5v-2c0-.28.22-.5.5-.5h2c.28 0 .5.22.5.5v2C27 25.78 26.78 26 26.5 26zM19.5 23h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5v-2C19 23.22 19.22 23 19.5 23z"
|
||||
/>
|
||||
</svg>
|
||||
<b>Території</b>
|
||||
</div>
|
||||
<a href="/territory" data-route></a>
|
||||
</li>
|
||||
<li id="li-sheeps" style="display: none;">
|
||||
<div id="nav-sheeps">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 42.5 14 C 37.813 14 34 18.038 34 23 C 34 27.962 37.813 32 42.5 32 C 47.187 32 51 27.962 51 23 C 51 18.038 47.187 14 42.5 14 z M 21.5 17 C 16.813 17 13 21.038 13 26 C 13 30.962 16.813 35 21.5 35 C 26.187 35 30 30.962 30 26 C 30 21.038 26.187 17 21.5 17 z M 42.5 18 C 44.981 18 47 20.243 47 23 C 47 25.757 44.981 28 42.5 28 C 40.019 28 38 25.757 38 23 C 38 20.243 40.019 18 42.5 18 z M 42.498047 34.136719 C 37.579021 34.136719 33.07724 35.947963 30.054688 38.962891 C 27.67058 37.796576 24.915421 37.136719 22 37.136719 C 14.956 37.136719 8.8129375 40.942422 6.7109375 46.607422 C 5.7409375 49.220422 7.7121406 52 10.494141 52 L 33.505859 52 C 35.43112 52 36.95694 50.674804 37.404297 49 L 53.431641 49 C 56.437641 49 59.121453 45.844281 57.564453 42.613281 C 55.084453 37.463281 49.169047 34.136719 42.498047 34.136719 z M 42.5 38.136719 C 47.565 38.136719 52.171937 40.633609 53.960938 44.349609 C 54.119938 44.687609 53.741687 45 53.429688 45 L 36.544922 45 C 35.777257 43.585465 34.746773 42.317451 33.503906 41.234375 C 35.78496 39.306575 39.034912 38.136719 42.5 38.136719 z"
|
||||
/>
|
||||
</svg>
|
||||
<b>Вісники</b>
|
||||
</div>
|
||||
<a href="/sheeps" data-route></a>
|
||||
</li>
|
||||
<li id="li-schedule" style="display: none;">
|
||||
<div id="nav-schedule">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M47 23c3.314 0 6 2.686 6 6v17c0 3.309-2.691 6-6 6H17c-3.309 0-6-2.691-6-6V29c0-3.314 2.686-6 6-6H47zM22 45v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C21.552 46 22 45.552 22 45zM22 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C21.552 39 22 38.552 22 38zM30 45v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C29.552 46 30 45.552 30 45zM30 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C29.552 39 30 38.552 30 38zM30 31v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C29.552 32 30 31.552 30 31zM38 45v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C37.552 46 38 45.552 38 45zM38 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C37.552 39 38 38.552 38 38zM38 31v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C37.552 32 38 31.552 38 31zM46 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C45.552 39 46 38.552 46 38zM46 31v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C45.552 32 46 31.552 46 31zM17 20c-2.308 0-4.407.876-6 2.305V18c0-3.309 2.691-6 6-6h30c3.309 0 6 2.691 6 6v4.305C51.407 20.876 49.308 20 47 20H17z"
|
||||
/>
|
||||
</svg>
|
||||
<b>Графік зібрань</b>
|
||||
</div>
|
||||
<a href="/schedule" data-route></a>
|
||||
</li>
|
||||
<li id="li-stand" style="display: none;">
|
||||
<div id="nav-stand">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path
|
||||
d="M 6.9707031 4 C 6.8307031 4 6.6807813 4.039375 6.5507812 4.109375 L 2.5507812 6.109375 C 2.0607813 6.349375 1.859375 6.9492188 2.109375 7.4492188 C 2.349375 7.9392188 2.9492187 8.140625 3.4492188 7.890625 L 6.4902344 6.3691406 L 12.5 20.650391 C 12.73 21.180391 13.040156 21.650547 13.410156 22.060547 C 12.040156 22.340547 11 23.56 11 25 C 11 26.65 12.35 28 14 28 C 15.65 28 17 26.65 17 25 C 17 24.52 16.869922 24.070156 16.669922 23.660156 C 17.479922 23.740156 18.319141 23.639062 19.119141 23.289062 L 26.400391 20.099609 C 26.910391 19.889609 27.159219 19.310781 26.949219 18.800781 C 26.749219 18.290781 26.160391 18.040234 25.650391 18.240234 C 25.630391 18.250234 25.619609 18.259531 25.599609 18.269531 L 18.320312 21.460938 C 16.770312 22.130938 14.999609 21.429141 14.349609 19.869141 L 7.9199219 4.609375 C 7.7599219 4.229375 7.3807031 3.99 6.9707031 4 z M 21.359375 8.0605469 C 21.229375 8.0605469 21.100703 8.090625 20.970703 8.140625 L 13.609375 11.269531 C 13.099375 11.479531 12.860078 12.070078 13.080078 12.580078 L 16.029297 19.179688 C 16.249297 19.689688 16.829844 19.930937 17.339844 19.710938 L 24.710938 16.589844 C 25.210938 16.369844 25.450234 15.789297 25.240234 15.279297 L 22.279297 8.6699219 C 22.119297 8.2899219 21.749375 8.0605469 21.359375 8.0605469 z M 14 24 C 14.56 24 15 24.44 15 25 C 15 25.56 14.56 26 14 26 C 13.44 26 13 25.56 13 25 C 13 24.44 13.44 24 14 24 z"
|
||||
/>
|
||||
</svg>
|
||||
<b>Графік стенду</b>
|
||||
</div>
|
||||
<a href="/stand" data-route></a>
|
||||
</li>
|
||||
<li>
|
||||
<div id="nav-options">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 172 172">
|
||||
<path
|
||||
d="M75.18001,14.33333c-3.43283,0 -6.36736,2.42659 -7.02669,5.79492l-2.39355,12.28971c-5.8821,2.22427 -11.32102,5.33176 -16.097,9.25228l-11.78581,-4.05924c-3.2465,-1.118 -6.81841,0.22441 -8.53841,3.19141l-10.80599,18.72852c-1.71283,2.97417 -1.08945,6.74999 1.49772,9.00033l9.44824,8.21647c-0.49137,3.0197 -0.81185,6.09382 -0.81185,9.25228c0,3.15846 0.32048,6.23258 0.81185,9.25228l-9.44824,8.21647c-2.58717,2.25033 -3.21055,6.02616 -1.49772,9.00032l10.80599,18.72852c1.71283,2.97417 5.29191,4.31623 8.53841,3.2054l11.78581,-4.05924c4.77441,3.91806 10.21756,7.01501 16.097,9.23828l2.39355,12.28972c0.65933,3.36833 3.59386,5.79492 7.02669,5.79492h21.63998c3.43283,0 6.36735,-2.42659 7.02669,-5.79492l2.39356,-12.28972c5.88211,-2.22427 11.32102,-5.33176 16.097,-9.25227l11.78581,4.05924c3.2465,1.118 6.81841,-0.21724 8.53841,-3.1914l10.80599,-18.74252c1.71284,-2.97417 1.08945,-6.73599 -1.49772,-8.98633l-9.44824,-8.21647c0.49137,-3.0197 0.81185,-6.09382 0.81185,-9.25228c0,-3.15846 -0.32048,-6.23258 -0.81185,-9.25228l9.44824,-8.21647c2.58717,-2.25033 3.21056,-6.02616 1.49772,-9.00033l-10.80599,-18.72852c-1.71283,-2.97417 -5.29191,-4.31624 -8.53841,-3.2054l-11.78581,4.05924c-4.7744,-3.91806 -10.21755,-7.01501 -16.097,-9.23828l-2.39356,-12.28971c-0.65933,-3.36833 -3.59385,-5.79492 -7.02669,-5.79492zM86,57.33333c15.83117,0 28.66667,12.8355 28.66667,28.66667c0,15.83117 -12.8355,28.66667 -28.66667,28.66667c-15.83117,0 -28.66667,-12.8355 -28.66667,-28.66667c0,-15.83117 12.8355,-28.66667 28.66667,-28.66667z"
|
||||
></path>
|
||||
</svg>
|
||||
<b>Опції</b>
|
||||
</div>
|
||||
<a href="/options" data-route></a>
|
||||
</li>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Блок контенту застосунка -->
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
303
web/js/app.js
Normal file
@@ -0,0 +1,303 @@
|
||||
let socket, username;
|
||||
|
||||
let listEntrances = []
|
||||
let listApartment = []
|
||||
|
||||
let statusD = document.getElementById("status");
|
||||
let hashD = document.getElementById("hash");
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const share_hash = urlParams.get('share_hash');
|
||||
|
||||
let house = urlParams.get('id') ?? 81;
|
||||
let entrance = 1;
|
||||
let group_number = 1;
|
||||
|
||||
const api = "https://sheep-service.com";
|
||||
const wss = "wss://sheep-service.com/ws";
|
||||
|
||||
// let color_status = [
|
||||
// "#000000",
|
||||
// "#C16917",
|
||||
// "#b10202",
|
||||
// "#3d3d3d",
|
||||
// "#11734b",
|
||||
// "#6cc5fc",
|
||||
// "#5a3286"
|
||||
// ];
|
||||
|
||||
// let color_status = [
|
||||
// ["#ffffff", "#000000"],
|
||||
// ["#e7af32", "#ffffff"],
|
||||
// ["#fc2a2a", "#ffffff"],
|
||||
// ["#3d3d3d", "#ffffff"],
|
||||
// ["#11a568", "#ffffff"],
|
||||
// ["#6cc5fc", "#ffffff"],
|
||||
// ["#b381eb", "#ffffff"]
|
||||
// ];
|
||||
|
||||
let color_status = [
|
||||
["#ffffff", "#eaebef"],
|
||||
["#fbf1e0", "#ff8300"],
|
||||
["#fce3e2", "#ff0000"],
|
||||
["#e2e0e0", "#3d3d3d"],
|
||||
["#d5e9dd", "#11a568"],
|
||||
["#d7ebfa", "#3fb4fc"],
|
||||
["#e8dbf5", "#b381eb"]
|
||||
];
|
||||
|
||||
function makeid(length) {
|
||||
let result = '';
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const charactersLength = characters.length;
|
||||
let counter = 0;
|
||||
while (counter < length) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
counter += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function start(name) {
|
||||
if (!hash) return;
|
||||
|
||||
|
||||
hashD.innerText = `HASH: ${name}`
|
||||
username = name;
|
||||
|
||||
socket = new WebSocket(`${wss}?share_hash=${share_hash}`);
|
||||
|
||||
let user_hash = localStorage.getItem('hash') ?? share_hash;
|
||||
|
||||
socket.onopen = function (e) {
|
||||
console.log("[WebSocket | open] Соединение установлено");
|
||||
statusD.innerText = "WebSocket | open";
|
||||
|
||||
const message = {
|
||||
event: 'connection',
|
||||
id: getTimeInSeconds(),
|
||||
date: getTimeInSeconds(),
|
||||
hash: user_hash,
|
||||
username: name,
|
||||
data: {
|
||||
id: 1,
|
||||
entrance_id: 1,
|
||||
apartment_number: "1",
|
||||
group_number: 1,
|
||||
status: 1,
|
||||
description: "",
|
||||
created_at: 1727541827,
|
||||
updated_at: 1727541827
|
||||
}
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(message))
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data)
|
||||
|
||||
if (data.event == 'connection') {
|
||||
if (data.username == username) return
|
||||
|
||||
console.log(`Добавлен новый пользователь по имени ${data.username}`);
|
||||
} else if (data.event == 'message') {
|
||||
update(data);
|
||||
|
||||
if (data.username == username) return
|
||||
|
||||
console.log(`${data.username} пишет: `, data.data);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
if (event.wasClean) {
|
||||
statusD.innerText = "WebSocket | close"
|
||||
console.log(`[WebSocket | close] Соединение закрыто чисто, код=${event.code} причина=${event.reason}`);
|
||||
} else {
|
||||
statusD.innerText = "WebSocket | close"
|
||||
console.log('[WebSocket | close] Соединение прервано');
|
||||
|
||||
// setTimeout(function() {
|
||||
// start(username);
|
||||
// }, 1000);
|
||||
|
||||
const result = confirm(`З'єднання розірвано! Перепідключитись?`);
|
||||
if (result) {
|
||||
getEntrances();
|
||||
start(username);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.log(`[WebSocket | error]`);
|
||||
statusD.innerText = "WebSocket | error"
|
||||
};
|
||||
}
|
||||
|
||||
function mess(entrance_number, id, date_type) {
|
||||
console.log(id, listApartment[entrance_number]);
|
||||
|
||||
const pos = listApartment[entrance_number].map(e => e.id).indexOf(id);
|
||||
let apartment = listApartment[entrance_number][pos];
|
||||
|
||||
console.log(pos, apartment);
|
||||
|
||||
|
||||
let status = document.getElementById(`status_${id}`);
|
||||
let description = document.getElementById(`description_${id}`);
|
||||
let date = new Date(document.getElementById(`date_${id}`).value);
|
||||
const timestamp = date.getTime();
|
||||
|
||||
apartment.description = description.value;
|
||||
apartment.status = Number(status.value);
|
||||
apartment.updated_at = date_type ? getTimeInSeconds(timestamp) : getTimeInSeconds(),
|
||||
|
||||
|
||||
status.style.backgroundColor = color_status[status.value][0];
|
||||
status.style.color = color_status[status.value][1];
|
||||
status.style.border = `1px solid ${color_status[status.value][1]}`;
|
||||
|
||||
let user_hash = localStorage.getItem('hash') ?? share_hash;
|
||||
|
||||
let message = {
|
||||
event: 'message',
|
||||
id: getTimeInSeconds(),
|
||||
date: getTimeInSeconds(),
|
||||
hash: user_hash,
|
||||
username: username,
|
||||
data: {
|
||||
id: apartment.id,
|
||||
entrance_id: apartment.entrance_id,
|
||||
apartment_number: apartment.apartment_number,
|
||||
group_number: apartment.group_number,
|
||||
status: apartment.status,
|
||||
description: apartment.description,
|
||||
updated_at: apartment.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(message));
|
||||
|
||||
sort(apartment.id, apartment.entrance_id);
|
||||
}
|
||||
|
||||
function update(message) {
|
||||
if (!document.getElementById(`status_${message.data.id}`)) return;
|
||||
|
||||
let now = new Date(message.data.updated_at);
|
||||
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
|
||||
|
||||
document.getElementById(`status_${message.data.id}`).style.backgroundColor = color_status[message.data.status][0];
|
||||
document.getElementById(`status_${message.data.id}`).style.color = color_status[message.data.status][1];
|
||||
document.getElementById(`status_${message.data.id}`).style.border = `1px solid ${color_status[message.data.status][1]}`;
|
||||
|
||||
document.getElementById(`status_${message.data.id}`).value = message.data.status;
|
||||
document.getElementById(`description_${message.data.id}`).value = message.data.description;
|
||||
document.getElementById(`date_${message.data.id}`).value = now.toISOString().slice(0, 16);
|
||||
}
|
||||
|
||||
function getEntrances(house_id = house) {
|
||||
let url = `${api}/api/house/${house_id}/entrances?share_hash=${share_hash}`;
|
||||
fetch(url)
|
||||
.then(function (response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
listEntrances = data;
|
||||
document.getElementById('list').innerHTML = "";
|
||||
|
||||
for (let i = 0; i < listEntrances.length; i++) {
|
||||
const element = listEntrances[i];
|
||||
|
||||
let status = () => {
|
||||
if (element.history.name == "Групова") return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M 12 1 C 9.1277778 1 6.7189086 3.0461453 6.1230469 5.7871094 L 8.078125 6.2128906 C 8.4822632 4.3538547 10.072222 3 12 3 C 14.27619 3 16 4.7238095 16 7 L 16 8 L 6 8 C 4.9069372 8 4 8.9069372 4 10 L 4 20 C 4 21.093063 4.9069372 22 6 22 L 18 22 C 19.093063 22 20 21.093063 20 20 L 20 10 C 20 8.9069372 19.093063 8 18 8 L 18 7 C 18 3.6761905 15.32381 1 12 1 z M 6 10 L 18 10 L 18 20 L 6 20 L 6 10 z M 12 13 C 10.9 13 10 13.9 10 15 C 10 16.1 10.9 17 12 17 C 13.1 17 14 16.1 14 15 C 14 13.9 13.1 13 12 13 z"/></svg>'
|
||||
else return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M 12 1 C 8.6761905 1 6 3.6761905 6 7 L 6 8 C 4.9069372 8 4 8.9069372 4 10 L 4 20 C 4 21.093063 4.9069372 22 6 22 L 18 22 C 19.093063 22 20 21.093063 20 20 L 20 10 C 20 8.9069372 19.093063 8 18 8 L 18 7 C 18 3.6761905 15.32381 1 12 1 z M 12 3 C 14.27619 3 16 4.7238095 16 7 L 16 8 L 8 8 L 8 7 C 8 4.7238095 9.7238095 3 12 3 z M 6 10 L 18 10 L 18 20 L 6 20 L 6 10 z M 12 13 C 10.9 13 10 13.9 10 15 C 10 16.1 10.9 17 12 17 C 13.1 17 14 16.1 14 15 C 14 13.9 13.1 13 12 13 z"/></svg>'
|
||||
}
|
||||
|
||||
document.getElementById('list').innerHTML += `
|
||||
<details ${element.history.name == "Групова" ? "open" : "disabled"}>
|
||||
<summary>
|
||||
<p>${element.title}</p>
|
||||
${status()}
|
||||
</summary>
|
||||
<div id="apartments_${element.id}" class="apartments_list">
|
||||
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
getApartment(element.id, element.entrance_number);
|
||||
|
||||
console.log(element);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function getApartment(entrance_id, entrance_number) {
|
||||
let url = `${api}/api/apartment/${entrance_id}?share_hash=${share_hash}`;
|
||||
fetch(url)
|
||||
.then(function (response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
listApartment[entrance_number] = data;
|
||||
|
||||
data.sort((a, b) => a.apartment_number - b.apartment_number);
|
||||
|
||||
data.sort((a, b) => a.updated_at - b.updated_at);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
|
||||
let now = new Date(element.updated_at);
|
||||
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
|
||||
now = now.toISOString().slice(0, 16)
|
||||
|
||||
document.getElementById(`apartments_${entrance_id}`).innerHTML += `
|
||||
<div id="card_${element.id}">
|
||||
<span>кв.${element.title}</span>
|
||||
<select id="status_${element.id}" onchange="mess(${entrance_number}, ${element.id})" style="background-color: ${color_status[element.status][0]}; color: ${color_status[element.status][1]}; border: 1px solid ${color_status[element.status][1]};" ${element.status == 2 ? "disabled" : ""}>
|
||||
<option value="0" ${element.status == 0 ? "selected" : ""}></option>
|
||||
<option value="1" ${element.status == 1 ? "selected" : ""}>Відмова</option>
|
||||
<option value="2" ${element.status == 2 ? "selected" : ""}>Не заходити (Груба відмова)</option>
|
||||
<option value="3" ${element.status == 3 ? "selected" : ""}>Нема домофона</option>
|
||||
<option value="4" ${element.status == 4 ? "selected" : ""}>Повторна відвідина</option>
|
||||
<option value="5" ${element.status == 5 ? "selected" : ""}>Немає вдома</option>
|
||||
<option value="6" ${element.status == 6 ? "selected" : ""}>Свідки Єгови</option>
|
||||
</select>
|
||||
<input onchange="mess(${entrance_number}, ${element.id})" type="text" name="description" id="description_${element.id}" placeholder="Нотатки..." value="${element.description ?? ""}" ${element.status == 2 ? "disabled" : ""}>
|
||||
<input onchange="mess(${entrance_number}, ${element.id}, true)" type="datetime-local" name="date" id="date_${element.id}" placeholder="Дата" value="${element.updated_at ? now : ""}" ${element.status == 2 ? "disabled" : ""} style="max-width: 170px;">
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function sort(id, entrance_id) {
|
||||
let child = document.getElementById(`card_${id}`);
|
||||
|
||||
document.getElementById(`apartments_${entrance_id}`).removeChild(child);
|
||||
|
||||
document.getElementById(`apartments_${entrance_id}`).append(child);
|
||||
|
||||
child.style.border = "1px solid var(--PrimaryColor)";
|
||||
}
|
||||
|
||||
function getTimeInSeconds(time = Date.now()) {
|
||||
// Если время больше 10 знаков (это значит, что время в миллисекундах)
|
||||
if (time.toString().length < 10) {
|
||||
// Округляем до секунд, убирая последние 3 цифры (миллисекунды)
|
||||
time = Math.floor(time * 1000);
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
getEntrances();
|
||||
start(makeid(6));
|
||||
|
||||
|
||||
116
web/lib/app.js
Normal file
@@ -0,0 +1,116 @@
|
||||
let USER = {};
|
||||
|
||||
// Определение ID главного блока
|
||||
let app = document.getElementById('app');
|
||||
|
||||
// Конфигурация роутера
|
||||
Router.config({ mode: 'history' });
|
||||
|
||||
async function appReload() {
|
||||
|
||||
location.reload();
|
||||
|
||||
// Router.navigate(window.location.pathname, false).check();
|
||||
|
||||
// // Закрытие старого соединения WebSocket
|
||||
// if (socket) socket.close(1000, "Перезапуск соединения");
|
||||
// listEntrances = []
|
||||
// listApartment = []
|
||||
}
|
||||
|
||||
const Rotation = async () => {
|
||||
const result = confirm(`Ви бажаєте провести ротацію територій між групами?`);
|
||||
|
||||
if (result) {
|
||||
let rotationButton = document.getElementById('rotationButton-title');
|
||||
rotationButton.innerText = "Зачекайте";
|
||||
|
||||
let uuid = localStorage.getItem("uuid");
|
||||
|
||||
const URL = `${CONFIG.api}rotation`;
|
||||
await fetch(URL, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json, text/plain, */*",
|
||||
"Authorization": uuid
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
rotationButton.innerText = 'Ротація завершилась успішно!';
|
||||
|
||||
Territory.house.setHTML();
|
||||
Territory.homestead.setHTML();
|
||||
|
||||
setTimeout(() => {
|
||||
rotationButton.innerText = "Провести ротацію";
|
||||
}, 3000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
rotationButton.innerText = "Помилка ротації!";
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Функция загрузки приложения
|
||||
window.addEventListener('load', async function () {
|
||||
console.log('[OS] ', detectOS());
|
||||
if (window.matchMedia('(display-mode: standalone)').matches) {
|
||||
if (detectOS() == 'Android') {
|
||||
document.getElementById('navigation').dataset.state = '';
|
||||
} else if (detectOS() == 'iOS') {
|
||||
document.getElementById('navigation').dataset.state = 'ios';
|
||||
localStorage.setItem('backToTop', 'false');
|
||||
} else if (detectOS() == 'MacOS') {
|
||||
document.getElementById('navigation').dataset.state = 'ios';
|
||||
localStorage.setItem('backToTop', 'false');
|
||||
} else {
|
||||
document.getElementById('navigation').dataset.state = '';
|
||||
}
|
||||
}
|
||||
|
||||
let userInput = () => {
|
||||
let h = prompt("Введіть ваше посилання з UUID:");
|
||||
if (h) {
|
||||
h = h.replace("https://sheep-service.com/?uuid=", "");
|
||||
h = h.replace("https://sheep-service.com?uuid=", "");
|
||||
h = h.replace("https://sheep-service.com?/hash=", "");
|
||||
h = h.replace("https://sheep-service.com?hash=", "");
|
||||
|
||||
localStorage.setItem("uuid", h)
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get('uuid')) {
|
||||
localStorage.setItem("uuid", urlParams.get('uuid'))
|
||||
}
|
||||
if (urlParams.get('hash')) {
|
||||
localStorage.setItem("uuid", urlParams.get('hash'))
|
||||
}
|
||||
|
||||
let uuid = localStorage.getItem("uuid") ? localStorage.getItem("uuid") : userInput();
|
||||
if (uuid) {
|
||||
const URL = `${CONFIG.api}auth`;
|
||||
USER = await fetch(URL, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json, text/plain, */*",
|
||||
"Authorization": uuid
|
||||
}
|
||||
}).then((response) => response.json());
|
||||
|
||||
console.log("USER Info: ", USER);
|
||||
|
||||
|
||||
if (USER.administrator.uuid || USER.moderator.uuid) document.getElementById("li-sheeps").style.display = "";
|
||||
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_add_schedule)) document.getElementById("li-schedule").style.display = "";
|
||||
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_manager_territory)) document.getElementById("li-territory").style.display = "";
|
||||
if (USER.administrator.uuid || USER.can_view_stand) document.getElementById("li-stand").style.display = "";
|
||||
}
|
||||
|
||||
Router.check().listen();
|
||||
});
|
||||
5
web/lib/components/clipboard.js
Normal file
@@ -0,0 +1,5 @@
|
||||
clipboard = (text) => {
|
||||
navigator.clipboard.writeText(text)
|
||||
.then(() => console.log("Текст скопійовано!"))
|
||||
.catch(err => console.error(err))
|
||||
}
|
||||
20
web/lib/components/colorGroup.js
Normal file
@@ -0,0 +1,20 @@
|
||||
let colorGroup = (number) => {
|
||||
switch (number) {
|
||||
case 1:
|
||||
return "#c1ae4d"
|
||||
case 2:
|
||||
return "#93c14d"
|
||||
case 3:
|
||||
return "#4dc1a7"
|
||||
case 4:
|
||||
return "#4d90c1"
|
||||
case 5:
|
||||
return "#654dc1"
|
||||
case 6:
|
||||
return "#c14db7"
|
||||
case 7:
|
||||
return "#c1734d"
|
||||
default:
|
||||
return "#C14D4D"
|
||||
}
|
||||
}
|
||||
35
web/lib/components/detectBrowser.js
Normal file
@@ -0,0 +1,35 @@
|
||||
detectBrowser = () => {
|
||||
const userAgent = navigator.userAgent;
|
||||
let browser = "unkown";
|
||||
// Detect browser name
|
||||
browser = (/ucbrowser/i).test(userAgent) ? 'UCBrowser' : browser;
|
||||
browser = (/edg/i).test(userAgent) ? 'Edge' : browser;
|
||||
browser = (/googlebot/i).test(userAgent) ? 'GoogleBot' : browser;
|
||||
browser = (/chromium/i).test(userAgent) ? 'Chromium' : browser;
|
||||
browser = (/firefox|fxios/i).test(userAgent) && !(/seamonkey/i).test(userAgent) ? 'Firefox' : browser;
|
||||
browser = (/; msie|trident/i).test(userAgent) && !(/ucbrowser/i).test(userAgent) ? 'IE' : browser;
|
||||
browser = (/chrome|crios/i).test(userAgent) && !(/opr|opera|chromium|edg|ucbrowser|googlebot/i).test(userAgent) ? 'Chrome' : browser;;
|
||||
browser = (/safari/i).test(userAgent) && !(/chromium|edg|ucbrowser|chrome|crios|opr|opera|fxios|firefox/i).test(userAgent) ? 'Safari' : browser;
|
||||
browser = (/opr|opera/i).test(userAgent) ? 'Opera' : browser;
|
||||
|
||||
// detect browser version
|
||||
switch (browser) {
|
||||
case 'UCBrowser': return `${browser}/${browserVersion(userAgent,/(ucbrowser)\/([\d\.]+)/i)}`;
|
||||
case 'Edge': return `${browser}/${browserVersion(userAgent,/(edge|edga|edgios|edg)\/([\d\.]+)/i)}`;
|
||||
case 'GoogleBot': return `${browser}/${browserVersion(userAgent,/(googlebot)\/([\d\.]+)/i)}`;
|
||||
case 'Chromium': return `${browser}/${browserVersion(userAgent,/(chromium)\/([\d\.]+)/i)}`;
|
||||
case 'Firefox': return `${browser}/${browserVersion(userAgent,/(firefox|fxios)\/([\d\.]+)/i)}`;
|
||||
case 'Chrome': return `${browser}/${browserVersion(userAgent,/(chrome|crios)\/([\d\.]+)/i)}`;
|
||||
case 'Safari': return `${browser}/${browserVersion(userAgent,/(safari)\/([\d\.]+)/i)}`;
|
||||
case 'Opera': return `${browser}/${browserVersion(userAgent,/(opera|opr)\/([\d\.]+)/i)}`;
|
||||
case 'IE': const version = browserVersion(userAgent,/(trident)\/([\d\.]+)/i);
|
||||
// IE version is mapped using trident version
|
||||
// IE/8.0 = Trident/4.0, IE/9.0 = Trident/5.0
|
||||
return version ? `${browser}/${parseFloat(version) + 4.0}` : `${browser}/7.0`;
|
||||
default: return `unknown/0.0.0.0`;
|
||||
}
|
||||
}
|
||||
|
||||
browserVersion = (userAgent,regex) => {
|
||||
return userAgent.match(regex) ? userAgent.match(regex)[2] : null;
|
||||
}
|
||||
12
web/lib/components/detectOS.js
Normal file
@@ -0,0 +1,12 @@
|
||||
function detectOS() {
|
||||
const platform = navigator.platform.toLowerCase(),
|
||||
iosPlatforms = ['iphone', 'ipad', 'ipod', 'ipod touch'];
|
||||
|
||||
if (platform.includes('mac')) return 'MacOS';
|
||||
if (iosPlatforms.includes(platform)) return 'iOS';
|
||||
if (platform.includes('win')) return 'Windows';
|
||||
if (/android/.test(navigator.userAgent.toLowerCase())) return 'Android';
|
||||
if (/linux/.test(platform)) return 'Linux';
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
9
web/lib/components/formattedDate.js
Normal file
@@ -0,0 +1,9 @@
|
||||
let formattedDate = (unix_timestamp) => {
|
||||
if(!unix_timestamp) return
|
||||
|
||||
let date = new Date(unix_timestamp);
|
||||
let year = date.getFullYear() >= 10 ? date.getFullYear() : "0" + date.getFullYear();
|
||||
let month = (date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1);
|
||||
let weekday = date.getDate() >= 10 ? date.getDate() : "0" + date.getDate();
|
||||
return weekday + '.' + month + '.' + year;
|
||||
}
|
||||
283
web/lib/components/geoman/leaflet-geoman.css
Normal file
@@ -0,0 +1,283 @@
|
||||
.marker-icon,
|
||||
.marker-icon:focus {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #3388ff;
|
||||
border-radius: 50%;
|
||||
margin: -4px 0 0 -4px !important;
|
||||
width: 6px !important;
|
||||
height: 6px !important;
|
||||
outline: 0;
|
||||
transition: opacity ease 0.3s;
|
||||
}
|
||||
|
||||
.marker-icon-middle,
|
||||
.marker-icon-middle:focus {
|
||||
opacity: 0.7;
|
||||
margin: -6px 0 0 -6px !important;
|
||||
width: 10px !important;
|
||||
height: 10px !important;
|
||||
}
|
||||
|
||||
.leaflet-pm-draggable {
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
.cursor-marker {
|
||||
cursor: crosshair;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cursor-marker.visible {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.leaflet-pm-invalid {
|
||||
stroke: red;
|
||||
transition: fill ease 0s, stroke ease 0s;
|
||||
}
|
||||
|
||||
.rect-style-marker,
|
||||
.rect-start-marker {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.rect-style-marker.visible,
|
||||
.rect-start-marker.visible {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.vertexmarker-disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.pm-text-marker {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.pm-textarea {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
resize: none;
|
||||
border: none;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
padding-left: 7px;
|
||||
padding-bottom: 0;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.leaflet-pm-draggable .pm-textarea {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.pm-textarea:focus,
|
||||
.pm-textarea:focus-within,
|
||||
.pm-textarea:focus-visible,
|
||||
.pm-textarea:active {
|
||||
border: 2px solid #000;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.pm-textarea.pm-disabled {
|
||||
border: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pm-textarea.pm-hasfocus {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar {
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar .leaflet-buttons-control-button {
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar
|
||||
.leaflet-pm-actions-container
|
||||
a.leaflet-pm-action:first-child:not(.pos-right),
|
||||
.leaflet-pm-toolbar
|
||||
.leaflet-pm-actions-container
|
||||
a.leaflet-pm-action:last-child.pos-right {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar .button-container a.leaflet-buttons-control-button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar
|
||||
.button-container:last-child
|
||||
a.leaflet-buttons-control-button {
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar
|
||||
.button-container:first-child
|
||||
a.leaflet-buttons-control-button {
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar
|
||||
.button-container:last-child
|
||||
a.leaflet-buttons-control-button {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar .control-fa-icon {
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar .control-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-marker {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjUgKDY3NDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5BdG9tcy9JY29ucy9Ub29scy9NYXJrZXI8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBkPSJNMTUuNSwyNC44NzgyOTU5IEMxNS4yOTA5MjAxLDI0Ljg3NzIyMTkgMTUuMTc0NDg1NywyNC44NDY3ODE3IDE0LjY1OTA4NjYsMjQuMjM1NDE2MyBDMTAuMjE5Njk1NSwxOS40MTE4MDU0IDgsMTUuNTAxNDM5MiA4LDEyLjUwNDMxNzcgQzgsOC4zNTk3OTc0NiAxMS4zNTc4NjQ0LDUgMTUuNSw1IEMxOS42NDIxMzU2LDUgMjMsOC4zNTk3OTc0NiAyMywxMi41MDQzMTc3IEMyMywxNyAxOC4yODc4MjE3LDIxLjkyNjgzNzggMTYuMzMzNjYwMSwyNC4yNDQwMTg2IEMxNS44MjI0NjIyLDI0Ljg1MDE4MDIgMTUuNzA5MDc5OSwyNC44NzkzNjk5IDE1LjUsMjQuODc4Mjk1OSBaIE0xNS41LDE1LjUzMjY5NDggQzE3LjI3NTIwMSwxNS41MzI2OTQ4IDE4LjcxNDI4NTcsMTQuMTE4MDAwNCAxOC43MTQyODU3LDEyLjM3Mjg4NjQgQzE4LjcxNDI4NTcsMTAuNjI3NzcyMyAxNy4yNzUyMDEsOS4yMTMwNzc5MiAxNS41LDkuMjEzMDc3OTIgQzEzLjcyNDc5OSw5LjIxMzA3NzkyIDEyLjI4NTcxNDMsMTAuNjI3NzcyMyAxMi4yODU3MTQzLDEyLjM3Mjg4NjQgQzEyLjI4NTcxNDMsMTQuMTE4MDAwNCAxMy43MjQ3OTksMTUuNTMyNjk0OCAxNS41LDE1LjUzMjY5NDggWiIgaWQ9InBhdGgtMSI+PC9wYXRoPgogICAgPC9kZWZzPgogICAgPGcgaWQ9IlN5bWJvbHMiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJBdG9tcy9JY29ucy9Ub29scy9NYXJrZXIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zLjAwMDAwMCwgLTMuMDAwMDAwKSI+CiAgICAgICAgICAgIDxtYXNrIGlkPSJtYXNrLTIiIGZpbGw9IndoaXRlIj4KICAgICAgICAgICAgICAgIDx1c2UgeGxpbms6aHJlZj0iI3BhdGgtMSI+PC91c2U+CiAgICAgICAgICAgIDwvbWFzaz4KICAgICAgICAgICAgPHVzZSBpZD0iTWFzayIgZmlsbD0iIzVCNUI1QiIgZmlsbC1ydWxlPSJub256ZXJvIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-polygon {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPGRlZnM+CiAgICA8cGF0aCBpZD0icG9seWdvbi1hIiBkPSJNMTkuNDIwNjg5Miw5LjE2NTA5NzI1IEMxOS4xNTIzNjgxLDguNjY5OTI5MTQgMTksOC4xMDI3NTgzMSAxOSw3LjUgQzE5LDUuNTY3MDAzMzggMjAuNTY3MDAzNCw0IDIyLjUsNCBDMjQuNDMyOTk2Niw0IDI2LDUuNTY3MDAzMzggMjYsNy41IEMyNiw5LjI2MzIzNTk1IDI0LjY5NjE0NzEsMTAuNzIxOTQwNyAyMywxMC45NjQ1NTU2IEwyMywxOS4wMzU0NDQ0IEMyNC42OTYxNDcxLDE5LjI3ODA1OTMgMjYsMjAuNzM2NzY0IDI2LDIyLjUgQzI2LDI0LjQzMjk5NjYgMjQuNDMyOTk2NiwyNiAyMi41LDI2IEMyMC43MzY3NjQsMjYgMTkuMjc4MDU5MywyNC42OTYxNDcxIDE5LjAzNTQ0NDQsMjMgTDEwLjk2NDU1NTYsMjMgQzEwLjcyMTk0MDcsMjQuNjk2MTQ3MSA5LjI2MzIzNTk1LDI2IDcuNSwyNiBDNS41NjcwMDMzOCwyNiA0LDI0LjQzMjk5NjYgNCwyMi41IEM0LDIwLjU2NzAwMzQgNS41NjcwMDMzOCwxOSA3LjUsMTkgQzguMTAyNzU4MzEsMTkgOC42Njk5MjkxNCwxOS4xNTIzNjgxIDkuMTY1MDk3MjUsMTkuNDIwNjg5MiBMMTkuNDIwNjg5Miw5LjE2NTA5NzI1IFogTTIwLjgzNDkwNzMsMTAuNTc5MzA2MyBMMTAuNTc5MzEwOCwyMC44MzQ5MDI3IEMxMC42MDg2NzMxLDIwLjg4OTA4ODggMTAuNjM2NjQ2OSwyMC45NDQxMzcyIDEwLjY2MzE4NDQsMjEgTDE5LjMzNjgxNTYsMjEgQzE5LjY4MjU3NzUsMjAuMjcyMTU0IDIwLjI3MjE1NCwxOS42ODI1Nzc1IDIxLDE5LjMzNjgxNTYgTDIxLDEwLjY2MzE4NDQgQzIwLjk0NDEzNzIsMTAuNjM2NjQ2OSAyMC44ODkwODg4LDEwLjYwODY3MzEgMjAuODM0OTAyNywxMC41NzkzMTA4IFogTTIyLjUsOSBDMjMuMzI4NDI3MSw5IDI0LDguMzI4NDI3MTIgMjQsNy41IEMyNCw2LjY3MTU3Mjg4IDIzLjMyODQyNzEsNiAyMi41LDYgQzIxLjY3MTU3MjksNiAyMSw2LjY3MTU3Mjg4IDIxLDcuNSBDMjEsOC4zMjg0MjcxMiAyMS42NzE1NzI5LDkgMjIuNSw5IFogTTIyLjUsMjQgQzIzLjMyODQyNzEsMjQgMjQsMjMuMzI4NDI3MSAyNCwyMi41IEMyNCwyMS42NzE1NzI5IDIzLjMyODQyNzEsMjEgMjIuNSwyMSBDMjEuNjcxNTcyOSwyMSAyMSwyMS42NzE1NzI5IDIxLDIyLjUgQzIxLDIzLjMyODQyNzEgMjEuNjcxNTcyOSwyNCAyMi41LDI0IFogTTcuNSwyNCBDOC4zMjg0MjcxMiwyNCA5LDIzLjMyODQyNzEgOSwyMi41IEM5LDIxLjY3MTU3MjkgOC4zMjg0MjcxMiwyMSA3LjUsMjEgQzYuNjcxNTcyODgsMjEgNiwyMS42NzE1NzI5IDYsMjIuNSBDNiwyMy4zMjg0MjcxIDYuNjcxNTcyODgsMjQgNy41LDI0IFoiLz4KICA8L2RlZnM+CiAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMyAtMykiPgogICAgPG1hc2sgaWQ9InBvbHlnb24tYiIgZmlsbD0iI2ZmZiI+CiAgICAgIDx1c2UgeGxpbms6aHJlZj0iI3BvbHlnb24tYSIvPgogICAgPC9tYXNrPgogICAgPHVzZSBmaWxsPSIjNUI1QjVCIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHhsaW5rOmhyZWY9IiNwb2x5Z29uLWEiLz4KICAgIDxnIGZpbGw9IiM1QjVCNUIiIG1hc2s9InVybCgjcG9seWdvbi1iKSI+CiAgICAgIDxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIvPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg==);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-polyline {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPGRlZnM+CiAgICA8cGF0aCBpZD0ibGluZS1hIiBkPSJNOS4xNjUwOTcyNSwxOS40MjA2ODkyIEwxOC40MjA2ODkyLDEwLjE2NTA5NzMgQzE4LjE1MjM2ODEsOS42Njk5MjkxNCAxOCw5LjEwMjc1ODMxIDE4LDguNSBDMTgsNi41NjcwMDMzOCAxOS41NjcwMDM0LDUgMjEuNSw1IEMyMy40MzI5OTY2LDUgMjUsNi41NjcwMDMzOCAyNSw4LjUgQzI1LDEwLjQzMjk5NjYgMjMuNDMyOTk2NiwxMiAyMS41LDEyIEMyMC44OTcyNDE3LDEyIDIwLjMzMDA3MDksMTEuODQ3NjMxOSAxOS44MzQ5MDI3LDExLjU3OTMxMDggTDEwLjU3OTMxMDgsMjAuODM0OTAyNyBDMTAuODQ3NjMxOSwyMS4zMzAwNzA5IDExLDIxLjg5NzI0MTcgMTEsMjIuNSBDMTEsMjQuNDMyOTk2NiA5LjQzMjk5NjYyLDI2IDcuNSwyNiBDNS41NjcwMDMzOCwyNiA0LDI0LjQzMjk5NjYgNCwyMi41IEM0LDIwLjU2NzAwMzQgNS41NjcwMDMzOCwxOSA3LjUsMTkgQzguMTAyNzU4MzEsMTkgOC42Njk5MjkxNCwxOS4xNTIzNjgxIDkuMTY1MDk3MjUsMTkuNDIwNjg5MiBaIE0yMS41LDEwIEMyMi4zMjg0MjcxLDEwIDIzLDkuMzI4NDI3MTIgMjMsOC41IEMyMyw3LjY3MTU3Mjg4IDIyLjMyODQyNzEsNyAyMS41LDcgQzIwLjY3MTU3MjksNyAyMCw3LjY3MTU3Mjg4IDIwLDguNSBDMjAsOS4zMjg0MjcxMiAyMC42NzE1NzI5LDEwIDIxLjUsMTAgWiBNNy41LDI0IEM4LjMyODQyNzEyLDI0IDksMjMuMzI4NDI3MSA5LDIyLjUgQzksMjEuNjcxNTcyOSA4LjMyODQyNzEyLDIxIDcuNSwyMSBDNi42NzE1NzI4OCwyMSA2LDIxLjY3MTU3MjkgNiwyMi41IEM2LDIzLjMyODQyNzEgNi42NzE1NzI4OCwyNCA3LjUsMjQgWiIvPgogIDwvZGVmcz4KICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zIC0zKSI+CiAgICA8bWFzayBpZD0ibGluZS1iIiBmaWxsPSIjZmZmIj4KICAgICAgPHVzZSB4bGluazpocmVmPSIjbGluZS1hIi8+CiAgICA8L21hc2s+CiAgICA8dXNlIGZpbGw9IiM1QjVCNUIiIGZpbGwtcnVsZT0ibm9uemVybyIgeGxpbms6aHJlZj0iI2xpbmUtYSIvPgogICAgPGcgZmlsbD0iIzVCNUI1QiIgbWFzaz0idXJsKCNsaW5lLWIpIj4KICAgICAgPHJlY3Qgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-circle {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjUgKDY3NDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5BdG9tcy9JY29ucy9Ub29scy9DaXJjbGU8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBkPSJNMTguMjg5Nzc1MSw2Ljc4NjAyMjc1IEMxOC44OTI0MTMxLDYuMjk0NjQ5ODEgMTkuNjYxNzk3LDYgMjAuNSw2IEMyMi40MzI5OTY2LDYgMjQsNy41NjcwMDMzOCAyNCw5LjUgQzI0LDEwLjMzODIwMyAyMy43MDUzNTAyLDExLjEwNzU4NjkgMjMuMjEzOTc3MiwxMS43MTAyMjQ5IEMyMy43MTk1OTksMTIuODcxMjA1MyAyNCwxNC4xNTI4NTcxIDI0LDE1LjUgQzI0LDIwLjc0NjcwNTEgMTkuNzQ2NzA1MSwyNSAxNC41LDI1IEM5LjI1MzI5NDg4LDI1IDUsMjAuNzQ2NzA1MSA1LDE1LjUgQzUsMTAuMjUzMjk0OSA5LjI1MzI5NDg4LDYgMTQuNSw2IEMxNS44NDcxNDI5LDYgMTcuMTI4Nzk0Nyw2LjI4MDQwMDk4IDE4LjI4OTc3NTEsNi43ODYwMjI3NSBaIE0xNy4xNTA0MjI4LDguNDgxNzU4NiBDMTYuMzI2MzU4MSw4LjE3MDM5MjM2IDE1LjQzMzA3NzcsOCAxNC41LDggQzEwLjM1Nzg2NDQsOCA3LDExLjM1Nzg2NDQgNywxNS41IEM3LDE5LjY0MjEzNTYgMTAuMzU3ODY0NCwyMyAxNC41LDIzIEMxOC42NDIxMzU2LDIzIDIyLDE5LjY0MjEzNTYgMjIsMTUuNSBDMjIsMTQuNTY2OTIyMyAyMS44Mjk2MDc2LDEzLjY3MzY0MTkgMjEuNTE4MjQxNCwxMi44NDk1NzcyIEMyMS4xOTYwMzgzLDEyLjk0NzM5NjggMjAuODU0MTYyMiwxMyAyMC41LDEzIEMxOC41NjcwMDM0LDEzIDE3LDExLjQzMjk5NjYgMTcsOS41IEMxNyw5LjE0NTgzNzc4IDE3LjA1MjYwMzIsOC44MDM5NjE2OSAxNy4xNTA0MjI4LDguNDgxNzU4NiBaIE0xNC41LDE3IEMxMy42NzE1NzI5LDE3IDEzLDE2LjMyODQyNzEgMTMsMTUuNSBDMTMsMTQuNjcxNTcyOSAxMy42NzE1NzI5LDE0IDE0LjUsMTQgQzE1LjMyODQyNzEsMTQgMTYsMTQuNjcxNTcyOSAxNiwxNS41IEMxNiwxNi4zMjg0MjcxIDE1LjMyODQyNzEsMTcgMTQuNSwxNyBaIE0yMC41LDExIEMyMS4zMjg0MjcxLDExIDIyLDEwLjMyODQyNzEgMjIsOS41IEMyMiw4LjY3MTU3Mjg4IDIxLjMyODQyNzEsOCAyMC41LDggQzE5LjY3MTU3MjksOCAxOSw4LjY3MTU3Mjg4IDE5LDkuNSBDMTksMTAuMzI4NDI3MSAxOS42NzE1NzI5LDExIDIwLjUsMTEgWiIgaWQ9InBhdGgtMSI+PC9wYXRoPgogICAgPC9kZWZzPgogICAgPGcgaWQ9IlN5bWJvbHMiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJBdG9tcy9JY29ucy9Ub29scy9DaXJjbGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zLjAwMDAwMCwgLTMuMDAwMDAwKSI+CiAgICAgICAgICAgIDxtYXNrIGlkPSJtYXNrLTIiIGZpbGw9IndoaXRlIj4KICAgICAgICAgICAgICAgIDx1c2UgeGxpbms6aHJlZj0iI3BhdGgtMSI+PC91c2U+CiAgICAgICAgICAgIDwvbWFzaz4KICAgICAgICAgICAgPHVzZSBpZD0iTWFzayIgZmlsbD0iIzVCNUI1QiIgZmlsbC1ydWxlPSJub256ZXJvIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgPGcgaWQ9IkF0b21zL0NvbG9yL0dyZXkiIG1hc2s9InVybCgjbWFzay0yKSIgZmlsbD0iIzVCNUI1QiI+CiAgICAgICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiB4PSIwIiB5PSIwIiB3aWR0aD0iMzAiIGhlaWdodD0iMzAiPjwvcmVjdD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-circle-marker {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KCjxzdmcgdmlld0JveD0iMCAwIDEwMCAxMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjNUI1QjVCIiBzdHJva2Utd2lkdGg9IjgiCiAgICAgZmlsbD0ibm9uZSI+CjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjM1Ii8+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iMyIgZmlsbD0iIzVCNUI1QiIvPgo8L3N2Zz4=);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-rectangle {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPGRlZnM+CiAgICA8cGF0aCBpZD0icmVjdGFuZ2xlLWEiIGQ9Ik0yMywxMC45NjQ1NTU2IEwyMywxOS4wMzU0NDQ0IEMyNC42OTYxNDcxLDE5LjI3ODA1OTMgMjYsMjAuNzM2NzY0IDI2LDIyLjUgQzI2LDI0LjQzMjk5NjYgMjQuNDMyOTk2NiwyNiAyMi41LDI2IEMyMC43MzY3NjQsMjYgMTkuMjc4MDU5MywyNC42OTYxNDcxIDE5LjAzNTQ0NDQsMjMgTDEwLjk2NDU1NTYsMjMgQzEwLjcyMTk0MDcsMjQuNjk2MTQ3MSA5LjI2MzIzNTk1LDI2IDcuNSwyNiBDNS41NjcwMDMzOCwyNiA0LDI0LjQzMjk5NjYgNCwyMi41IEM0LDIwLjczNjc2NCA1LjMwMzg1MjkzLDE5LjI3ODA1OTMgNywxOS4wMzU0NDQ0IEw3LDEwLjk2NDU1NTYgQzUuMzAzODUyOTMsMTAuNzIxOTQwNyA0LDkuMjYzMjM1OTUgNCw3LjUgQzQsNS41NjcwMDMzOCA1LjU2NzAwMzM4LDQgNy41LDQgQzkuMjYzMjM1OTUsNCAxMC43MjE5NDA3LDUuMzAzODUyOTMgMTAuOTY0NTU1Niw3IEwxOS4wMzU0NDQ0LDcgQzE5LjI3ODA1OTMsNS4zMDM4NTI5MyAyMC43MzY3NjQsNCAyMi41LDQgQzI0LjQzMjk5NjYsNCAyNiw1LjU2NzAwMzM4IDI2LDcuNSBDMjYsOS4yNjMyMzU5NSAyNC42OTYxNDcxLDEwLjcyMTk0MDcgMjMsMTAuOTY0NTU1NiBaIE0yMSwxMC42NjMxODQ0IEMyMC4yNzIxNTQsMTAuMzE3NDIyNSAxOS42ODI1Nzc1LDkuNzI3ODQ1OTggMTkuMzM2ODE1Niw5IEwxMC42NjMxODQ0LDkgQzEwLjMxNzQyMjUsOS43Mjc4NDU5OCA5LjcyNzg0NTk4LDEwLjMxNzQyMjUgOSwxMC42NjMxODQ0IEw5LDE5LjMzNjgxNTYgQzkuNzI3ODQ1OTgsMTkuNjgyNTc3NSAxMC4zMTc0MjI1LDIwLjI3MjE1NCAxMC42NjMxODQ0LDIxIEwxOS4zMzY4MTU2LDIxIEMxOS42ODI1Nzc1LDIwLjI3MjE1NCAyMC4yNzIxNTQsMTkuNjgyNTc3NSAyMSwxOS4zMzY4MTU2IEwyMSwxMC42NjMxODQ0IFogTTcuNSw5IEM4LjMyODQyNzEyLDkgOSw4LjMyODQyNzEyIDksNy41IEM5LDYuNjcxNTcyODggOC4zMjg0MjcxMiw2IDcuNSw2IEM2LjY3MTU3Mjg4LDYgNiw2LjY3MTU3Mjg4IDYsNy41IEM2LDguMzI4NDI3MTIgNi42NzE1NzI4OCw5IDcuNSw5IFogTTIyLjUsOSBDMjMuMzI4NDI3MSw5IDI0LDguMzI4NDI3MTIgMjQsNy41IEMyNCw2LjY3MTU3Mjg4IDIzLjMyODQyNzEsNiAyMi41LDYgQzIxLjY3MTU3MjksNiAyMSw2LjY3MTU3Mjg4IDIxLDcuNSBDMjEsOC4zMjg0MjcxMiAyMS42NzE1NzI5LDkgMjIuNSw5IFogTTIyLjUsMjQgQzIzLjMyODQyNzEsMjQgMjQsMjMuMzI4NDI3MSAyNCwyMi41IEMyNCwyMS42NzE1NzI5IDIzLjMyODQyNzEsMjEgMjIuNSwyMSBDMjEuNjcxNTcyOSwyMSAyMSwyMS42NzE1NzI5IDIxLDIyLjUgQzIxLDIzLjMyODQyNzEgMjEuNjcxNTcyOSwyNCAyMi41LDI0IFogTTcuNSwyNCBDOC4zMjg0MjcxMiwyNCA5LDIzLjMyODQyNzEgOSwyMi41IEM5LDIxLjY3MTU3MjkgOC4zMjg0MjcxMiwyMSA3LjUsMjEgQzYuNjcxNTcyODgsMjEgNiwyMS42NzE1NzI5IDYsMjIuNSBDNiwyMy4zMjg0MjcxIDYuNjcxNTcyODgsMjQgNy41LDI0IFoiLz4KICA8L2RlZnM+CiAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMyAtMykiPgogICAgPG1hc2sgaWQ9InJlY3RhbmdsZS1iIiBmaWxsPSIjZmZmIj4KICAgICAgPHVzZSB4bGluazpocmVmPSIjcmVjdGFuZ2xlLWEiLz4KICAgIDwvbWFzaz4KICAgIDx1c2UgZmlsbD0iIzVCNUI1QiIgZmlsbC1ydWxlPSJub256ZXJvIiB4bGluazpocmVmPSIjcmVjdGFuZ2xlLWEiLz4KICAgIDxnIGZpbGw9IiM1QjVCNUIiIG1hc2s9InVybCgjcmVjdGFuZ2xlLWIpIj4KICAgICAgPHJlY3Qgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-delete {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjUgKDY3NDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5BdG9tcy9JY29ucy9Ub29scy9FcmFzZXI8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBkPSJNMTcuNzg3NDIxOSwxOC40ODEyNTUyIEwxMS42NDgwMDc5LDEzLjM0OTgxODQgTDYuNDA0NjYwMDksMTkuMzgxNjAwMSBMMTAuNTUzOTE1NiwyMi45ODg0OTI5IEwxMy44NjkzNCwyMi45ODg0OTI5IEwxNy43ODc0MjE5LDE4LjQ4MTI1NTIgWiBNMTYuNTA3NDI1MiwyMi45ODg0OTI5IEwyNi4wMDAwMDAyLDIyLjk4ODQ5MjkgTDI2LjAwMDAwMDIsMjQuOTg4NDkyOSBMMTAuMDAwMDAwMiwyNC45ODg0OTI5IEw5LjgwNzA4MzEzLDI0Ljk4ODQ5MjkgTDUuMDkyNTQyMDQsMjAuODkxMDE5MiBDNC4yNTg5MTI4NSwyMC4xNjYzNTY0IDQuMTcwNTc4MTQsMTguOTAzMTExMiA0Ljg5NTI0MDkzLDE4LjA2OTQ4MiBMMTYuMDQ4MjQ0NCw1LjIzOTQxOTE2IEMxNi43NzI5MDcyLDQuNDA1Nzg5OTggMTguMDM2MTUyNSw0LjMxNzQ1NTI2IDE4Ljg2OTc4MTYsNS4wNDIxMTgwNiBMMjQuOTA3NDU4MywxMC4yOTA1OTAzIEMyNS43NDEwODc1LDExLjAxNTI1MzEgMjUuODI5NDIyMiwxMi4yNzg0OTgzIDI1LjEwNDc1OTQsMTMuMTEyMTI3NSBMMTYuNTA3NDI1MiwyMi45ODg0OTI5IFoiIGlkPSJwYXRoLTEiPjwvcGF0aD4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJTeW1ib2xzIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iQXRvbXMvSWNvbnMvVG9vbHMvRXJhc2VyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMy4wMDAwMDAsIC0zLjAwMDAwMCkiPgogICAgICAgICAgICA8bWFzayBpZD0ibWFzay0yIiBmaWxsPSJ3aGl0ZSI+CiAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPgogICAgICAgICAgICA8L21hc2s+CiAgICAgICAgICAgIDx1c2UgaWQ9IkNvbWJpbmVkLVNoYXBlIiBmaWxsPSIjNUI1QjVCIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-edit {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPGRlZnM+CiAgICA8cGF0aCBpZD0iZWRpdF9hbmNob3ItYSIgZD0iTTEzLjUsMTEgQzExLjU2NzAwMzQsMTEgMTAsOS40MzI5OTY2MiAxMCw3LjUgQzEwLDUuNTY3MDAzMzggMTEuNTY3MDAzNCw0IDEzLjUsNCBDMTUuNDMyOTk2Niw0IDE3LDUuNTY3MDAzMzggMTcsNy41IEMxNyw5LjQzMjk5NjYyIDE1LjQzMjk5NjYsMTEgMTMuNSwxMSBaIE0xMy41LDkgQzE0LjMyODQyNzEsOSAxNSw4LjMyODQyNzEyIDE1LDcuNSBDMTUsNi42NzE1NzI4OCAxNC4zMjg0MjcxLDYgMTMuNSw2IEMxMi42NzE1NzI5LDYgMTIsNi42NzE1NzI4OCAxMiw3LjUgQzEyLDguMzI4NDI3MTIgMTIuNjcxNTcyOSw5IDEzLjUsOSBaIE0xMi4wMDAyODg5LDcuNTI5NzM4OTMgQzEyLjAxMjU5ODMsOC4xNjI3MzY3MiAxMi40MTcwMTk3LDguNjk5NjY0MyAxMi45ODA3MTExLDguOTA3Njc5NjYgTDMsMTUgTDMsMTMgTDEyLjAwMDI4ODksNy41Mjk3Mzg5MyBaIE0xNC4yMTcyNzIyLDYuMTgyMjg0NzIgTDE5LjQ1MzEyNSwzIEwyMi42NTg5MzU1LDMgTDE0Ljk4OTEwMiw3LjY4MTczODg1IEMxNC45OTYyOTcxLDcuNjIyMTY0NTkgMTUsNy41NjE1MTQ3MiAxNSw3LjUgQzE1LDYuOTMxMzgzODEgMTQuNjgzNjA5OCw2LjQzNjY2NDUgMTQuMjE3MjcyMiw2LjE4MjI4NDcyIFogTTIzLjQ0MzQwNDIsMTkuMjg1MTczNiBMMjAuMTI4Mjc5OSwxOS4yODUxNzM2IEwyMS44NzI5OTgzLDIzLjUzNDk1MjUgQzIxLjk5NDUyOTYsMjMuODI5NTc3MyAyMS44NTU2NTQ2LDI0LjE1OTkyMDkgMjEuNTc3ODczNCwyNC4yODQ5MjA4IEwyMC4wNDE0Njc1LDI0Ljk1NDUxNDIgQzE5Ljc1NTA2MTMsMjUuMDc5NTE0MSAxOS40MzM4NzM4LDI0LjkzNjY3MDQgMTkuMzEyMzQyNiwyNC42NTA5NTE4IEwxNy42NTQ0MzY3LDIwLjYxNTQ1NDEgTDE0Ljk0NjE4NzMsMjMuNDAxMDE1MSBDMTQuNTg1MjgxMSwyMy43NzIxNzExIDE0LDIzLjQ4NjA0NjMgMTQsMjIuOTk5MjY1MyBMMTQsOS41NzE4MzUzMyBDMTQsOS4wNTkzMzU2MSAxNC42MjI1MzExLDguODA5NDkyIDE0Ljk0NjE1Niw5LjE3MDA4NTU1IEwyMy44MzQwMjkyLDE4LjMxMjAxNzkgQzI0LjE5MjUyOTEsMTguNjYxMzYxNSAyMy45Mjc5OTc5LDE5LjI4NTE3MzYgMjMuNDQzNDA0MiwxOS4yODUxNzM2IFoiLz4KICA8L2RlZnM+CiAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMyAtMykiPgogICAgPG1hc2sgaWQ9ImVkaXRfYW5jaG9yLWIiIGZpbGw9IiNmZmYiPgogICAgICA8dXNlIHhsaW5rOmhyZWY9IiNlZGl0X2FuY2hvci1hIi8+CiAgICA8L21hc2s+CiAgICA8dXNlIGZpbGw9IiM1QjVCNUIiIGZpbGwtcnVsZT0ibm9uemVybyIgeGxpbms6aHJlZj0iI2VkaXRfYW5jaG9yLWEiLz4KICAgIDxnIGZpbGw9IiM1QjVCNUIiIG1hc2s9InVybCgjZWRpdF9hbmNob3ItYikiPgogICAgICA8cmVjdCB3aWR0aD0iMzAiIGhlaWdodD0iMzAiLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-drag {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPGRlZnM+CiAgICA8cGF0aCBpZD0ibW92ZS1hIiBkPSJNMjEsMTQgTDIxLDEwIEwyNywxNSBMMjEsMjAgTDIxLDE2IEwxNiwxNiBMMTYsMjEgTDIwLDIxIEwxNSwyNyBMMTAsMjEgTDE0LDIxIEwxNCwxNiBMOSwxNiBMOSwyMCBMMywxNSBMOSwxMCBMOSwxNCBMMTQsMTQgTDE0LDkgTDEwLDkgTDE1LDMgTDIwLDkgTDE2LDkgTDE2LDE0IEwyMSwxNCBaIi8+CiAgPC9kZWZzPgogIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMgLTMpIj4KICAgIDxtYXNrIGlkPSJtb3ZlLWIiIGZpbGw9IiNmZmYiPgogICAgICA8dXNlIHhsaW5rOmhyZWY9IiNtb3ZlLWEiLz4KICAgIDwvbWFzaz4KICAgIDx1c2UgZmlsbD0iI0Q4RDhEOCIgeGxpbms6aHJlZj0iI21vdmUtYSIvPgogICAgPGcgZmlsbD0iIzVCNUI1QiIgbWFzaz0idXJsKCNtb3ZlLWIpIj4KICAgICAgPHJlY3Qgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-cut {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjUgKDY3NDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5BdG9tcy9JY29ucy9Ub29scy9TY2lzc29yczwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPgogICAgICAgIDxwYXRoIGQ9Ik0xMi45NjkxNTc0LDEzLjQ5Mzk0MzUgTDIxLjAzMTcwMzIsNS41NDE2NzAxMyBMMjMuNDY0OTQ5OSw1LjY3NzIyOTU3IEwxNy4wNDcwNzEzLDE0LjUxMDY4MTYgTDI3LjU2NjAzMzYsMTcuMTMzMzUzNSBMMjUuNzg5MTk0NCwxOC44MDEyNTg4IEwxNC41ODU0OTUxLDE3Ljg5ODc1MDYgTDEzLjY0ODc5NTUsMTkuMTg4MDA3IEMxMy43OTQ2MzksMTkuMjY1MDk1OCAxMy45MzY3OTg1LDE5LjM1MzQ0MTcgMTQuMDc0MTM3NywxOS40NTMyMjQ1IEMxNS42Mzc5NjQ4LDIwLjU4OTQxMTQgMTUuOTg0NjM1NywyMi43NzgyMDUyIDE0Ljg0ODQ0ODgsMjQuMzQyMDMyNCBDMTMuNzEyMjYxOSwyNS45MDU4NTk1IDExLjUyMzQ2ODEsMjYuMjUyNTMwNCA5Ljk1OTY0MDk2LDI1LjExNjM0MzUgQzguMzk1ODEzODQsMjMuOTgwMTU2NSA4LjA0OTE0Mjk2LDIxLjc5MTM2MjcgOS4xODUzMjk4NiwyMC4yMjc1MzU2IEM5Ljc0NTg3Mjc2LDE5LjQ1NjAxNDUgMTAuNTYyNjE4OCwxOC45ODA3NDc1IDExLjQzNDEyMTgsMTguODMzNjQwNyBMMTIuNjgwNTY1NiwxNy4xMTgwNTc5IEwxMi41MjM5NzI0LDE2LjM3NDcyMTYgTDExLjk1MDY5MzIsMTUuMzAxMjM5MSBMOS44OTMxMDY0NiwxNC43ODgyMjUxIEM5LjEzMDkzNzk2LDE1LjIzNTcyNjEgOC4xOTk3Nzg1NCwxNS4zOTY2NDQ3IDcuMjc0NDUzNTUsMTUuMTY1OTM1MiBDNS4zOTg4NzUxOSwxNC42OTgzMDEgNC4yNTc1MTA5NCwxMi43OTg3NTE5IDQuNzI1MTQ1MTUsMTAuOTIzMTczNiBDNS4xOTI3NzkzNSw5LjA0NzU5NTE5IDcuMDkyMzI4NDYsNy45MDYyMzA5NCA4Ljk2NzkwNjgyLDguMzczODY1MTUgQzEwLjg0MzQ4NTIsOC44NDE0OTkzNSAxMS45ODQ4NDk0LDEwLjc0MTA0ODUgMTEuNTE3MjE1MiwxMi42MTY2MjY4IEMxMS40NzYxNDY0LDEyLjc4MTM0NDkgMTEuNDI0MDMzNSwxMi45NDA0MDAxIDExLjM2MTg2MjcsMTMuMDkzMTk5OSBMMTIuOTY5MTU3NCwxMy40OTM5NDM1IFogTTcuNzU4Mjk3MzUsMTMuMjI1MzQzOCBDOC41NjIxMTY2NCwxMy40MjU3NTg0IDkuMzc2MjA5MTIsMTIuOTM2NjAyMyA5LjU3NjYyMzc4LDEyLjEzMjc4MyBDOS43NzcwMzg0NCwxMS4zMjg5NjM3IDkuMjg3ODgyMzMsMTAuNTE0ODcxMyA4LjQ4NDA2MzAzLDEwLjMxNDQ1NjYgQzcuNjgwMjQzNzMsMTAuMTE0MDQxOSA2Ljg2NjE1MTI2LDEwLjYwMzE5OCA2LjY2NTczNjYsMTEuNDA3MDE3MyBDNi40NjUzMjE5NCwxMi4yMTA4MzY2IDYuOTU0NDc4MDUsMTMuMDI0OTI5MSA3Ljc1ODI5NzM1LDEzLjIyNTM0MzggWiBNMTAuODAzMzYzOSwyMS40MDMxMDYxIEMxMC4zMTY0MjY2LDIyLjA3MzMxNzcgMTAuNDY0OTk5OCwyMy4wMTEzNzIyIDExLjEzNTIxMTUsMjMuNDk4MzA5NSBDMTEuODA1NDIzMSwyMy45ODUyNDY3IDEyLjc0MzQ3NzYsMjMuODM2NjczNSAxMy4yMzA0MTQ4LDIzLjE2NjQ2MTkgQzEzLjcxNzM1MjEsMjIuNDk2MjUwMiAxMy41Njg3Nzg4LDIxLjU1ODE5NTcgMTIuODk4NTY3MiwyMS4wNzEyNTg1IEMxMi4yMjgzNTU2LDIwLjU4NDMyMTIgMTEuMjkwMzAxMSwyMC43MzI4OTQ1IDEwLjgwMzM2MzksMjEuNDAzMTA2MSBaIiBpZD0icGF0aC0xIj48L3BhdGg+CiAgICA8L2RlZnM+CiAgICA8ZyBpZD0iU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IkF0b21zL0ljb25zL1Rvb2xzL1NjaXNzb3JzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMy4wMDAwMDAsIC0zLjAwMDAwMCkiPgogICAgICAgICAgICA8bWFzayBpZD0ibWFzay0yIiBmaWxsPSJ3aGl0ZSI+CiAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPgogICAgICAgICAgICA8L21hc2s+CiAgICAgICAgICAgIDx1c2UgaWQ9Ik1hc2siIGZpbGw9IiM1QjVCNUIiIGZpbGwtcnVsZT0ibm9uemVybyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTYuMDkzMTk0LCAxNS42NjMzNTEpIHJvdGF0ZSgtMzIuMDAwMDAwKSB0cmFuc2xhdGUoLTE2LjA5MzE5NCwgLTE1LjY2MzM1MSkgIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-snapping {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDU3LjEgKDgzMDg4KSAtIGh0dHBzOi8vc2tldGNoLmNvbSAtLT4KICAgIDx0aXRsZT5BdG9tcy9JY29ucy9Ub29scy9NYWduZXQ8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBkPSJNMjEuOTk5NDc1OSwxMC45NDI4MTgzIEwyMS45OTk5OTg1LDE2LjM3MTA0MTcgQzIyLDE2LjY4NzIwMDcgMjIsMTcuMDA1ODI3OCAyMiwxNy4zMjY5NDExIEMyMiwyMS41NjQ2NTQ1IDE4LjY0MjEzNTYsMjUgMTQuNSwyNSBDMTAuMzU3ODY0NCwyNSA3LDIxLjU2NDY1NDUgNywxNy4zMjY5NDExIEw3LjAwMDg3NTA4LDEwLjk5MDc1MDcgTDExLjAwMjI4MDgsMTAuOTk4NDEyNSBDMTEuMDAxNzAzMywxMS42OTgwMTE0IDExLjAwMTI0NywxMi40MTY4MjQ4IDExLjAwMDg5OTIsMTMuMTU1NDg4NyBMMTEsMTcuMzI2OTQxMSBDMTEsMTkuMzc1NjgwOSAxMi41ODc2ODQxLDIxIDE0LjUsMjEgQzE2LjQxMjMxNTksMjEgMTgsMTkuMzc1NjgwOSAxOCwxNy4zMjY5NDExIEMxOCwxNS4wNzAyMDMyIDE3Ljk5OTU2OTYsMTIuOTYxOTY2OCAxNy45OTg1MzksMTAuOTkxMDAzMiBMMjEuOTk5NDc1OSwxMC45NDI4MTgzIFogTTEwLDcgQzEwLjU1MjI4NDcsNyAxMSw3LjQ0NzcxNTI1IDExLDggTDExLDEwIEw3LDEwIEw3LDggQzcsNy40NDc3MTUyNSA3LjQ0NzcxNTI1LDcgOCw3IEwxMCw3IFogTTIxLDcgQzIxLjU1MjI4NDcsNyAyMiw3LjQ0NzcxNTI1IDIyLDggTDIyLDEwIEwxOCwxMCBMMTgsOCBDMTgsNy40NDc3MTUyNSAxOC40NDc3MTUzLDcgMTksNyBMMjEsNyBaIiBpZD0icGF0aC0xIj48L3BhdGg+CiAgICA8L2RlZnM+CiAgICA8ZyBpZD0iU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IkF0b21zL0ljb25zL1Rvb2xzL01hZ25ldCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMuMDAwMDAwLCAtMy4wMDAwMDApIj4KICAgICAgICAgICAgPG1hc2sgaWQ9Im1hc2stMiIgZmlsbD0id2hpdGUiPgogICAgICAgICAgICAgICAgPHVzZSB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgPC9tYXNrPgogICAgICAgICAgICA8dXNlIGlkPSJNYXNrIiBmaWxsPSIjNUI1QjVCIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0LjUwMDAwMCwgMTYuMDAwMDAwKSByb3RhdGUoNDUuMDAwMDAwKSB0cmFuc2xhdGUoLTE0LjUwMDAwMCwgLTE2LjAwMDAwMCkgIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-rotate {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBpZD0icm90YXRlIiBkPSJNMjEuMiw1LjhjLTAuMS0wLjItMC4yLTAuMy0wLjMtMC41bC0wLjEtMC4yYy0wLjEtMC4yLTAuMi0wLjMtMC4zLTAuNWwtMC4xLTAuMmMtMC4xLTAuMi0wLjItMC4zLTAuNC0wLjVsLTAuMi0wLjNsMi44LTMuMUwxOCwwLjZsLTQuNiwwLjFsMC41LDQuNWwwLjUsNC41bDMuMi0zLjZ2MC4xbDAuMSwwLjJjMC4xLDAuMSwwLjEsMC4yLDAuMiwwLjJsMC4xLDAuMkMxOCw3LDE4LDcuMSwxOC4xLDcuMmMwLjMsMC43LDAuNiwxLjQsMC43LDIuMWMwLjIsMS40LDAsMi45LTAuNiw0LjJMMTgsMTMuOUwxNy45LDE0bC0wLjMsMC41bC0wLjEsMC4yYy0wLjIsMC4yLTAuNCwwLjUtMC42LDAuN2MtMC41LDAuNS0xLjEsMS0xLjcsMS4zYy0wLjYsMC40LTEuMywwLjYtMi4xLDAuOGMtMC43LDAuMS0xLjUsMC4yLTIuMiwwLjFjLTAuOC0wLjEtMS41LTAuMy0yLjItMC41Yy0wLjctMC4zLTEuMy0wLjctMS45LTEuMmwtMC40LTAuNGwtMC4yLTAuM0w2LDE1Yy0wLjEtMC4xLTAuMi0wLjItMC4yLTAuM2wtMC4zLTAuNGwtMC4xLTAuMWwtMC4yLTAuNGMwLTAuMS0wLjEtMC4xLTAuMS0wLjJsLTAuMy0wLjVsLTAuMS0wLjJjLTAuMS0wLjMtMC4yLTAuNi0wLjMtMC45Yy0wLjItMC44LTAuMy0xLjYtMC4zLTIuNGMwLTAuMiwwLTAuMywwLTAuNVY4LjljMC0wLjIsMC0wLjMsMC4xLTAuNGwwLjEtMC42bDAuMi0wLjZjMC4zLTAuOCwwLjctMS41LDEuMi0yLjJjMC41LTAuNywxLjEtMS4zLDEuOC0xLjhjMC4yLTAuMSwwLjMtMC40LDAuMS0wLjZDNy41LDIuNiw3LjQsMi41LDcuMywyLjVINy4xTDcsMi42QzYuMSwzLDUuNCwzLjYsNC43LDQuMkM0LDQuOSwzLjUsNS43LDMsNi42Yy0wLjksMS44LTEuMiwzLjgtMC44LDUuOGMwLjEsMC41LDAuMiwwLjksMC4zLDEuNGwwLjMsMC44QzIuOSwxNC43LDMsMTQuOCwzLDE1bDAuMiwwLjRjMCwwLjEsMC4xLDAuMiwwLjEsMC4ybDAuMywwLjVjMC4xLDAuMiwwLjIsMC4zLDAuMywwLjVsMC4xLDAuMmMwLjEsMC4xLDAuMiwwLjMsMC4zLDAuNEw1LDE3LjhjMC43LDAuNywxLjYsMS4zLDIuNSwxLjhjMC45LDAuNSwxLjksMC44LDMsMC45YzAuNSwwLjEsMSwwLjEsMS41LDAuMWMwLjYsMCwxLjEsMCwxLjYtMC4xYzEtMC4yLDIuMS0wLjUsMy0xbDAuMi0wLjFjMC4yLTAuMSwwLjMtMC4yLDAuNS0wLjNsMC43LTAuNGMwLjItMC4xLDAuMy0wLjIsMC40LTAuM2wwLjItMC4yYzAuMi0wLjEsMC40LTAuMywwLjUtMC41bDAuMS0wLjFjMC4zLTAuMywwLjctMC43LDAuOS0xbDAuNi0wLjlsMC40LTAuNmMxLTEuOSwxLjQtNC4xLDEuMS02LjJDMjIsNy44LDIxLjcsNi43LDIxLjIsNS44eiIvPgogICAgPC9kZWZzPgogICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDIpIj4KICAgICAgICA8bWFzayBpZD0icm90YXRlLWIiIGZpbGw9IiNmZmYiPgogICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNyb3RhdGUiLz4KICAgICAgICA8L21hc2s+CiAgICAgICAgPHVzZSBmaWxsPSIjNUI1QjVCIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHhsaW5rOmhyZWY9IiNyb3RhdGUiLz4KICAgICAgICA8ZyBmaWxsPSIjNUI1QjVCIiBtYXNrPSJ1cmwoI3JvdGF0ZS1iKSI+CiAgICAgICAgICAgIDxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIvPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+Cg==);
|
||||
}
|
||||
.leaflet-pm-toolbar .leaflet-pm-icon-text {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7c3Ryb2tlOiM1YjViNWI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS13aWR0aDoyLjVweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlRleHQ8L3RpdGxlPjxnIGlkPSJFYmVuZV8yIiBkYXRhLW5hbWU9IkViZW5lIDIiPjxwb2x5bGluZSBjbGFzcz0iY2xzLTEiIHBvaW50cz0iMTkuNjQgNy4yNyAxOS42NCA0IDEyIDQgMTIgMjAgMTUuOTEgMjAgOC4wOSAyMCAxMiAyMCAxMiA0IDQuMzYgNCA0LjM2IDcuMjciLz48L2c+PC9zdmc+);
|
||||
}
|
||||
|
||||
.leaflet-buttons-control-button:hover,
|
||||
.leaflet-buttons-control-button:focus {
|
||||
cursor: pointer;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.active > .leaflet-buttons-control-button {
|
||||
box-shadow: inset 0 -1px 5px 2px rgba(81, 77, 77, 0.31);
|
||||
}
|
||||
|
||||
.leaflet-buttons-control-text-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.button-container .leaflet-pm-actions-container {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
display: none;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.leaflet-right
|
||||
.leaflet-pm-toolbar
|
||||
.button-container
|
||||
.leaflet-pm-actions-container {
|
||||
right: 100%;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.button-container.active .leaflet-pm-actions-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.button-container
|
||||
.leaflet-pm-actions-container:not(.pos-right)
|
||||
a.leaflet-pm-action:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
border-right: 0;
|
||||
}
|
||||
.button-container
|
||||
.leaflet-pm-actions-container.pos-right
|
||||
a.leaflet-pm-action:first-child {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.button-container
|
||||
.leaflet-pm-actions-container.pos-right
|
||||
a.leaflet-pm-action:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
.button-container .leaflet-pm-actions-container .leaflet-pm-action {
|
||||
padding: 0 10px;
|
||||
background-color: #666;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
border-right: 1px solid #eee;
|
||||
user-select: none;
|
||||
border-bottom: none;
|
||||
height: 29px;
|
||||
line-height: 29px;
|
||||
}
|
||||
.leaflet-pm-toolbar
|
||||
.button-container:first-child.pos-right.active
|
||||
a.leaflet-buttons-control-button {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
.leaflet-pm-toolbar
|
||||
.button-container:first-child.active:not(.pos-right)
|
||||
a.leaflet-buttons-control-button {
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.button-container .leaflet-pm-actions-container .leaflet-pm-action:hover,
|
||||
.button-container .leaflet-pm-actions-container .leaflet-pm-action:focus {
|
||||
cursor: pointer;
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
/* That the active control is always over the other controls */
|
||||
.leaflet-pm-toolbar.activeChild {
|
||||
z-index: 801;
|
||||
}
|
||||
|
||||
.leaflet-buttons-control-button.pm-disabled {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.leaflet-buttons-control-button.pm-disabled > .control-icon {
|
||||
filter: opacity(0.6);
|
||||
}
|
||||
1
web/lib/components/geoman/leaflet-geoman.min.js
vendored
Normal file
BIN
web/lib/components/leaflet/images/layers-2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
web/lib/components/leaflet/images/layers.png
Normal file
|
After Width: | Height: | Size: 696 B |
BIN
web/lib/components/leaflet/images/marker-icon-2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
web/lib/components/leaflet/images/marker-icon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
web/lib/components/leaflet/images/marker-shadow.png
Normal file
|
After Width: | Height: | Size: 618 B |
14419
web/lib/components/leaflet/leaflet-src.esm.js
Normal file
1
web/lib/components/leaflet/leaflet-src.esm.js.map
Normal file
14512
web/lib/components/leaflet/leaflet-src.js
Normal file
1
web/lib/components/leaflet/leaflet-src.js.map
Normal file
666
web/lib/components/leaflet/leaflet.css
Normal file
@@ -0,0 +1,666 @@
|
||||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container img.leaflet-tile {
|
||||
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||
mix-blend-mode: plus-lighter;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #4CAF50;
|
||||
border: 1px solid #666;
|
||||
width: 7px !important;
|
||||
height: 7px !important;
|
||||
margin-left: -5.5px !important;
|
||||
margin-top: -5.5px !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||