mirror of
https://github.com/MrTalon63/NeoMap.git
synced 2026-01-11 11:59:13 +01:00
Compare commits
2 commits
e87cc775c8
...
067594e669
| Author | SHA1 | Date | |
|---|---|---|---|
| 067594e669 | |||
| 908b0953bb |
8 changed files with 284 additions and 153 deletions
|
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"tasks": {
|
||||
"dev": "deno run --watch -A --unstable-kv src/server.ts"
|
||||
"dev": "deno run --watch -A --unstable-kv --env-file src/server.ts"
|
||||
},
|
||||
"imports": {
|
||||
"@db/sqlite": "jsr:@db/sqlite@^0.12.0",
|
||||
"@deno-library/compress": "jsr:@deno-library/compress@^0.5.5",
|
||||
"@hono/oidc-auth": "npm:@hono/oidc-auth@^1.7.0",
|
||||
"@std/assert": "jsr:@std/assert@1",
|
||||
"h3-js": "npm:h3-js@^4.1.0",
|
||||
"hono": "npm:hono@^4.6.19"
|
||||
"hono": "npm:hono@^4.8.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
deno.lock
generated
32
deno.lock
generated
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "4",
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"jsr:@db/sqlite@0.12": "0.12.0",
|
||||
"jsr:@db/sqlite@0.12.0": "0.12.0",
|
||||
|
|
@ -23,9 +23,11 @@
|
|||
"jsr:@std/streams@^1.0.7": "1.0.8",
|
||||
"jsr:@std/tar@0.1.3": "0.1.3",
|
||||
"jsr:@zip-js/zip-js@2.7.53": "2.7.53",
|
||||
"npm:@hono/oidc-auth@^1.7.0": "1.7.0_hono@4.8.3",
|
||||
"npm:@types/node@*": "22.15.15",
|
||||
"npm:h3-js@*": "4.1.0",
|
||||
"npm:h3-js@^4.1.0": "4.1.0",
|
||||
"npm:hono@^4.6.19": "4.6.19"
|
||||
"npm:hono@^4.8.3": "4.8.3"
|
||||
},
|
||||
"jsr": {
|
||||
"@db/sqlite@0.12.0": {
|
||||
|
|
@ -130,11 +132,30 @@
|
|||
}
|
||||
},
|
||||
"npm": {
|
||||
"@hono/oidc-auth@1.7.0_hono@4.8.3": {
|
||||
"integrity": "sha512-lJnGrz1ktYPsLKDgLNwl2bKbRFC3ZJb8GUIpMfY8QQEnmmlDtJX0NA2YnW+SgEROAWO5suY/QBlRjEM+xlLo8A==",
|
||||
"dependencies": [
|
||||
"hono",
|
||||
"oauth4webapi"
|
||||
]
|
||||
},
|
||||
"@types/node@22.15.15": {
|
||||
"integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==",
|
||||
"dependencies": [
|
||||
"undici-types"
|
||||
]
|
||||
},
|
||||
"h3-js@4.1.0": {
|
||||
"integrity": "sha512-LQhmMl1dRQQjMXPzJc7MpZ/CqPOWWuAvVEoVJM9n/s7vHypj+c3Pd5rLQCkAsOgAoAYKbNCsYFE++LF7MvSfCQ=="
|
||||
},
|
||||
"hono@4.6.19": {
|
||||
"integrity": "sha512-Xw5DwU2cewEsQ1DkDCdy6aBJkEBARl5loovoL1gL3/gw81RdaPbXrNJYp3LoQpzpJ7ECC/1OFi/vn3UZTLHFEw=="
|
||||
"hono@4.8.3": {
|
||||
"integrity": "sha512-jYZ6ZtfWjzBdh8H/0CIFfCBHaFL75k+KMzaM177hrWWm2TWL39YMYaJgB74uK/niRc866NMlH9B8uCvIo284WQ=="
|
||||
},
|
||||
"oauth4webapi@2.17.0": {
|
||||
"integrity": "sha512-lbC0Z7uzAFNFyzEYRIC+pkSVvDHJTbEW+dYlSBAlCYDe6RxUkJ26bClhk8ocBZip1wfI9uKTe0fm4Ib4RHn6uQ=="
|
||||
},
|
||||
"undici-types@6.21.0": {
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
|
|
@ -142,8 +163,9 @@
|
|||
"jsr:@db/sqlite@0.12",
|
||||
"jsr:@deno-library/compress@~0.5.5",
|
||||
"jsr:@std/assert@1",
|
||||
"npm:@hono/oidc-auth@^1.7.0",
|
||||
"npm:h3-js@^4.1.0",
|
||||
"npm:hono@^4.6.19"
|
||||
"npm:hono@^4.8.3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
.filter-menu {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
top: 40px;
|
||||
right: 10px;
|
||||
z-index: 1000;
|
||||
background: white;
|
||||
|
|
@ -32,6 +32,16 @@
|
|||
<script src="https://unpkg.com/h3-js@4.1.0/dist/h3-js.umd.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div style="font-size: 20px; text-align: center">
|
||||
<div style="font-size: 20px; text-align: left">
|
||||
<a href="/" style="font-weight: bold; color: black; margin-right: 20px; text-decoration: none">NeoMap</a>
|
||||
<a href="/stats" style="margin: 0 10px; color: black; text-decoration: none">Statystyka</a>
|
||||
<a href="/user" style="margin: 0 10px; color: black; text-decoration: none">Profil</a>
|
||||
<a href="/user/settings" style="margin: 0 10px; color: black; text-decoration: none">Ustawienia</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="filter-menu">
|
||||
<div class="filter-group">
|
||||
<h3>Czas aktualizacji</h3>
|
||||
|
|
@ -51,7 +61,11 @@
|
|||
<label><input type="checkbox" class="signal-filter" value="wcdma" /> WCDMA</label>
|
||||
<label><input type="checkbox" class="signal-filter" value="lte" /> LTE</label>
|
||||
<p style="color: #666; margin-top: 5px">(Zaznacz jakie sygnały mają być obecne)</p>
|
||||
<a href="/stats">Statystyka</a>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<h3>Filtr użytkownika</h3>
|
||||
<label><input type="checkbox" class="signal-filter" value="user" /> Pokaż tylko moje</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -73,7 +87,7 @@
|
|||
currentMarkers.forEach((marker) => map.removeLayer(marker));
|
||||
currentMarkers = [];
|
||||
|
||||
hexes.forEach((hex) => {
|
||||
hexes.data.forEach((hex) => {
|
||||
const isTimeValid = timeFilter === "any" || Date.now() / 1000 - hex.last_update < timeFilter;
|
||||
const hasAllSignals = selectedSignals.every((signal) => hex[signal] === 1);
|
||||
const isSignalValid = selectedSignals.length === 0 || hasAllSignals;
|
||||
|
|
|
|||
17
src/api/api.ts
Normal file
17
src/api/api.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Hono } from "hono";
|
||||
|
||||
import geosubmit from "./geosubmit.ts";
|
||||
import hexes from "./hexes.ts";
|
||||
import stats from "./stats.ts";
|
||||
|
||||
const api = new Hono().basePath("/api");
|
||||
|
||||
api.get("/", (c) => {
|
||||
return c.json({ status: 200, message: "NeoMap REST API. Documentation: Soon" });
|
||||
});
|
||||
|
||||
api.route("/v1/geosubmit", geosubmit);
|
||||
api.route("/v1/hexes", hexes);
|
||||
api.route("/v1/stats", stats);
|
||||
|
||||
export default api;
|
||||
120
src/api/geosubmit.ts
Normal file
120
src/api/geosubmit.ts
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import { Hono } from "hono";
|
||||
import { gunzip, gzip } from "@deno-library/compress";
|
||||
import h3 from "npm:h3-js";
|
||||
|
||||
import { db } from "../db.ts";
|
||||
import { kv } from "../kv.ts";
|
||||
import { Geosubmit } from "../types.d.ts";
|
||||
|
||||
const api = new Hono();
|
||||
|
||||
api.post("/", async (c) => {
|
||||
const enconding = await c.req.header("Content-Encoding");
|
||||
if (enconding && enconding !== "gzip") {
|
||||
console.log(enconding);
|
||||
return c.json({ status: 400, message: "Bad Request" }, 400);
|
||||
}
|
||||
let json: Geosubmit;
|
||||
if (enconding === "gzip") {
|
||||
const body = await c.req.arrayBuffer();
|
||||
const arr = new Uint8Array(body);
|
||||
const data = await gunzip(arr);
|
||||
json = JSON.parse(new TextDecoder().decode(data)) as Geosubmit;
|
||||
} else {
|
||||
json = (await c.req.json()) as Geosubmit;
|
||||
}
|
||||
|
||||
const body = gzip(new TextEncoder().encode(JSON.stringify(json)));
|
||||
|
||||
const ftch = await fetch("https://api.beacondb.net/v2/geosubmit", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Encoding": "gzip",
|
||||
},
|
||||
body: body,
|
||||
});
|
||||
if (ftch.status !== 200) {
|
||||
return c.json({ status: ftch.status, message: "Bad Request" }, 400);
|
||||
}
|
||||
|
||||
json.items.forEach(async (item) => {
|
||||
if (item.position.altitude > 2000) return;
|
||||
const timestamp = Math.floor(item.timestamp / 1000);
|
||||
const hex = h3.latLngToCell(item.position.latitude, item.position.longitude, 10);
|
||||
let hasGsm = false,
|
||||
hasWcdma = false,
|
||||
hasLte = false,
|
||||
hasWifi = false,
|
||||
hasBle = false;
|
||||
if (item.cellTowers) {
|
||||
item.cellTowers.forEach((cell) => {
|
||||
if (hasGsm && hasWcdma && hasLte) return;
|
||||
if (cell.radioType === "gsm") hasGsm = true;
|
||||
if (cell.radioType === "wcdma") hasWcdma = true;
|
||||
if (cell.radioType === "lte") hasLte = true;
|
||||
});
|
||||
}
|
||||
if (item.wifiAccessPoints) {
|
||||
item.wifiAccessPoints.forEach((wifi) => {
|
||||
if (hasWifi) return;
|
||||
hasWifi = true;
|
||||
});
|
||||
}
|
||||
if (item.bluetoothBeacons) {
|
||||
item.bluetoothBeacons.forEach((ble) => {
|
||||
if (hasBle) return;
|
||||
hasBle = true;
|
||||
});
|
||||
}
|
||||
|
||||
const hexInKv = await kv.get([hex]);
|
||||
if (hexInKv.value) {
|
||||
const { wifi, gsm, wcdma, lte, ble, last_update } = JSON.parse(hexInKv.value as string);
|
||||
|
||||
if (last_update > timestamp && wifi === hasWifi && gsm === hasGsm && wcdma === hasWcdma && lte === hasLte && ble === hasBle) {
|
||||
return c.json({ status: 200, message: "OK" });
|
||||
}
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE hexes
|
||||
SET
|
||||
wifi = MAX(wifi, ?),
|
||||
gsm = MAX(gsm, ?),
|
||||
wcdma = MAX(wcdma, ?),
|
||||
lte = MAX(lte, ?),
|
||||
ble = MAX(ble, ?),
|
||||
last_update = ?
|
||||
WHERE hex_id = ?
|
||||
`,
|
||||
).run(hasWifi, hasGsm, hasWcdma, hasLte, hasBle, timestamp, hex);
|
||||
kv.set([hex], JSON.stringify({ wifi: hasWifi, gsm: hasGsm, wcdma: hasWcdma, lte: hasLte, ble: hasBle, last_update: timestamp }));
|
||||
} else {
|
||||
const hexInDb = db.prepare("SELECT * FROM hexes WHERE hex_id = ?").get(hex) as { hex_id: string; wifi: boolean; gsm: boolean; wcdma: boolean; lte: boolean; ble: boolean; last_update: number } | undefined;
|
||||
if (hexInDb) {
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE hexes
|
||||
SET
|
||||
wifi = MAX(wifi, ?),
|
||||
gsm = MAX(gsm, ?),
|
||||
wcdma = MAX(wcdma, ?),
|
||||
lte = MAX(lte, ?),
|
||||
ble = MAX(ble, ?),
|
||||
last_update = ?
|
||||
WHERE hex_id = ?
|
||||
`,
|
||||
).run(hasWifi, hasGsm, hasWcdma, hasLte, hasBle, timestamp, hex);
|
||||
kv.set([hex], JSON.stringify({ wifi: hasWifi, gsm: hasGsm, wcdma: hasWcdma, lte: hasLte, ble: hasBle, last_update: timestamp }));
|
||||
} else {
|
||||
db.prepare("INSERT INTO hexes (hex_id, wifi, gsm, wcdma, lte, ble, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)").run(hex, hasWifi, hasGsm, hasWcdma, hasLte, hasBle, timestamp);
|
||||
kv.set([hex], JSON.stringify({ wifi: hasWifi, gsm: hasGsm, wcdma: hasWcdma, lte: hasLte, ble: hasBle, last_update: timestamp }));
|
||||
}
|
||||
}
|
||||
});
|
||||
//db.exec("PRAGMA wal_checkpoint(PASSIVE);");
|
||||
return c.json({ status: 200, message: "OK" });
|
||||
});
|
||||
|
||||
export default api;
|
||||
12
src/api/hexes.ts
Normal file
12
src/api/hexes.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Hono } from "hono";
|
||||
|
||||
import { db } from "../db.ts";
|
||||
|
||||
const api = new Hono();
|
||||
|
||||
api.get("/", async (c) => {
|
||||
const hexes = db.prepare("SELECT hex_id, wifi, gsm, wcdma, lte, ble, last_update FROM hexes").all();
|
||||
return c.json({ status: 200, message: "OK", data: hexes });
|
||||
});
|
||||
|
||||
export default api;
|
||||
12
src/api/stats.ts
Normal file
12
src/api/stats.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Hono } from "hono";
|
||||
|
||||
import { db } from "../db.ts";
|
||||
|
||||
const api = new Hono();
|
||||
|
||||
api.get("", async (c) => {
|
||||
const stats = db.prepare("SELECT COUNT(hex_id) as hexes, SUM(wifi) as wifi, SUM(gsm) as gsm, SUM(wcdma) as wcdma, SUM(lte) as lte, SUM(ble) as ble FROM hexes").get();
|
||||
return c.json(stats);
|
||||
});
|
||||
|
||||
export default api;
|
||||
169
src/server.ts
169
src/server.ts
|
|
@ -1,142 +1,70 @@
|
|||
import h3 from "npm:h3-js";
|
||||
import { gunzip, gzip } from "@deno-library/compress";
|
||||
import { Hono } from "hono";
|
||||
import { Hono, Context, OidcAuthClaims } from "hono";
|
||||
import { serveStatic } from "hono/deno";
|
||||
import { logger } from "hono/logger";
|
||||
import { compress } from "hono/compress";
|
||||
import { oidcAuthMiddleware, getAuth, revokeSession, processOAuthCallback, TokenEndpointResponses, IDToken, OidcAuth } from "@hono/oidc-auth";
|
||||
|
||||
import api from "./api/api.ts";
|
||||
|
||||
import { db } from "./db.ts";
|
||||
import { kv } from "./kv.ts";
|
||||
import { Geosubmit } from "./types.d.ts";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
declare module "hono" {
|
||||
interface OidcAuthClaims {
|
||||
name: string;
|
||||
email: string;
|
||||
profile: string;
|
||||
groups: string[];
|
||||
}
|
||||
}
|
||||
|
||||
const oidcClaimsHook = async (orig: OidcAuth | undefined, claims: IDToken | undefined, _response: TokenEndpointResponses): Promise<OidcAuthClaims> => {
|
||||
return {
|
||||
name: (claims?.name as string) ?? (orig?.name as string) ?? "",
|
||||
profile: (claims?.profile as string) ?? (orig?.profile as string) ?? "",
|
||||
email: (claims?.email as string) ?? (orig?.email as string) ?? "",
|
||||
groups: (claims?.groups as string[]) ?? (orig?.groups as string[]) ?? [],
|
||||
};
|
||||
};
|
||||
|
||||
app.get("/auth/callback", async (c: Context) => {
|
||||
c.set("oidcClaimsHook", oidcClaimsHook);
|
||||
return processOAuthCallback(c);
|
||||
});
|
||||
|
||||
app.use(logger(), compress());
|
||||
app.use("/", serveStatic({ root: "./public" }));
|
||||
|
||||
app.post("/api/v1/geosubmit", async (c) => {
|
||||
const enconding = await c.req.header("Content-Encoding");
|
||||
if (enconding && enconding !== "gzip") {
|
||||
console.log(enconding);
|
||||
return c.json({ status: 400, message: "Bad Request" }, 400);
|
||||
app.use("/auth/*", oidcAuthMiddleware());
|
||||
app.use("/auth/*", async (c, next) => {
|
||||
const auth = await getAuth(c);
|
||||
if (!auth) {
|
||||
return c.json({ status: 401, message: "Unauthorized" }, 401);
|
||||
}
|
||||
let json: Geosubmit;
|
||||
if (enconding === "gzip") {
|
||||
const body = await c.req.arrayBuffer();
|
||||
const arr = new Uint8Array(body);
|
||||
const data = await gunzip(arr);
|
||||
json = JSON.parse(new TextDecoder().decode(data)) as Geosubmit;
|
||||
} else {
|
||||
json = (await c.req.json()) as Geosubmit;
|
||||
}
|
||||
|
||||
const body = gzip(new TextEncoder().encode(JSON.stringify(json)));
|
||||
|
||||
const ftch = await fetch("https://api.beacondb.net/v2/geosubmit", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Encoding": "gzip",
|
||||
},
|
||||
body: body,
|
||||
await next();
|
||||
});
|
||||
if (ftch.status !== 200) {
|
||||
return c.json({ status: ftch.status, message: "Bad Request" }, 400);
|
||||
}
|
||||
|
||||
json.items.forEach(async (item) => {
|
||||
if (item.position.altitude > 2000) return;
|
||||
const timestamp = Math.floor(item.timestamp / 1000);
|
||||
const hex = h3.latLngToCell(item.position.latitude, item.position.longitude, 10);
|
||||
let hasGsm = false,
|
||||
hasWcdma = false,
|
||||
hasLte = false,
|
||||
hasWifi = false,
|
||||
hasBle = false;
|
||||
if (item.cellTowers) {
|
||||
item.cellTowers.forEach((cell) => {
|
||||
if (hasGsm && hasWcdma && hasLte) return;
|
||||
if (cell.radioType === "gsm") hasGsm = true;
|
||||
if (cell.radioType === "wcdma") hasWcdma = true;
|
||||
if (cell.radioType === "lte") hasLte = true;
|
||||
});
|
||||
app.get("/auth/logout", async (c) => {
|
||||
const auth = await getAuth(c);
|
||||
if (!auth) {
|
||||
return c.json({ status: 401, message: "Unauthorized" }, 401);
|
||||
}
|
||||
if (item.wifiAccessPoints) {
|
||||
item.wifiAccessPoints.forEach((wifi) => {
|
||||
if (hasWifi) return;
|
||||
hasWifi = true;
|
||||
});
|
||||
}
|
||||
if (item.bluetoothBeacons) {
|
||||
item.bluetoothBeacons.forEach((ble) => {
|
||||
if (hasBle) return;
|
||||
hasBle = true;
|
||||
});
|
||||
}
|
||||
|
||||
const hexInKv = await kv.get([hex]);
|
||||
if (hexInKv.value) {
|
||||
const { wifi, gsm, wcdma, lte, ble, last_update } = JSON.parse(hexInKv.value as string);
|
||||
|
||||
if (last_update > timestamp && wifi === hasWifi && gsm === hasGsm && wcdma === hasWcdma && lte === hasLte && ble === hasBle) {
|
||||
return c.json({ status: 200, message: "OK" });
|
||||
}
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE hexes
|
||||
SET
|
||||
wifi = MAX(wifi, ?),
|
||||
gsm = MAX(gsm, ?),
|
||||
wcdma = MAX(wcdma, ?),
|
||||
lte = MAX(lte, ?),
|
||||
ble = MAX(ble, ?),
|
||||
last_update = ?
|
||||
WHERE hex_id = ?
|
||||
`,
|
||||
).run(hasWifi, hasGsm, hasWcdma, hasLte, hasBle, timestamp, hex);
|
||||
kv.set([hex], JSON.stringify({ wifi: hasWifi, gsm: hasGsm, wcdma: hasWcdma, lte: hasLte, ble: hasBle, last_update: timestamp }));
|
||||
} else {
|
||||
const hexInDb = db.prepare("SELECT * FROM hexes WHERE hex_id = ?").get(hex) as { hex_id: string; wifi: boolean; gsm: boolean; wcdma: boolean; lte: boolean; ble: boolean; last_update: number } | undefined;
|
||||
if (hexInDb) {
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE hexes
|
||||
SET
|
||||
wifi = MAX(wifi, ?),
|
||||
gsm = MAX(gsm, ?),
|
||||
wcdma = MAX(wcdma, ?),
|
||||
lte = MAX(lte, ?),
|
||||
ble = MAX(ble, ?),
|
||||
last_update = ?
|
||||
WHERE hex_id = ?
|
||||
`,
|
||||
).run(hasWifi, hasGsm, hasWcdma, hasLte, hasBle, timestamp, hex);
|
||||
kv.set([hex], JSON.stringify({ wifi: hasWifi, gsm: hasGsm, wcdma: hasWcdma, lte: hasLte, ble: hasBle, last_update: timestamp }));
|
||||
} else {
|
||||
db.prepare("INSERT INTO hexes (hex_id, wifi, gsm, wcdma, lte, ble, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)").run(hex, hasWifi, hasGsm, hasWcdma, hasLte, hasBle, timestamp);
|
||||
kv.set([hex], JSON.stringify({ wifi: hasWifi, gsm: hasGsm, wcdma: hasWcdma, lte: hasLte, ble: hasBle, last_update: timestamp }));
|
||||
}
|
||||
}
|
||||
});
|
||||
//db.exec("PRAGMA wal_checkpoint(PASSIVE);");
|
||||
await revokeSession(c);
|
||||
return c.json({ status: 200, message: "OK" });
|
||||
});
|
||||
|
||||
app.get("/api/v1/hexes", async (c) => {
|
||||
const hexes = db.prepare("SELECT hex_id, wifi, gsm, wcdma, lte, ble, last_update FROM hexes").all();
|
||||
return c.json(hexes);
|
||||
app.get("/auth/login", async (c) => {
|
||||
const auth = await getAuth(c);
|
||||
return c.json({ status: 200, message: "OK", data: auth });
|
||||
});
|
||||
|
||||
app.get("/api/v1/stats", async (c) => {
|
||||
const stats = db.prepare("SELECT COUNT(hex_id) as hexes, SUM(wifi) as wifi, SUM(gsm) as gsm, SUM(wcdma) as wcdma, SUM(lte) as lte, SUM(ble) as ble FROM hexes").get();
|
||||
return c.json(stats);
|
||||
});
|
||||
app.route("/", api);
|
||||
|
||||
app.get("/stats", async (c) => {
|
||||
const stat = db.prepare("SELECT COUNT(hex_id) as hexes, SUM(wifi) as wifi, SUM(gsm) as gsm, SUM(wcdma) as wcdma, SUM(lte) as lte, SUM(ble) as ble FROM hexes").get();
|
||||
if (!stat) return c.json({ status: 500, message: "Server Error" }, 500);
|
||||
return c.html(`<!DOCTYPE html>
|
||||
return c.html(
|
||||
`<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
|
@ -165,9 +93,14 @@ app.get("/stats", async (c) => {
|
|||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="center">test</div>
|
||||
<div class="center">loading failed</div>
|
||||
</body>
|
||||
</html> `);
|
||||
</html>`,
|
||||
);
|
||||
});
|
||||
|
||||
app.onError((err, c) => {
|
||||
return c.json({ status: 500, message: "Server Error" }, 500);
|
||||
});
|
||||
|
||||
Deno.serve(app.fetch);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue