Compare commits
20 Commits
93f0c71a6c
...
feat/syste
Author | SHA256 | Date | |
---|---|---|---|
165c79e67b | |||
c0ac71ebba | |||
10e8e14cd6 | |||
a9c8525e6e | |||
39c07fcef0 | |||
52d521a6ad | |||
d3848ac1aa | |||
d04bdb3ac1 | |||
51112b5870 | |||
262b4718fc | |||
4d0e84b84a | |||
f969b0a4c0 | |||
34167c4d99 | |||
79580e16b7 | |||
abf6b9af82 | |||
85e3509731 | |||
8a4eadefcb | |||
e8154d1e13 | |||
9b00853d5a | |||
574a63b2a3 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
docker-compose.yml
|
11
README.md
11
README.md
@ -11,10 +11,11 @@ We recommend using [The Things Network](https://www.thethingsnetwork.org/) (TTN)
|
|||||||
- [Database](#database)
|
- [Database](#database)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
2. [TTN Integration](#ttn-integration)
|
2. [TTN Integration](#ttn-integration)
|
||||||
3. [Add a Location Tracker](#add-a-location-tracker)
|
3. [Prometheus Metrics](#prometheus-metrics)
|
||||||
|
4. [Add a Location Tracker](#add-a-location-tracker)
|
||||||
- [Onboard SenseCAP T1000-B](#onboard-sensecap-t1000-b)
|
- [Onboard SenseCAP T1000-B](#onboard-sensecap-t1000-b)
|
||||||
- [Register SenseCAP T1000-B](#register-sensecap-t1000-b)
|
- [Register SenseCAP T1000-B](#register-sensecap-t1000-b)
|
||||||
4. [Testing](#testing)
|
5. [Testing](#testing)
|
||||||
- [Testing Webhook](#testing-webhook)
|
- [Testing Webhook](#testing-webhook)
|
||||||
- [Emulating Wigle API](#emulating-wigle-api)
|
- [Emulating Wigle API](#emulating-wigle-api)
|
||||||
|
|
||||||
@ -49,9 +50,15 @@ Add a addidtional header:
|
|||||||
- Type: `authorization`
|
- Type: `authorization`
|
||||||
- Value: `Bearer your-very-secure-token-from-the-env-file`
|
- Value: `Bearer your-very-secure-token-from-the-env-file`
|
||||||
|
|
||||||
|
### Prometheus Metrics
|
||||||
|
Use `https://your.domain.tld/api/metrics` to retrieve useful insides for monitoring.
|
||||||
|
|
||||||
## Add a Location Tracker
|
## Add a Location Tracker
|
||||||
We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-B-p-5698.html) from seeedstudio because of the fair price and multiple location providers. However, you can use any LoRaWAN-enabled tracker that is compatible with TTN and supports the required payload fields.
|
We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-B-p-5698.html) from seeedstudio because of the fair price and multiple location providers. However, you can use any LoRaWAN-enabled tracker that is compatible with TTN and supports the required payload fields.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
Run `journalctl -u locationhub.service -f` to see log output.
|
||||||
|
|
||||||
### Onboard SenseCAP T1000-B
|
### Onboard SenseCAP T1000-B
|
||||||
1. Download and install the App [SenseCraft](https://play.google.com/store/apps/details?id=cc.seeed.sensecapmate)
|
1. Download and install the App [SenseCraft](https://play.google.com/store/apps/details?id=cc.seeed.sensecapmate)
|
||||||
2. Skip the user account at startup with `Skip` in the upper right corner
|
2. Skip the user account at startup with `Skip` in the upper right corner
|
||||||
|
5
server/.gitignore
vendored
5
server/.gitignore
vendored
@ -308,7 +308,4 @@ cython_debug/
|
|||||||
# Built Visual Studio Code Extensions
|
# Built Visual Studio Code Extensions
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|
||||||
config.py
|
config.py
|
||||||
|
|
||||||
#docker
|
|
||||||
docker-compose.yml
|
|
38
server/package-lock.json
generated
38
server/package-lock.json
generated
@ -14,6 +14,7 @@
|
|||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"http-status-codes": "^2.3.0",
|
"http-status-codes": "^2.3.0",
|
||||||
"mariadb": "^3.4.0",
|
"mariadb": "^3.4.0",
|
||||||
|
"prom-client": "^15.1.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"sequelize": "^6.37.5",
|
"sequelize": "^6.37.5",
|
||||||
"swagger-jsdoc": "^6.2.8",
|
"swagger-jsdoc": "^6.2.8",
|
||||||
@ -120,6 +121,15 @@
|
|||||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@opentelemetry/api": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@scarf/scarf": {
|
"node_modules/@scarf/scarf": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
||||||
@ -386,6 +396,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bintrees": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.3",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
@ -1434,6 +1450,19 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prom-client": {
|
||||||
|
"version": "15.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz",
|
||||||
|
"integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@opentelemetry/api": "^1.4.0",
|
||||||
|
"tdigest": "^0.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^16 || ^18 || >=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@ -1873,6 +1902,15 @@
|
|||||||
"express": ">=4.0.0 || >=5.0.0-beta"
|
"express": ">=4.0.0 || >=5.0.0-beta"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tdigest": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bintrees": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"http-status-codes": "^2.3.0",
|
"http-status-codes": "^2.3.0",
|
||||||
"mariadb": "^3.4.0",
|
"mariadb": "^3.4.0",
|
||||||
|
"prom-client": "^15.1.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"sequelize": "^6.37.5",
|
"sequelize": "^6.37.5",
|
||||||
"swagger-jsdoc": "^6.2.8",
|
"swagger-jsdoc": "^6.2.8",
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=LocationHub
|
Description=LocationHub Service
|
||||||
|
Documentation=https://git.mosad.xyz/localhorst/LocationHub
|
||||||
After=network.target systemd-networkd-wait-online.service mysqld.service
|
After=network.target systemd-networkd-wait-online.service mysqld.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=locationhub
|
User=locationhub
|
||||||
|
Group=locationhub
|
||||||
WorkingDirectory=/home/locationhub/git/LocationHub/server/
|
WorkingDirectory=/home/locationhub/git/LocationHub/server/
|
||||||
ExecStart=/usr/bin/npm run dev
|
|
||||||
|
# Combine commands for build and start
|
||||||
|
ExecStart=/bin/bash -c "/usr/bin/npm run build && /usr/bin/npm run start"
|
||||||
|
|
||||||
|
# Restart policies
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
StandardOutput=append:/var/log/LocationHub.log
|
RestartSec=5s
|
||||||
StandardError=append:/var/log/LocationHub.log
|
|
||||||
|
# Logging configuration
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=locationhub
|
||||||
|
|
||||||
|
# Resource control (optional but helps stability)
|
||||||
|
MemoryLimit=512M
|
||||||
|
CPUQuota=50%
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
```
|
|
||||||
Activate Systemd Job
|
|
||||||
```
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable locationhub.service
|
|
||||||
systemctl start locationhub.service
|
|
||||||
systemctl status locationhub.service
|
|
63
server/src/controller/metricsController.ts
Normal file
63
server/src/controller/metricsController.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import express, { Request, Response } from "express";
|
||||||
|
import { container } from "tsyringe";
|
||||||
|
import { Counter, Gauge, collectDefaultMetrics, register } from "prom-client";
|
||||||
|
import { LocationService } from "../services/locationService";
|
||||||
|
import { WifiLocationService } from "../services/wifiLocationService";
|
||||||
|
import { TtnGatewayReceptionService } from "../services/ttnGatewayReceptionService";
|
||||||
|
import { MetricsService } from "../services/metricsService";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const locationService = container.resolve(LocationService);
|
||||||
|
const wifiLocationService = container.resolve(WifiLocationService);
|
||||||
|
const ttnGatewayReceptionService = container.resolve(TtnGatewayReceptionService);
|
||||||
|
const metricsService = container.resolve(MetricsService);
|
||||||
|
|
||||||
|
const requestCounter = metricsService.getRequestCounter();
|
||||||
|
const locationsTotal = metricsService.getLocationsTotal();
|
||||||
|
const gnssLocationsTotal = metricsService.getGnssLocationsTotal()
|
||||||
|
const wifiLocationTotal = metricsService.getWifiLocationTotal();
|
||||||
|
const wifiLocationNotResolvable = metricsService.getWifiLocationResolvable();
|
||||||
|
const wifiLocationRequestLimitExceeded = metricsService.getLWifiLocationRequestLimitExceeded();
|
||||||
|
const ttnGatewayReceptions = metricsService.getTnnGatewayReceptions();
|
||||||
|
|
||||||
|
// Define the metrics endpoint
|
||||||
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const [
|
||||||
|
allLocations,
|
||||||
|
gnssLocations,
|
||||||
|
allWifiLocations,
|
||||||
|
wifiLocationsNotResolvable,
|
||||||
|
wifiLocationsRequestLimitExceeded,
|
||||||
|
allTtnGatewayReceptions
|
||||||
|
] = await Promise.all([
|
||||||
|
locationService.getAllLocations(),
|
||||||
|
locationService.getAllGnssLocations(),
|
||||||
|
wifiLocationService.getAllWifiLocations(),
|
||||||
|
wifiLocationService.getAllWifiLocationsByNotResolvable(),
|
||||||
|
wifiLocationService.getAllWifiLocationsByRequestLimitExceeded(),
|
||||||
|
ttnGatewayReceptionService.getAllGatewayReceptions()
|
||||||
|
]);
|
||||||
|
|
||||||
|
locationsTotal.set(allLocations.length);
|
||||||
|
gnssLocationsTotal.set(gnssLocations.length);
|
||||||
|
wifiLocationTotal.set(allWifiLocations.length);
|
||||||
|
wifiLocationNotResolvable.set(wifiLocationsNotResolvable.length);
|
||||||
|
wifiLocationRequestLimitExceeded.set(wifiLocationsRequestLimitExceeded.length);
|
||||||
|
ttnGatewayReceptions.set(allTtnGatewayReceptions.length);
|
||||||
|
|
||||||
|
// Increment the counter with labels
|
||||||
|
requestCounter.inc({ method: req.method, route: req.route.path, status: 200 });
|
||||||
|
|
||||||
|
// Expose metrics in Prometheus format
|
||||||
|
res.set("Content-Type", register.contentType);
|
||||||
|
res.send(await register.metrics());
|
||||||
|
} catch (error) {
|
||||||
|
// Increment the counter for errors
|
||||||
|
requestCounter.inc({ method: req.method, route: req.route.path, status: 500 });
|
||||||
|
console.error("Error running metrics endpoint:", error);
|
||||||
|
res.status(500).json({ error: "Error running metrics endpoint" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -56,24 +56,24 @@ router.post(
|
|||||||
timestamp: latitudeData?.timestamp
|
timestamp: latitudeData?.timestamp
|
||||||
? new Date(latitudeData.timestamp)
|
? new Date(latitudeData.timestamp)
|
||||||
: longitudeData?.timestamp
|
: longitudeData?.timestamp
|
||||||
? new Date(longitudeData.timestamp)
|
? new Date(longitudeData.timestamp)
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
const wifiTimestamp = (() => {
|
|
||||||
const messages = message.uplink_message.decoded_payload?.messages?.[0];
|
const wifiMessage =
|
||||||
const wifiScan = messages?.find((e: { type: string }) => e.type === "Wi-Fi Scan");
|
message.uplink_message.decoded_payload?.messages[0].find(
|
||||||
return wifiScan?.timestamp ? new Date(wifiScan.timestamp) : undefined;
|
(e) => e.type === "Wi-Fi Scan"
|
||||||
})();
|
);
|
||||||
|
|
||||||
const wifiScans =
|
const wifiScans =
|
||||||
message.uplink_message.decoded_payload?.messages[0]
|
wifiMessage?.measurementValue?.map((w) => ({
|
||||||
.find((e) => e.type === "Wi-Fi Scan")
|
lp_ttn_end_device_uplinks_id,
|
||||||
?.measurementValue?.map((w) => ({
|
mac: w.mac,
|
||||||
lp_ttn_end_device_uplinks_id,
|
rssi: w.rssi,
|
||||||
mac: w.mac,
|
scanned_at_utc: wifiMessage?.timestamp
|
||||||
rssi: w.rssi,
|
? new Date(wifiMessage.timestamp)
|
||||||
scanned_at_timestamp: wifiTimestamp,
|
: undefined,
|
||||||
})) ?? [];
|
})) ?? [];
|
||||||
|
|
||||||
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
|
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
|
||||||
(g) => ({
|
(g) => ({
|
||||||
@ -109,9 +109,9 @@ router.post(
|
|||||||
gnss:
|
gnss:
|
||||||
gnnsLocation.latitude && gnnsLocation.longitude
|
gnnsLocation.latitude && gnnsLocation.longitude
|
||||||
? {
|
? {
|
||||||
latitude: gnnsLocation.latitude,
|
latitude: gnnsLocation.latitude,
|
||||||
longitude: gnnsLocation.longitude,
|
longitude: gnnsLocation.longitude,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
gnss_timestamp: gnssTimestamp.timestamp,
|
gnss_timestamp: gnssTimestamp.timestamp,
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ import ttnGatewayReceptionRoutes from "./controller/ttnGatewayReceptionControlle
|
|||||||
import wifiLocationRoutes from "./controller/wifiLocationController";
|
import wifiLocationRoutes from "./controller/wifiLocationController";
|
||||||
import wifiLocationHistoryRoutes from "./controller/wifiLocationHistoryController";
|
import wifiLocationHistoryRoutes from "./controller/wifiLocationHistoryController";
|
||||||
import wifiScanRoutes from "./controller/wifiScanController";
|
import wifiScanRoutes from "./controller/wifiScanController";
|
||||||
|
import metricsRoutes from "./controller/metricsController";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ app.use("/api/wifi-location", wifiLocationRoutes);
|
|||||||
app.use("/api/wifi-scans", wifiScanRoutes);
|
app.use("/api/wifi-scans", wifiScanRoutes);
|
||||||
app.use("/api/locations", locationRoutes);
|
app.use("/api/locations", locationRoutes);
|
||||||
app.use("/api/ttn", ttnRoutes);
|
app.use("/api/ttn", ttnRoutes);
|
||||||
|
app.use("/api/metrics", metricsRoutes);
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`🚀 Server runs here: http://localhost:${PORT}`);
|
console.log(`🚀 Server runs here: http://localhost:${PORT}`);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import { Attributes, FindOptions } from "sequelize";
|
||||||
import { injectable } from "tsyringe";
|
import { injectable } from "tsyringe";
|
||||||
import { Location } from "../models/location";
|
import { Location } from "../models/location";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LocationRepository {
|
export class LocationRepository {
|
||||||
public async findAll() {
|
public async findAll(options?: FindOptions<Attributes<Location>>) {
|
||||||
return await Location.findAll();
|
return await Location.findAll(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findById(id: string) {
|
public async findById(id: string) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { inject, injectable } from "tsyringe";
|
import { inject, injectable } from "tsyringe";
|
||||||
|
import { Op } from 'sequelize';
|
||||||
import { Location } from "../models/location";
|
import { Location } from "../models/location";
|
||||||
import { LocationRepository } from "../repositories/locationRepository";
|
import { LocationRepository } from "../repositories/locationRepository";
|
||||||
import { WifiLocationService } from "./wifiLocationService";
|
import { WifiLocationService } from "./wifiLocationService";
|
||||||
@ -54,6 +55,15 @@ export class LocationService {
|
|||||||
return this.repository.findAll();
|
return this.repository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAllGnssLocations() {
|
||||||
|
return this.repository.findAll({
|
||||||
|
where: {
|
||||||
|
gnss_latitude: { [Op.ne]: null },
|
||||||
|
gnss_longitude: { [Op.ne]: null }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getLocationById(id: string) {
|
public async getLocationById(id: string) {
|
||||||
return this.repository.findById(id);
|
return this.repository.findById(id);
|
||||||
}
|
}
|
||||||
|
70
server/src/services/metricsService.ts
Normal file
70
server/src/services/metricsService.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import {injectable } from "tsyringe";
|
||||||
|
import { Counter, Gauge, collectDefaultMetrics} from "prom-client";
|
||||||
|
|
||||||
|
// Collect default system metrics (e.g., CPU, memory usage)
|
||||||
|
const prefix = 'locationhub_';
|
||||||
|
collectDefaultMetrics({ prefix });
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MetricsService {
|
||||||
|
constructor(
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public getRequestCounter() {
|
||||||
|
const requestCounter = new Counter({
|
||||||
|
name: `${prefix}http_requests_total`,
|
||||||
|
help: "Total number of HTTP requests",
|
||||||
|
labelNames: ["method", "route", "status"],
|
||||||
|
});
|
||||||
|
return requestCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLocationsTotal() {
|
||||||
|
const locationsTotal = new Gauge({
|
||||||
|
name: `${prefix}locations_total`,
|
||||||
|
help: "Total number of location entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return locationsTotal;
|
||||||
|
}
|
||||||
|
public getGnssLocationsTotal() {
|
||||||
|
const gnssLocationsTotal = new Gauge({
|
||||||
|
name: `${prefix}gnss_locations_total`,
|
||||||
|
help: "Total number of location entries with GNSS in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return gnssLocationsTotal;
|
||||||
|
}
|
||||||
|
public getWifiLocationTotal() {
|
||||||
|
const wifiLocationTotal = new Gauge({
|
||||||
|
name: `${prefix}wifi_locations_total`,
|
||||||
|
help: "Total number of wifi location entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return wifiLocationTotal;
|
||||||
|
}
|
||||||
|
public getWifiLocationResolvable() {
|
||||||
|
const wifiLocationNotResolvable = new Gauge({
|
||||||
|
name: `${prefix}wifi_locations_not_resolvable`,
|
||||||
|
help: "Unresolved number of wifi location entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return wifiLocationNotResolvable;
|
||||||
|
}
|
||||||
|
public getLWifiLocationRequestLimitExceeded() {
|
||||||
|
const wifiLocationRequestLimitExceeded = new Gauge({
|
||||||
|
name: `${prefix}wifi_locations_request_limit_exceeded`,
|
||||||
|
help: "Unresolved number of wifi location because request limit exceeded entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return wifiLocationRequestLimitExceeded;
|
||||||
|
}
|
||||||
|
public getTnnGatewayReceptions() {
|
||||||
|
const ttnGatewayReceptions = new Gauge({
|
||||||
|
name: `${prefix}ttn_gateway_receptions`,
|
||||||
|
help: "Total number of TTN Gateway receptions entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return ttnGatewayReceptions;
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,18 @@ export class WifiLocationService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAllWifiLocationsByNotResolvable() {
|
||||||
|
return this.repository.findAll({
|
||||||
|
where: { location_not_resolvable: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAllWifiLocationsByRequestLimitExceeded() {
|
||||||
|
return this.repository.findAll({
|
||||||
|
where: { request_limit_exceeded: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getWifiLocationByMac(mac: string) {
|
public async getWifiLocationByMac(mac: string) {
|
||||||
let wifiLocation = await this.repository.findById(mac);
|
let wifiLocation = await this.repository.findById(mac);
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ interface CreateWifiScanParams {
|
|||||||
lp_ttn_end_device_uplinks_id: string;
|
lp_ttn_end_device_uplinks_id: string;
|
||||||
mac: string;
|
mac: string;
|
||||||
rssi: number;
|
rssi: number;
|
||||||
scanned_at_timestamp?: Date;
|
scanned_at_utc?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateWifiScanParams {
|
interface UpdateWifiScanParams {
|
||||||
wifi_scan_id: string;
|
wifi_scan_id: string;
|
||||||
mac?: string;
|
mac?: string;
|
||||||
rssi?: number;
|
rssi?: number;
|
||||||
scanned_at_timestamp?: Date;
|
scanned_at_utc?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
|
Reference in New Issue
Block a user