mirror of
https://gitlab.com/cyclane/sbdatatracker.git
synced 2025-04-18 20:05:04 +00:00
175 lines
4.0 KiB
TypeScript
175 lines
4.0 KiB
TypeScript
import sirv from "sirv";
|
|
import polka from "polka";
|
|
import compression from "compression";
|
|
import * as sapper from "@sapper/server";
|
|
|
|
import arangojs, { Database, aql } from "arangojs";
|
|
import { hash } from "bcrypt";
|
|
import { log } from "./logger";
|
|
import { Permissions } from "./lib/permissions";
|
|
import { stringify } from "json-bigint";
|
|
|
|
// HACK : BigInt support on JSON.stringify
|
|
JSON.stringify = stringify;
|
|
|
|
export let profileNameCache: {
|
|
[player: string]: {
|
|
[profile: string]: string;
|
|
};
|
|
} = {};
|
|
|
|
export const {
|
|
PORT,
|
|
NODE_ENV,
|
|
DB_USERNAME,
|
|
DB_PASSWORD,
|
|
DB_URL,
|
|
DB_NAME,
|
|
API_KEY
|
|
} = process.env;
|
|
const dev = NODE_ENV === "development";
|
|
export const db = arangojs({
|
|
auth: {
|
|
username: DB_USERNAME || "root",
|
|
password: DB_PASSWORD
|
|
},
|
|
url: DB_URL || "http://127.0.0.1:8529",
|
|
databaseName: DB_NAME || "sbdatatracker"
|
|
});
|
|
export const saltRounds = 9;
|
|
export const accessTokenExpire = 1000 * 60 * 20;
|
|
export const refreshTokenExpire = 1000 * 60 * 60 * 24 * 7;
|
|
const delay = 3 * 60 * 1000;
|
|
|
|
const intervalFunc = async (db: Database) => {
|
|
const cnf = db.collection("config");
|
|
const users = db.collection("users");
|
|
const sessions = db.collection("sessions");
|
|
if (!(await cnf.exists())) {
|
|
await cnf.create({
|
|
schema: {
|
|
rule: {
|
|
properties: {
|
|
type: { type: "string" },
|
|
data: { type: "object" }
|
|
},
|
|
required: ["type", "data"]
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (!(await users.exists())) {
|
|
await users.create({
|
|
schema: {
|
|
rule: {
|
|
properties: {
|
|
username: { type: "string" },
|
|
password: { type: "string" },
|
|
permissions: {
|
|
type: "array",
|
|
items: { type: "string" }
|
|
}
|
|
},
|
|
required: ["username", "permissions"]
|
|
}
|
|
}
|
|
});
|
|
await users.save({
|
|
username: DB_USERNAME || "root",
|
|
password: await hash(DB_PASSWORD || "SBDataTracker", saltRounds),
|
|
permissions: [Permissions.ALL]
|
|
});
|
|
await users.save({
|
|
username: "_guest",
|
|
permissions: [Permissions.VIEW_PROFILES]
|
|
});
|
|
}
|
|
if (!(await sessions.exists())) {
|
|
await sessions.create({
|
|
schema: {
|
|
rule: {
|
|
properties: {
|
|
token: { type: "string" },
|
|
exp: { type: "number" },
|
|
type: { type: "string" },
|
|
user: { type: "string" }
|
|
},
|
|
required: ["token", "exp", "type", "user"]
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Delete expired sessions
|
|
const now = Date.now();
|
|
await db.query(aql`
|
|
FOR doc in ${sessions}
|
|
FILTER doc.exp <= ${now}
|
|
REMOVE doc in ${sessions}
|
|
`);
|
|
|
|
const cursor = await db.query(
|
|
aql`
|
|
FOR doc IN ${cnf}
|
|
FILTER doc.type == "tracker"
|
|
RETURN { _key: doc._key }
|
|
`,
|
|
{
|
|
count: true
|
|
}
|
|
);
|
|
/*
|
|
Instead of running a loop on all trackers at once,
|
|
they are spread out evenly across the delay which
|
|
lowers the chance of exceeding the Hypixel rate-limit
|
|
when there are a lot of trackers.
|
|
|
|
TODO : Add a hard-stop if the rate-limit is about
|
|
to be exceeded.
|
|
*/
|
|
if (cursor.hasNext) {
|
|
let counter = 0;
|
|
let interval: NodeJS.Timeout;
|
|
const iterate = async () => {
|
|
try {
|
|
if (++counter >= cursor.count!) clearInterval(interval);
|
|
|
|
const { _key: profile } = await cursor.next();
|
|
if (profile) {
|
|
const col = db.collection(`c${profile}`);
|
|
if (!(await col.exists())) col.create();
|
|
console.log(`${new Date()}\tLogging '${profile}'`);
|
|
await col.save(await log(API_KEY || "", profile));
|
|
} else {
|
|
console.warn(
|
|
`Configuration entry '${profile}' with type 'tracker' has no "profile" value`
|
|
);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
// Separated into setInterval and a setTimeout so when the script is started,
|
|
// immediately at least one profile is logged. This is useful for debugging
|
|
// since you don't have to wait for up to 3 minutes to pass.
|
|
if (cursor.count! > 1)
|
|
interval = setInterval(iterate, delay / (cursor.count! - 1));
|
|
setTimeout(iterate);
|
|
}
|
|
};
|
|
|
|
setInterval(intervalFunc, delay, db);
|
|
setTimeout(intervalFunc, 0, db);
|
|
|
|
polka({
|
|
onError: err => {
|
|
if (err) console.log("Error", err);
|
|
}
|
|
})
|
|
.use(
|
|
compression({ threshold: 0 }),
|
|
sirv("static", { dev }),
|
|
sapper.middleware()
|
|
)
|
|
.listen(PORT);
|