Use own instance of Umami (visitors.taevas.xyz
) (#10)
Not quite self-hosted, it's on Vercel ...surely there are more positives than negatives as for doing that (instead of using `cloud.umami.is`, I mean)
This commit is contained in:
parent
d42bb932cd
commit
5fff23156b
6 changed files with 122 additions and 13 deletions
|
@ -28,5 +28,6 @@ This package makes use of several online APIs through Netlify in order to delive
|
|||
- `API_LASTFM`
|
||||
- `API_OSU`
|
||||
- `API_WANIKANI`
|
||||
- `API_UMAMI`
|
||||
- `USERNAME_UMAMI`
|
||||
- `PASSWORD_UMAMI`
|
||||
- `URL_MONGODB`
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<link rel="stylesheet" href="/src/App.css">
|
||||
<script defer src="https://cloud.umami.is/script.js" data-website-id="3461d539-c2fb-4930-9d4a-a0e4016a174a"></script>
|
||||
<script defer src="https://visitors.taevas.xyz/script.js" data-website-id="f196d626-e609-4841-9a80-0dc60f523ed5"></script>
|
||||
<title>Site - taevas.xyz</title>
|
||||
</head>
|
||||
<body class="bg-gradient-to-tl from-indigo-100 via-sky-300 to-indigo-100 bg-fixed">
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
import { UmamiInfo } from "#Infos/Website/Umami.js";
|
||||
import {type Handler} from "@netlify/functions";
|
||||
import { MongoClient } from "mongodb";
|
||||
import { Token } from "./umami_token.js";
|
||||
|
||||
const handler: Handler = async () => {
|
||||
const api_server = "https://api.umami.is/v1";
|
||||
const website_id = "3461d539-c2fb-4930-9d4a-a0e4016a174a";
|
||||
const client = new MongoClient(process.env.URL_MONGODB!);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("tokens");
|
||||
const collection = db.collection<Token>("umami");
|
||||
const token = await collection.findOne();
|
||||
void client.close();
|
||||
|
||||
const api_server = "https://visitors.taevas.xyz/api";
|
||||
const website_id = "f196d626-e609-4841-9a80-0dc60f523ed5";
|
||||
const now = new Date();
|
||||
const response = await fetch(`${api_server}/websites/${website_id}/stats?startAt=${Number(new Date("2025"))}&endAt=${Number(now)}`, {
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"x-umami-api-key": process.env.API_UMAMI!
|
||||
"Authorization": `Bearer ${token?.access_token}`
|
||||
},
|
||||
});
|
||||
|
||||
|
|
74
netlify/functions/umami_token.ts
Normal file
74
netlify/functions/umami_token.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
/* eslint no-async-promise-executor: 0 */ // Doing promises is needed in order to make multiple requests at once, lowering wait time
|
||||
|
||||
import {type Handler} from "@netlify/functions";
|
||||
import {MongoClient} from "mongodb";
|
||||
|
||||
export interface Token {
|
||||
access_token: string;
|
||||
expires: Date;
|
||||
}
|
||||
|
||||
const handler: Handler = async () => {
|
||||
const client = new MongoClient(process.env.URL_MONGODB!);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("tokens");
|
||||
const collection = db.collection<Token>("umami");
|
||||
const tokens = await collection.find().toArray();
|
||||
|
||||
const now = new Date();
|
||||
const token = tokens.find((t) => t.expires > now);
|
||||
const expiredTokens = tokens.filter((t) => now > t.expires);
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
if (!token) {
|
||||
promises.push(new Promise(async (resolve) => {
|
||||
console.log("Setting a new token for umami...");
|
||||
const response = await fetch("https://visitors.taevas.xyz/api/auth/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
body: `username=${process.env.USERNAME_UMAMI}&password=${process.env.PASSWORD_UMAMI}`
|
||||
});
|
||||
const json: {token: string} = await response.json();
|
||||
|
||||
// Assume it expires in one day
|
||||
const date = new Date();
|
||||
date.setHours(date.getHours() + 24);
|
||||
const insertion = await collection.insertOne({
|
||||
access_token: json.token,
|
||||
expires: date,
|
||||
});
|
||||
console.log(`New umami token in the database: ${insertion.insertedId.toString()}`);
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
|
||||
if (expiredTokens.length) {
|
||||
promises.push(new Promise(async (resolve) => {
|
||||
console.log("Deleting old tokens for umami...");
|
||||
await Promise.all(expiredTokens.map(async (t) => {
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const deletion = await collection.deleteOne({_id: t._id});
|
||||
if (deletion.deletedCount) {
|
||||
console.log(`Old umami token deleted from the database: ${t._id.toString()}`);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
void client.close();
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
};
|
||||
};
|
||||
|
||||
export {handler};
|
|
@ -27,8 +27,8 @@ export default function Umami() {
|
|||
|
||||
return (
|
||||
<Website
|
||||
name="Umami"
|
||||
link="https://cloud.umami.is/share/g31G2F06EWgN9sRM/taevas.xyz"
|
||||
name="Analytics"
|
||||
link="https://visitors.taevas.xyz/share/DlW6mBQ09DMn0sTQ/taevas.xyz"
|
||||
elements={elements}
|
||||
error={error}
|
||||
/>
|
||||
|
|
|
@ -1,16 +1,40 @@
|
|||
import React from "react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import Info from "../Info.js";
|
||||
import Umami from "./Umami.js";
|
||||
|
||||
export default function Speedrun() {
|
||||
const umami = <Umami key={"umami"}/>;
|
||||
export default function Website() {
|
||||
const [token, setToken] = useState(false);
|
||||
const [websites, setWebsites] = useState([] as React.JSX.Element[]);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const getToken = async () => {
|
||||
await fetch("/.netlify/functions/umami_token").then((r) => {
|
||||
if (r.ok) {
|
||||
setToken(true);
|
||||
} else {
|
||||
setError(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getToken().catch(() => {
|
||||
setError(true);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
const umami = <Umami key={"umami"}/>;
|
||||
setWebsites([umami]);
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
return (
|
||||
<Info
|
||||
type="Website"
|
||||
websites={[
|
||||
umami,
|
||||
]}
|
||||
websites={websites}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue