Initial commit

master
Drake 3 months ago
commit a28f9a90c4

2
.gitignore vendored

@ -0,0 +1,2 @@
config.json
deno.lock

@ -0,0 +1,11 @@
{
"deno.enable": true,
"deno.unstable": true,
"editor.formatOnSave": true,
"deno.importMap": "import_map.json",
"[typescript]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.defaultFormatter": "denoland.vscode-deno"
}
}

@ -0,0 +1,8 @@
# Wackylytics Server
The server for the wackiest (and jankiest) analytics suite ever.
## FAQ
**Q**: Should I use this?\
**A**: No. (seriously, don't) ((your cartoon bendy privileges will be taken away if you do))

@ -0,0 +1,8 @@
{
"fmt": {
"options": {
"indentWidth": 4
}
},
"importMap": "import_map.json"
}

@ -0,0 +1,7 @@
{
"imports": {
"@oak/": "https://deno.land/x/oak@v11.1.0/",
"@std/": "https://deno.land/std@0.163.0/",
"@surrealdb/": "https://deno.land/x/surrealdb@v0.5.0/"
}
}

@ -0,0 +1,18 @@
import init from "./src/index.ts";
const app = await init();
app.addEventListener("listen", ({
hostname,
port,
}) => {
console.log(
`Wackylytics server listening on http://${
hostname ?? "localhost"
}:${port}`,
);
});
await app.listen({
port: Number(Deno.env.get("PORT")) ?? 8080,
});

@ -0,0 +1,13 @@
import config from "../config.json" assert { type: "json" };
export type configType = typeof config;
export interface Site {
metadata: {
description: string;
};
}
export { config };
export default config;

@ -0,0 +1,24 @@
import Surreal from "@surrealdb/mod.ts";
import config from "./config.ts";
import { Location } from "./utils.ts";
export interface IP {
location: Location;
history: {
timestamp: Date;
path: string;
}[];
}
export const db = new Surreal(`${config.db.url}/rpc`);
export default async function init() {
try {
await db.signin({
user: config.db.user,
pass: config.db.pass,
});
} catch (e) {
throw new Error(e);
}
}

@ -0,0 +1,92 @@
import { Application, Router } from "@oak/mod.ts";
import initDB, { db, IP } from "./database.ts";
import { deviousLick, hash } from "./utils.ts";
import config, { Site } from "./config.ts";
interface Value {
site: string;
id: string;
ip: string;
path: string;
}
export default async function init() {
await initDB();
const router = new Router();
router.post("/add", async (ctx) => {
const value: Value = await ctx.request.body({ type: "json" }).value;
const hashedIP = await hash(value.ip);
const site: Site = (config.sites as Record<string, Site>)[value.site];
if (!site) {
ctx.response.status = 406;
ctx.response.body = "Unknown site!";
return;
}
let ipDB: IP;
await db.use(config.db.namespace, value.site);
try {
ipDB = (await db.select("ip:" + hashedIP))[0] as IP;
} catch {
const geolocation = await deviousLick(value.ip);
await db.create("ip:" + hashedIP, {
location: geolocation,
history: [],
});
ipDB = (await db.select("ip:" + hashedIP))[0] as IP;
}
ipDB.history.push({
timestamp: new Date(),
path: value.path,
});
console.log(`${hashedIP} ${value.path}`);
// deno-lint-ignore no-explicit-any
await db.update(`ip:${hashedIP}`, ipDB as Record<string, any>);
ctx.response.status = 200;
ctx.response.body = "Successfully added request to history!";
});
router.get("/sites", (ctx) => {
ctx.response.status = 200;
ctx.response.type = "application/json";
ctx.response.body = JSON.stringify(config.sites);
});
router.get("/site/:site", (ctx) => {
const siteName: string = ctx.params.site;
const site = (config.sites as Record<string, Site>)[siteName];
ctx.response.status = 200;
ctx.response.type = "application/json";
ctx.response.body = JSON.stringify(site);
});
router.get("/site/:site/analytics", async (ctx) => {
const siteName: string = ctx.params.site;
await db.use(config.db.namespace, siteName);
const database = await db.select("ip");
ctx.response.status = 200;
ctx.response.type = "application/json";
ctx.response.body = JSON.stringify(database);
});
const app = new Application();
app.use(router.routes());
return app;
}

@ -0,0 +1,43 @@
import { crypto, toHashString } from "@std/crypto/mod.ts";
export interface Location {
country: string;
region: string;
}
interface InternalLocation {
status: string;
country: string;
countryCode: string;
region: string;
regionName: string;
city: string;
zip: string;
lat: number;
lon: number;
timezone: string;
isp: string;
org: string;
as: string;
query: string;
}
/** deviously lick (an ip's geolocation) */
export async function deviousLick(ip: string): Promise<Location> {
const res = await fetch(`http://ip-api.com/json/${ip}`);
const data: InternalLocation = await res.json();
return {
country: data.country,
region: data.regionName,
} as Location;
}
export async function hash(str: string): Promise<string> {
const digest = await crypto.subtle.digest(
"SHA3-256",
new TextEncoder().encode(str),
);
return toHashString(digest, "hex");
}
Loading…
Cancel
Save