Remove Netlify, make an API server
2
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
||||||
.netlify
|
.netlify
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
*.log
|
.env
|
||||||
|
|
12
README.md
|
@ -1,5 +1,3 @@
|
||||||
[](https://taevas.xyz)
|
|
||||||
|
|
||||||
# taevas.xyz
|
# taevas.xyz
|
||||||
|
|
||||||
My personal website!
|
My personal website!
|
||||||
|
@ -7,18 +5,16 @@ My personal website!
|
||||||
## Build and develop
|
## Build and develop
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun install --global netlify-cli
|
bun i
|
||||||
bun i --ignore-scripts
|
bun dev
|
||||||
netlify dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
This package uses [`@carbon/icons-react`](https://github.com/carbon-design-system/carbon/tree/main/packages/icons-react), which **installs [a telemetry package which can be disabled](https://github.com/ibm-telemetry/telemetry-js/tree/main#opting-out-of-ibm-telemetry-data-collection):**
|
This package uses [`@carbon/icons-react`](https://github.com/carbon-design-system/carbon/tree/main/packages/icons-react), which **installs [a telemetry package which can be disabled](https://github.com/ibm-telemetry/telemetry-js/tree/main#opting-out-of-ibm-telemetry-data-collection):**
|
||||||
|
|
||||||
```bash
|
Set the environment variable IBM_TELEMETRY_DISABLED to true
|
||||||
netlify env:set IBM_TELEMETRY_DISABLED true
|
|
||||||
```
|
|
||||||
|
|
||||||
This package makes use of several online APIs through Netlify in order to deliver the `Infos` that are available on the right side of the website, accessing most of these APIs requires a key (or similar), which can be set through the following environment variables:
|
This package makes use of several online APIs through Netlify in order to deliver the `Infos` that are available on the right side of the website, accessing most of these APIs requires a key (or similar), which can be set through the following environment variables:
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
|
||||||
import {Octokit} from "@octokit/rest";
|
import {Octokit} from "@octokit/rest";
|
||||||
import {type GithubInfo} from "#Infos/Coding/GitHub.js";
|
import {type GithubInfo} from "#Infos/Coding/GitHub.tsx";
|
||||||
|
import type { Handler } from "..";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const coding_github: Handler = async () => {
|
||||||
const octokit = new Octokit({auth: process.env.API_GITHUB});
|
const octokit = new Octokit({auth: process.env["API_GITHUB"]});
|
||||||
const github = await octokit.rest.activity.listEventsForAuthenticatedUser({username: "TTTaevas"});
|
const github = await octokit.rest.activity.listEventsForAuthenticatedUser({username: "TTTaevas"});
|
||||||
|
|
||||||
const publicPush = github.data.find((e) => (e.type === "PushEvent" || e.type === "PullRequestEvent") && e.public);
|
const publicPush = github.data.find((e) => (e.type === "PushEvent" || e.type === "PullRequestEvent") && e.public);
|
||||||
|
@ -19,10 +19,7 @@ const handler: Handler = async () => {
|
||||||
} : undefined,
|
} : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(info)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(info),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
21
api/coding_gitlab.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { Gitlab } from "@gitbeaker/rest";
|
||||||
|
import {type GitlabInfo} from "#Infos/Coding/GitLab.tsx";
|
||||||
|
import type { Handler } from "..";
|
||||||
|
|
||||||
|
export const coding_gitlab: Handler = async () => {
|
||||||
|
const api = new Gitlab({token: process.env["API_GITLAB"]!});
|
||||||
|
const gitlab = await api.Events.all({action: "pushed"});
|
||||||
|
|
||||||
|
const created_at = gitlab.at(0)?.created_at;
|
||||||
|
if (typeof created_at !== "string") {
|
||||||
|
return new Response("Not Found", {status: 404});
|
||||||
|
}
|
||||||
|
|
||||||
|
const activity: GitlabInfo = {
|
||||||
|
date: created_at.substring(0, created_at.indexOf("T")),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Response(new Blob([JSON.stringify(activity)], {
|
||||||
|
type: "application/json",
|
||||||
|
}), {status: 200});
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import { type KitsudevInfo } from "#Infos/Coding/KitsuDev.tsx";
|
||||||
import { type KitsudevInfo } from "#Infos/Coding/KitsuDev.js";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const coding_kitsudev: Handler = async () => {
|
||||||
const kitsudev = await (await fetch("https://kitsunes.dev/api/v1/users/Taevas/activities/feeds?limit=1")).json() as [{
|
const kitsudev = await (await fetch("https://kitsunes.dev/api/v1/users/Taevas/activities/feeds?limit=1")).json() as [{
|
||||||
repo: {
|
repo: {
|
||||||
full_name: string
|
full_name: string
|
||||||
|
@ -16,10 +16,7 @@ const handler: Handler = async () => {
|
||||||
date: kitsudev[0].created
|
date: kitsudev[0].created
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(info)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(info),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,11 +1,11 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import { type KitsuclubInfo } from "#Infos/Fediverse/KitsuClub.tsx";
|
||||||
import { KitsuclubInfo } from "#Infos/Fediverse/KitsuClub.js";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const fediverse_kitsuclub: Handler = async () => {
|
||||||
const kitsuclub = await (await fetch("https://kitsunes.club/api/users/notes", {
|
const kitsuclub = await (await fetch("https://kitsunes.club/api/users/notes", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `Bearer ${process.env.API_KITSUCLUB}`,
|
"Authorization": `Bearer ${process.env["API_KITSUCLUB"]}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
@ -42,9 +42,7 @@ const handler: Handler = async () => {
|
||||||
|
|
||||||
const details = kitsuclub.at(Math.max(0, kitsuclub.length - 1));
|
const details = kitsuclub.at(Math.max(0, kitsuclub.length - 1));
|
||||||
if (!details) {
|
if (!details) {
|
||||||
return {
|
return new Response("Not Found", {status: 404});
|
||||||
statusCode: 404,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let scan_text = details.text;
|
let scan_text = details.text;
|
||||||
|
@ -89,10 +87,7 @@ const handler: Handler = async () => {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(activity)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(activity),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
32
api/gaming_osu.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import * as osu from "osu-api-v2-js";
|
||||||
|
import {type OsuInfo} from "#Infos/Gaming/Osu.tsx";
|
||||||
|
import {MongoClient} from "mongodb";
|
||||||
|
import {type Token} from "./token.tsx";
|
||||||
|
import type { Handler } from "../index.ts";
|
||||||
|
|
||||||
|
export const gaming_osu: Handler = async (params) => {
|
||||||
|
const client = new MongoClient(process.env["URL_MONGODB"]!);
|
||||||
|
await client.connect();
|
||||||
|
|
||||||
|
const db = client.db("tokens");
|
||||||
|
const collection = db.collection<Token>("osu");
|
||||||
|
const token = await collection.findOne();
|
||||||
|
void client.close();
|
||||||
|
|
||||||
|
let ruleset = params.has("ruleset") ? Number(params.get("ruleset")) : undefined;
|
||||||
|
if (ruleset && isNaN(ruleset)) {ruleset = undefined;}
|
||||||
|
const api = new osu.API({access_token: token?.access_token});
|
||||||
|
const profile = await api.getUser(7276846, ruleset);
|
||||||
|
|
||||||
|
const info: OsuInfo = {
|
||||||
|
country: profile.country.name,
|
||||||
|
ranks: {
|
||||||
|
global: profile.statistics.global_rank ?? 0,
|
||||||
|
country: profile.statistics.country_rank ?? 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Response(new Blob([JSON.stringify(info)], {
|
||||||
|
type: "application/json",
|
||||||
|
}), {status: 200});
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import {type SpeedruncomInfo} from "#Infos/Gaming/Speedruncom.tsx";
|
||||||
import {type SpeedruncomInfo} from "#Infos/Gaming/Speedruncom.js";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
interface Runs {
|
interface Runs {
|
||||||
data: {
|
data: {
|
||||||
|
@ -41,15 +41,13 @@ interface Level {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const gaming_speedruncom: Handler = async () => {
|
||||||
// using the API's embedding would be stupid here, as that'd create lag due to irrelevant runs
|
// using the API's embedding would be stupid here, as that'd create lag due to irrelevant runs
|
||||||
const speedruncom = await (await fetch("https://www.speedrun.com/api/v1/users/j03v45mj/personal-bests")).json() as Runs;
|
const speedruncom = await (await fetch("https://www.speedrun.com/api/v1/users/j03v45mj/personal-bests")).json() as Runs;
|
||||||
const data = speedruncom.data.at(0);
|
const data = speedruncom.data.at(0);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return {
|
return new Response("Not Found", {status: 404});
|
||||||
statusCode: 404,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlsToRequest = [`https://www.speedrun.com/api/v1/games/${data.run.game}`];
|
const urlsToRequest = [`https://www.speedrun.com/api/v1/games/${data.run.game}`];
|
||||||
|
@ -76,10 +74,9 @@ const handler: Handler = async () => {
|
||||||
run.time = run.time.substring(1);
|
run.time = run.time.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(run)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(run),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://gist.github.com/vankasteelj/74ab7793133f4b257ea3
|
// https://gist.github.com/vankasteelj/74ab7793133f4b257ea3
|
||||||
|
@ -92,5 +89,3 @@ function sec2time(timeInSeconds: number) {
|
||||||
const milliseconds = Number(time.toString().slice(-3));
|
const milliseconds = Number(time.toString().slice(-3));
|
||||||
return pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "." + pad(milliseconds, 3);
|
return pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "." + pad(milliseconds, 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import {type HacktheboxInfo} from "#Infos/Hacking/Hackthebox.tsx";
|
||||||
import {type HacktheboxInfo} from "#Infos/Hacking/Hackthebox.js";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const hacking_hackthebox: Handler = async () => {
|
||||||
const hackthebox = await (await fetch("https://www.hackthebox.com/api/v4/profile/activity/1063999")).json() as {
|
const hackthebox = await (await fetch("https://www.hackthebox.com/api/v4/profile/activity/1063999")).json() as {
|
||||||
profile: {
|
profile: {
|
||||||
activity: HacktheboxInfo[];
|
activity: HacktheboxInfo[];
|
||||||
|
@ -10,19 +10,13 @@ const handler: Handler = async () => {
|
||||||
|
|
||||||
const pwn = hackthebox.profile.activity.find((a: HacktheboxInfo) => a?.object_type === "machine");
|
const pwn = hackthebox.profile.activity.find((a: HacktheboxInfo) => a?.object_type === "machine");
|
||||||
if (!pwn) {
|
if (!pwn) {
|
||||||
return {
|
return new Response("Not Found", {status: 404});
|
||||||
statusCode: 404,
|
|
||||||
body: "",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pwn.machine_avatar = `https://www.hackthebox.com${pwn.machine_avatar}`;
|
pwn.machine_avatar = `https://www.hackthebox.com${pwn.machine_avatar}`;
|
||||||
pwn.date = pwn.date.substring(0, pwn.date.indexOf("T"));
|
pwn.date = pwn.date.substring(0, pwn.date.indexOf("T"));
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(pwn)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(pwn),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import {type WanikaniInfo} from "#Infos/Japanese/Wanikani.tsx";
|
||||||
import {type WanikaniInfo} from "#Infos/Japanese/Wanikani.js";
|
import type { WKLevelProgression, WKResetCollection, WKSummary } from "@bachmacintosh/wanikani-api-types";
|
||||||
import { WKLevelProgression, WKResetCollection, WKSummary } from "@bachmacintosh/wanikani-api-types";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
interface Subject {
|
interface Subject {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -48,7 +48,7 @@ function addStuffToLearn(ids: number[], data: {available_at: string; subject_ids
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const japanese_wanikani: Handler = async () => {
|
||||||
const urlsToRequest = [
|
const urlsToRequest = [
|
||||||
"https://api.wanikani.com/v2/level_progressions",
|
"https://api.wanikani.com/v2/level_progressions",
|
||||||
"https://api.wanikani.com/v2/resets",
|
"https://api.wanikani.com/v2/resets",
|
||||||
|
@ -56,8 +56,8 @@ const handler: Handler = async () => {
|
||||||
];
|
];
|
||||||
const toRequest = urlsToRequest.map((url) => new Promise(async (resolve) => {
|
const toRequest = urlsToRequest.map((url) => new Promise(async (resolve) => {
|
||||||
const response = await fetch(url, {headers: {
|
const response = await fetch(url, {headers: {
|
||||||
"Authorization": `Bearer ${process.env.API_WANIKANI}`,
|
"Authorization": `Bearer ${process.env["API_WANIKANI"]}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application.json",
|
||||||
}});
|
}});
|
||||||
resolve(await response.json());
|
resolve(await response.json());
|
||||||
}));
|
}));
|
||||||
|
@ -92,8 +92,8 @@ const handler: Handler = async () => {
|
||||||
|
|
||||||
const subjectIdsAll = subjectIdsLessons.concat(subjectIdsReviews);
|
const subjectIdsAll = subjectIdsLessons.concat(subjectIdsReviews);
|
||||||
const subjects = await (await fetch(`https://api.wanikani.com/v2/subjects?ids=${subjectIdsAll.toString()}`, {headers: {
|
const subjects = await (await fetch(`https://api.wanikani.com/v2/subjects?ids=${subjectIdsAll.toString()}`, {headers: {
|
||||||
"Authorization": `Bearer ${process.env.API_WANIKANI}`,
|
"Authorization": `Bearer ${process.env["API_WANIKANI"]}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application.json",
|
||||||
}})).json() as {data: Subject[]};
|
}})).json() as {data: Subject[]};
|
||||||
|
|
||||||
const lessons = addStuffToLearn(subjectIdsLessons, summary.data.lessons, subjects.data);
|
const lessons = addStuffToLearn(subjectIdsLessons, summary.data.lessons, subjects.data);
|
||||||
|
@ -107,10 +107,7 @@ const handler: Handler = async () => {
|
||||||
moreThingsToReviewAt,
|
moreThingsToReviewAt,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(info)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(info),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import {type AnilistInfo} from "#Infos/Media/Anilist.tsx";
|
||||||
import {type AnilistInfo} from "#Infos/Media/Anilist.js";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const media_anilist: Handler = async () => {
|
||||||
const anilist = await fetch("https://graphql.anilist.co", {
|
const anilist = await fetch("https://graphql.anilist.co", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -63,10 +63,7 @@ const handler: Handler = async () => {
|
||||||
anime.updateDate = anime.updateDate.substring(0, anime.updateDate.indexOf("T"));
|
anime.updateDate = anime.updateDate.substring(0, anime.updateDate.indexOf("T"));
|
||||||
anime.endDate = anime.endDate.substring(0, anime.endDate.indexOf("T"));
|
anime.endDate = anime.endDate.substring(0, anime.endDate.indexOf("T"));
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(anime)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(anime),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import {type LastfmInfo} from "#Infos/Media/Lastfm.tsx";
|
||||||
import {type LastfmInfo} from "#Infos/Media/Lastfm.js";
|
import type { Handler } from "..";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const media_lastfm: Handler = async () => {
|
||||||
const lastfm = await (await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=TTTaevas&api_key=${process.env.API_LASTFM}&format=json&limit=1`)).json() as {
|
const lastfm = await (await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=TTTaevas&api_key=${process.env["API_LASTFM"]}&format=json&limit=1`)).json() as {
|
||||||
recenttracks: {
|
recenttracks: {
|
||||||
track: {
|
track: {
|
||||||
artist: {
|
artist: {
|
||||||
|
@ -39,10 +39,7 @@ const handler: Handler = async () => {
|
||||||
date: lastfm.recenttracks.track[0].date?.uts ?? String(Date.now()),
|
date: lastfm.recenttracks.track[0].date?.uts ?? String(Date.now()),
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(track)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(track),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,18 +1,19 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
import {MongoClient, type InsertOneResult} from "mongodb";
|
||||||
import {InsertOneResult, MongoClient} from "mongodb";
|
|
||||||
|
|
||||||
import {API} from "osu-api-v2-js";
|
import {API} from "osu-api-v2-js";
|
||||||
|
import type { Handler } from "..";
|
||||||
|
|
||||||
export interface Token {
|
export interface Token {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
expires: Date;
|
expires: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler: Handler = async (req) => {
|
export const token: Handler = async (params) => {
|
||||||
const service = req.queryStringParameters?.service;
|
const service = params.get("service");
|
||||||
if (!service) {return {statusCode: 400};}
|
if (!service) {
|
||||||
|
return new Response("Bad Request", {status: 400});
|
||||||
|
}
|
||||||
|
|
||||||
const client = new MongoClient(process.env.URL_MONGODB!);
|
const client = new MongoClient(process.env["URL_MONGODB"]!);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("tokens");
|
const db = client.db("tokens");
|
||||||
|
@ -27,14 +28,14 @@ const handler: Handler = async (req) => {
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
const collections = await db.listCollections().toArray();
|
const collections = await db.listCollections().toArray();
|
||||||
if (!collections.find((c) => c.name === service)) {client.close(); return {statusCode: 400};}
|
if (!collections.find((c) => c.name === service)) {client.close(); return new Response("Not Found", {status: 404});}
|
||||||
|
|
||||||
promises.push(new Promise(async (resolve, reject) => {
|
promises.push(new Promise(async (resolve, reject) => {
|
||||||
console.log(`Setting a new token for ${service}...`);
|
console.log(`Setting a new token for ${service}...`);
|
||||||
let insertion: InsertOneResult;
|
let insertion: InsertOneResult;
|
||||||
|
|
||||||
if (service === "osu") {
|
if (service === "osu") {
|
||||||
const api = await API.createAsync(11451, process.env.API_OSU!);
|
const api = await API.createAsync(11451, process.env["API_OSU"]!);
|
||||||
insertion = await collection.insertOne({
|
insertion = await collection.insertOne({
|
||||||
access_token: api.access_token,
|
access_token: api.access_token,
|
||||||
expires: api.expires,
|
expires: api.expires,
|
||||||
|
@ -47,7 +48,7 @@ const handler: Handler = async (req) => {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded"
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
},
|
},
|
||||||
body: `username=${process.env.USERNAME_UMAMI}&password=${process.env.PASSWORD_UMAMI}`
|
body: `username=${process.env["USERNAME_UMAMI"]}&password=${process.env["PASSWORD_UMAMI"]}`
|
||||||
});
|
});
|
||||||
const json: {token: string} = await response.json();
|
const json: {token: string} = await response.json();
|
||||||
|
|
||||||
|
@ -90,9 +91,5 @@ const handler: Handler = async (req) => {
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
void client.close();
|
void client.close();
|
||||||
|
|
||||||
return {
|
return new Response(null, {status: 200});
|
||||||
statusCode: 200,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { UmamiInfo } from "#Infos/Website/Umami.js";
|
|
||||||
import {type Handler} from "@netlify/functions";
|
|
||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import { Token } from "./token.js";
|
import type { Handler } from "../index.ts";
|
||||||
|
import type { UmamiInfo } from "#Infos/Website/Umami.tsx";
|
||||||
|
import type { Token } from "./token.ts";
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
export const website_umami: Handler = async () => {
|
||||||
const client = new MongoClient(process.env.URL_MONGODB!);
|
const client = new MongoClient(process.env["URL_MONGODB"]!);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("tokens");
|
const db = client.db("tokens");
|
||||||
|
@ -17,7 +17,7 @@ const handler: Handler = async () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const response = await fetch(`${api_server}/websites/${website_id}/stats?startAt=${Number(new Date("2025"))}&endAt=${Number(now)}`, {
|
const response = await fetch(`${api_server}/websites/${website_id}/stats?startAt=${Number(new Date("2025"))}&endAt=${Number(now)}`, {
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application.json",
|
||||||
"Authorization": `Bearer ${token?.access_token}`
|
"Authorization": `Bearer ${token?.access_token}`
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -33,9 +33,7 @@ const handler: Handler = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!umami) {
|
if (!umami) {
|
||||||
return {
|
return new Response("Not Found", {status: 404});
|
||||||
statusCode: 404,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const info: UmamiInfo = {
|
const info: UmamiInfo = {
|
||||||
|
@ -45,10 +43,7 @@ const handler: Handler = async () => {
|
||||||
totaltime: umami.totaltime.value
|
totaltime: umami.totaltime.value
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return new Response(new Blob([JSON.stringify(info)], {
|
||||||
statusCode: 200,
|
type: "application/json",
|
||||||
body: JSON.stringify(info),
|
}), {status: 200});
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {handler};
|
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 958 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 8 KiB |
Before Width: | Height: | Size: 955 B After Width: | Height: | Size: 955 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
BIN
bun.lockb
|
@ -6,7 +6,7 @@
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1"
|
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="/src/App.css">
|
<link rel="stylesheet" href="./index.css">
|
||||||
<script defer src="https://visitors.taevas.xyz/script.js" data-website-id="f196d626-e609-4841-9a80-0dc60f523ed5"></script>
|
<script defer src="https://visitors.taevas.xyz/script.js" data-website-id="f196d626-e609-4841-9a80-0dc60f523ed5"></script>
|
||||||
<title>Site - taevas.xyz</title>
|
<title>Site - taevas.xyz</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -42,6 +42,6 @@
|
||||||
<div id="root">
|
<div id="root">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/src/App.tsx"></script>
|
<script type="module" src="./App.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
103
index.ts
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import { coding_github } from "./api/coding_github";
|
||||||
|
import { coding_gitlab } from "./api/coding_gitlab";
|
||||||
|
import { coding_kitsudev } from "./api/coding_kitsudev";
|
||||||
|
import { fediverse_kitsuclub } from "./api/fediverse_kitsuclub";
|
||||||
|
import { gaming_osu } from "./api/gaming_osu";
|
||||||
|
import { gaming_speedruncom } from "./api/gaming_speedruncom";
|
||||||
|
import { hacking_hackthebox } from "./api/hacking_hackthebox";
|
||||||
|
import { japanese_wanikani } from "./api/japanese_wanikani";
|
||||||
|
import { media_anilist } from "./api/media_anilist";
|
||||||
|
import { media_lastfm } from "./api/media_lastfm";
|
||||||
|
import { token } from "./api/token";
|
||||||
|
import { website_umami } from "./api/website_umami";
|
||||||
|
|
||||||
|
export type Handler = (req: URLSearchParams) => Promise<Response>;
|
||||||
|
const api_endpoints: Handler[] = [
|
||||||
|
coding_github,
|
||||||
|
coding_gitlab,
|
||||||
|
coding_kitsudev,
|
||||||
|
fediverse_kitsuclub,
|
||||||
|
gaming_osu,
|
||||||
|
gaming_speedruncom,
|
||||||
|
hacking_hackthebox,
|
||||||
|
japanese_wanikani,
|
||||||
|
media_anilist,
|
||||||
|
media_lastfm,
|
||||||
|
token,
|
||||||
|
website_umami
|
||||||
|
];
|
||||||
|
|
||||||
|
const builds = await Bun.build({
|
||||||
|
entrypoints: ["./src/App.tsx", "index.css"],
|
||||||
|
target: "browser",
|
||||||
|
minify: {
|
||||||
|
identifiers: true,
|
||||||
|
syntax: true,
|
||||||
|
whitespace: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
port: 8080,
|
||||||
|
fetch: async (req) => {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
const parameters = url.searchParams;
|
||||||
|
// merciless sanitization
|
||||||
|
let pathname = url.pathname;
|
||||||
|
pathname = pathname
|
||||||
|
.replace(/([^A-Za-z0-9/.-_])/g, "")
|
||||||
|
.replace(/(?<![a-zA-Z])\.(?![a-zA-Z])/g, "");
|
||||||
|
|
||||||
|
if (req.method !== "GET") {
|
||||||
|
return new Response("Method Not Allowed", { status: 405 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MAIN PAGE
|
||||||
|
|
||||||
|
if (pathname === "/") {
|
||||||
|
const indexContent = await Bun.file("index.html").text();
|
||||||
|
return new Response(indexContent, {headers: {"Content-Type": "text/html"}});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathname === "/App.tsx" && req.method === "GET") {
|
||||||
|
return new Response(builds.outputs[0].stream(), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": builds.outputs[0].type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// EXTERNAL TO MAIN PAGE
|
||||||
|
|
||||||
|
if (pathname === "/index.css" && req.method === "GET") {
|
||||||
|
return new Response(builds.outputs[1].stream(), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": builds.outputs[1].type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (pathname.startsWith("/assets")) {
|
||||||
|
const asset = Bun.file("." + pathname);
|
||||||
|
return await asset.exists() ? new Response(asset, {status: 200}) : new Response("Not Found", {status: 404});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
if (pathname.startsWith("/api")) {
|
||||||
|
for (const endpoint of api_endpoints) {
|
||||||
|
if (pathname === "/api/" + endpoint.name) {
|
||||||
|
return await endpoint(parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response("Not Found", {status: 404});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Listening on ${server.hostname}:${server.port}`);
|
13
netlify.toml
|
@ -1,13 +0,0 @@
|
||||||
[build]
|
|
||||||
publish = "dist"
|
|
||||||
command = "bun run build"
|
|
||||||
node_bundler = "esbuild"
|
|
||||||
|
|
||||||
[dev]
|
|
||||||
publish = "src"
|
|
||||||
command = "bun run dev"
|
|
||||||
targetPort = 5173
|
|
||||||
node_bundler = "esbuild"
|
|
||||||
|
|
||||||
[functions]
|
|
||||||
node_bundler = "esbuild"
|
|
|
@ -1,26 +0,0 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
|
||||||
import { Gitlab } from "@gitbeaker/rest";
|
|
||||||
import {type GitlabInfo} from "#Infos/Coding/GitLab.js";
|
|
||||||
|
|
||||||
const handler: Handler = async () => {
|
|
||||||
const api = new Gitlab({token: process.env.API_GITLAB!});
|
|
||||||
const gitlab = await api.Events.all({action: "pushed"});
|
|
||||||
|
|
||||||
const created_at = gitlab.at(0)?.created_at;
|
|
||||||
if (typeof created_at !== "string") {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const activity: GitlabInfo = {
|
|
||||||
date: created_at.substring(0, created_at.indexOf("T")),
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(activity),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export {handler};
|
|
|
@ -1,34 +0,0 @@
|
||||||
import {type Handler} from "@netlify/functions";
|
|
||||||
import * as osu from "osu-api-v2-js";
|
|
||||||
import {type OsuInfo} from "#Infos/Gaming/Osu.js";
|
|
||||||
import {MongoClient} from "mongodb";
|
|
||||||
import {type Token} from "./token.js";
|
|
||||||
|
|
||||||
const handler: Handler = async (req) => {
|
|
||||||
const client = new MongoClient(process.env.URL_MONGODB!);
|
|
||||||
await client.connect();
|
|
||||||
|
|
||||||
const db = client.db("tokens");
|
|
||||||
const collection = db.collection<Token>("osu");
|
|
||||||
const token = await collection.findOne();
|
|
||||||
void client.close();
|
|
||||||
|
|
||||||
const ruleset = Number(req.queryStringParameters?.ruleset);
|
|
||||||
const api = new osu.API({access_token: token?.access_token});
|
|
||||||
const profile = await api.getUser(7276846, !isNaN(ruleset) ? ruleset : undefined);
|
|
||||||
|
|
||||||
const info: OsuInfo = {
|
|
||||||
country: profile.country.name,
|
|
||||||
ranks: {
|
|
||||||
global: profile.statistics.global_rank ?? 0,
|
|
||||||
country: profile.statistics.country_rank ?? 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(info),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export {handler};
|
|
18
package.json
|
@ -1,17 +1,15 @@
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "bunx bun css && bun --hot index.ts",
|
||||||
"build": "vite build",
|
"css": "bun tailwindcss -i ./src/App.css -o index.css",
|
||||||
"serve": "vite preview",
|
|
||||||
"lint": "bunx eslint ."
|
"lint": "bunx eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bachmacintosh/wanikani-api-types": "^1.7.0",
|
"@bachmacintosh/wanikani-api-types": "^1.7.0",
|
||||||
"@carbon/icons-react": "^11.56.0",
|
"@carbon/icons-react": "^11.56.0",
|
||||||
"@gitbeaker/rest": "^42.1.0",
|
"@gitbeaker/rest": "^42.1.0",
|
||||||
"@netlify/functions": "^2.8.2",
|
|
||||||
"@octokit/rest": "^20.1.2",
|
"@octokit/rest": "^20.1.2",
|
||||||
"mongodb": "^6.14.0",
|
"mongodb": "^6.14.2",
|
||||||
"osu-api-v2-js": "^1.1.1",
|
"osu-api-v2-js": "^1.1.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
@ -22,12 +20,13 @@
|
||||||
"@eslint/js": "^9.21.0",
|
"@eslint/js": "^9.21.0",
|
||||||
"@stylistic/eslint-plugin": "^3.1.0",
|
"@stylistic/eslint-plugin": "^3.1.0",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
|
"@tailwindcss/postcss": "^4.0.11",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/node": "^20.17.22",
|
"@types/node": "^20.17.23",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.0.10",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"eslint-config-xo-typescript": "^7.0.0",
|
"eslint-config-xo-typescript": "^7.0.0",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
|
@ -35,14 +34,13 @@
|
||||||
"react-animate-height": "^3.2.3",
|
"react-animate-height": "^3.2.3",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"typescript-eslint": "^8.25.0",
|
"typescript-eslint": "^8.26.0"
|
||||||
"vite": "^5.4.14"
|
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"#Main/*": "./src/Main/*",
|
"#Main/*": "./src/Main/*",
|
||||||
"#Infos/*": "./src/Infos/*",
|
"#Infos/*": "./src/Infos/*",
|
||||||
"#parts/*": "./src/parts/*",
|
"#parts/*": "./src/parts/*",
|
||||||
"#contexts": "./src/contexts.js"
|
"#contexts": "./src/contexts.tsx"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "taevas.xyz",
|
"name": "taevas.xyz",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "LexendDeca";
|
font-family: "LexendDeca";
|
||||||
src: url("/fonts/LexendDeca-Regular.ttf") format("truetype");
|
src: url("./assets/fonts/LexendDeca-Regular.ttf") format("truetype");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {createRoot} from "react-dom/client";
|
import {createRoot} from "react-dom/client";
|
||||||
|
|
||||||
import MainContent from "./Main/index.js";
|
import MainContent from "./Main/index.tsx";
|
||||||
import Infos from "./Infos/index.js";
|
import Infos from "./Infos/index.tsx";
|
||||||
|
|
||||||
|
|
||||||
const container = document.getElementById("root");
|
const root = createRoot(document.getElementById("root")!);
|
||||||
const root = createRoot(container!);
|
|
||||||
|
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<div className="App flex text-center bg-gradient-to-tl from-indigo-100 via-sky-300 to-indigo-100 bg-fixed">
|
<div className="App flex text-center bg-gradient-to-tl from-indigo-100 via-sky-300 to-indigo-100 bg-fixed">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
|
|
||||||
export interface GithubInfo {
|
export interface GithubInfo {
|
||||||
public?: {
|
public?: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export type GitlabInfo = {
|
export type GitlabInfo = {
|
||||||
date: string;
|
date: string;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
|
|
||||||
export type KitsudevInfo = {
|
export type KitsudevInfo = {
|
||||||
name: string
|
name: string
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
import GitHub from "./GitHub.js";
|
import GitHub from "./GitHub.tsx";
|
||||||
import GitLab from "./GitLab.js";
|
import GitLab from "./GitLab.tsx";
|
||||||
import KitsuDev from "./KitsuDev.js";
|
import KitsuDev from "./KitsuDev.tsx";
|
||||||
|
|
||||||
export default function Coding() {
|
export default function Coding() {
|
||||||
const github = <GitHub key={"github"}/>;
|
const github = <GitHub key={"github"}/>;
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default function DataHandler<T extends unknown | undefined>(netlifyFuncti
|
||||||
// Try to get and set data
|
// Try to get and set data
|
||||||
const updateData = async () => {
|
const updateData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/.netlify/functions/" + netlifyFunctionName);
|
const response = await fetch("/api/" + netlifyFunctionName);
|
||||||
if (!response.ok) {throw "failed";};
|
if (!response.ok) {throw "failed";};
|
||||||
setData(expectData ? await response.json() : true);
|
setData(expectData ? await response.json() : true);
|
||||||
setError(false);
|
setError(false);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
|
|
||||||
export type KitsuclubInfo = {
|
export type KitsuclubInfo = {
|
||||||
note_id: string
|
note_id: string
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
import KitsuClub from "./KitsuClub.js";
|
import KitsuClub from "./KitsuClub.tsx";
|
||||||
|
|
||||||
export default function Hacking() {
|
export default function Hacking() {
|
||||||
const kitsuclub = <KitsuClub key={"kitsuclub"}/>;
|
const kitsuclub = <KitsuClub key={"kitsuclub"}/>;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import { Ruleset } from "osu-api-v2-js";
|
import { Ruleset } from "osu-api-v2-js";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export type OsuInfo = {
|
export type OsuInfo = {
|
||||||
country: string;
|
country: string;
|
||||||
|
@ -28,7 +28,7 @@ export default function Osu(args: {ruleset: Ruleset}) {
|
||||||
try {
|
try {
|
||||||
setElements([
|
setElements([
|
||||||
<div key={`osu-${ruleset}`} className="flex">
|
<div key={`osu-${ruleset}`} className="flex">
|
||||||
<img className="m-auto w-16 h-16" alt={`${ruleset} mode logo`} src={`/osu_rulesets/${ruleset}.png`}/>
|
<img className="m-auto w-16 h-16" alt={`${ruleset} mode logo`} src={`./assets/osu_rulesets/${ruleset}.png`}/>
|
||||||
<div className="m-auto">
|
<div className="m-auto">
|
||||||
<p>Global: <strong>#{data.ranks.global}</strong></p>
|
<p>Global: <strong>#{data.ranks.global}</strong></p>
|
||||||
<p>{data.country}: <strong>#{data.ranks.country}</strong></p>
|
<p>{data.country}: <strong>#{data.ranks.country}</strong></p>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
import ButtonLink from "#parts/ButtonLink.js";
|
import ButtonLink from "#parts/ButtonLink.tsx";
|
||||||
|
|
||||||
export type SpeedruncomInfo = {
|
export type SpeedruncomInfo = {
|
||||||
place: number;
|
place: number;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
|
|
||||||
import Speedruncom from "./Speedruncom.js";
|
import Speedruncom from "./Speedruncom.tsx";
|
||||||
import Osu from "./Osu.js";
|
import Osu from "./Osu.tsx";
|
||||||
import { Ruleset } from "osu-api-v2-js";
|
import { Ruleset } from "osu-api-v2-js";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export default function RhythmGames() {
|
export default function RhythmGames() {
|
||||||
const {data, error} = DataHandler<boolean>("token?service=osu", 60 * 60 * 8, false);
|
const {data, error} = DataHandler<boolean>("token?service=osu", 60 * 60 * 8, false);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import ButtonLink from "#parts/ButtonLink.js";
|
import ButtonLink from "#parts/ButtonLink.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export type HacktheboxInfo = {
|
export type HacktheboxInfo = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
import Hackthebox from "./Hackthebox.js";
|
import Hackthebox from "./Hackthebox.tsx";
|
||||||
|
|
||||||
export default function Hacking() {
|
export default function Hacking() {
|
||||||
const hackthebox = <Hackthebox key={"hackthebox"}/>;
|
const hackthebox = <Hackthebox key={"hackthebox"}/>;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import { WKLevelProgression, WKReset } from "@bachmacintosh/wanikani-api-types";
|
import { WKLevelProgression, WKReset } from "@bachmacintosh/wanikani-api-types";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export type WanikaniInfo = {
|
export type WanikaniInfo = {
|
||||||
progression: {
|
progression: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
import Wanikani from "./Wanikani.js";
|
import Wanikani from "./Wanikani.tsx";
|
||||||
|
|
||||||
export default function Japanese() {
|
export default function Japanese() {
|
||||||
const wanikani = <Wanikani key={"wanikani"}/>;
|
const wanikani = <Wanikani key={"wanikani"}/>;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
|
|
||||||
export type AnilistInfo = {
|
export type AnilistInfo = {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import {format} from "timeago.js";
|
import {format} from "timeago.js";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export type LastfmInfo = {
|
export type LastfmInfo = {
|
||||||
artist: string;
|
artist: string;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
import Lastfm from "./Lastfm.js";
|
import Lastfm from "./Lastfm.tsx";
|
||||||
import Anilist from "./Anilist.js";
|
import Anilist from "./Anilist.tsx";
|
||||||
|
|
||||||
export default function Media() {
|
export default function Media() {
|
||||||
const lastfm = <Lastfm key={"Lastfm"}/>;
|
const lastfm = <Lastfm key={"Lastfm"}/>;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import Website from "../Website.js";
|
import Website from "../Website.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export type UmamiInfo = {
|
export type UmamiInfo = {
|
||||||
pageviews: number
|
pageviews: number
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import Info from "../Info.js";
|
import Info from "../Info.tsx";
|
||||||
import Umami from "./Umami.js";
|
import Umami from "./Umami.tsx";
|
||||||
import DataHandler from "#Infos/DataHandler.js";
|
import DataHandler from "#Infos/DataHandler.tsx";
|
||||||
|
|
||||||
export default function Website() {
|
export default function Website() {
|
||||||
const {data} = DataHandler<boolean>("token?service=umami", 60 * 60 * 8, false);
|
const {data} = DataHandler<boolean>("token?service=umami", 60 * 60 * 8, false);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React, {Component} from "react";
|
import React, {Component} from "react";
|
||||||
import Media from "./Media/index.js";
|
import Media from "./Media/index.tsx";
|
||||||
// import Hacking from "./Hacking/index.js";
|
// import Hacking from "./Hacking/index.tsx";
|
||||||
import Coding from "./Coding/index.js";
|
import Coding from "./Coding/index.tsx";
|
||||||
import Gaming from "./Gaming/index.js";
|
import Gaming from "./Gaming/index.tsx";
|
||||||
// import Japanese from "./Japanese/index.js";
|
// import Japanese from "./Japanese/index.tsx";
|
||||||
import Fediverse from "./Fediverse/index.js";
|
import Fediverse from "./Fediverse/index.tsx";
|
||||||
import Website from "./Website/index.js";
|
import Website from "./Website/index.tsx";
|
||||||
|
|
||||||
export default class Infos extends Component {
|
export default class Infos extends Component {
|
||||||
private readonly dragbar = React.createRef<HTMLDivElement>();
|
private readonly dragbar = React.createRef<HTMLDivElement>();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import SocialButton from "./SocialButton.js";
|
import SocialButton from "./SocialButton.tsx";
|
||||||
|
|
||||||
export default function SocialButtons() {
|
export default function SocialButtons() {
|
||||||
return (
|
return (
|
||||||
|
@ -9,7 +9,7 @@ export default function SocialButtons() {
|
||||||
border="border-red-500"
|
border="border-red-500"
|
||||||
rotation="rotate-12"
|
rotation="rotate-12"
|
||||||
link="https://www.youtube.com/@TTTaevas"
|
link="https://www.youtube.com/@TTTaevas"
|
||||||
image="/logos/youtube.svg"
|
image="assets/logos/youtube.svg"
|
||||||
padding="p-1"
|
padding="p-1"
|
||||||
/>
|
/>
|
||||||
<SocialButton
|
<SocialButton
|
||||||
|
@ -17,7 +17,7 @@ export default function SocialButtons() {
|
||||||
border="border-black"
|
border="border-black"
|
||||||
rotation="-rotate-6"
|
rotation="-rotate-6"
|
||||||
link="https://github.com/TTTaevas"
|
link="https://github.com/TTTaevas"
|
||||||
image="/logos/github.svg"
|
image="assets/logos/github.svg"
|
||||||
padding="p-1"
|
padding="p-1"
|
||||||
/>
|
/>
|
||||||
<SocialButton
|
<SocialButton
|
||||||
|
@ -25,14 +25,14 @@ export default function SocialButtons() {
|
||||||
border="border-pink-500"
|
border="border-pink-500"
|
||||||
rotation="-rotate-12"
|
rotation="-rotate-12"
|
||||||
link="https://osu.ppy.sh/users/7276846"
|
link="https://osu.ppy.sh/users/7276846"
|
||||||
image="/logos/osu.svg"
|
image="assets/logos/osu.svg"
|
||||||
/>
|
/>
|
||||||
<SocialButton
|
<SocialButton
|
||||||
title="Speedrun.com"
|
title="Speedrun.com"
|
||||||
border="border-yellow-500"
|
border="border-yellow-500"
|
||||||
rotation="rotate-12"
|
rotation="rotate-12"
|
||||||
link="https://www.speedrun.com/users/Taevas"
|
link="https://www.speedrun.com/users/Taevas"
|
||||||
image="/logos/speedrundotcom.png"
|
image="assets/logos/speedrundotcom.png"
|
||||||
padding="p-2"
|
padding="p-2"
|
||||||
/>
|
/>
|
||||||
<SocialButton
|
<SocialButton
|
||||||
|
@ -40,7 +40,7 @@ export default function SocialButtons() {
|
||||||
border="border-cyan-500"
|
border="border-cyan-500"
|
||||||
rotation="rotate-6"
|
rotation="rotate-6"
|
||||||
link="https://anilist.co/user/Taevas/"
|
link="https://anilist.co/user/Taevas/"
|
||||||
image="/logos/anilist.svg"
|
image="assets/logos/anilist.svg"
|
||||||
padding="p-1"
|
padding="p-1"
|
||||||
/>
|
/>
|
||||||
<SocialButton
|
<SocialButton
|
||||||
|
@ -48,7 +48,7 @@ export default function SocialButtons() {
|
||||||
border="border-orange-500"
|
border="border-orange-500"
|
||||||
rotation="-rotate-12"
|
rotation="-rotate-12"
|
||||||
link="https://gitlab.com/TTTaevas"
|
link="https://gitlab.com/TTTaevas"
|
||||||
image="/logos/gitlab.svg"
|
image="assets/logos/gitlab.svg"
|
||||||
padding="p-1"
|
padding="p-1"
|
||||||
/>
|
/>
|
||||||
<SocialButton
|
<SocialButton
|
||||||
|
@ -56,7 +56,7 @@ export default function SocialButtons() {
|
||||||
border="border-red-600"
|
border="border-red-600"
|
||||||
rotation="-rotate-6"
|
rotation="-rotate-6"
|
||||||
link="https://www.last.fm/user/TTTaevas"
|
link="https://www.last.fm/user/TTTaevas"
|
||||||
image="/logos/lastdotfm.png"
|
image="assets/logos/lastdotfm.png"
|
||||||
padding="p-1"
|
padding="p-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import TabButton from "./TabButton.js";
|
import TabButton from "./TabButton.tsx";
|
||||||
import Translatable from "#parts/Translatable.js";
|
import Translatable from "#parts/Translatable.tsx";
|
||||||
import {type TabDetails, LanguageContext, TabContext} from "#contexts";
|
import {type TabDetails, LanguageContext, TabContext} from "#contexts";
|
||||||
|
|
||||||
export default function TabButtons({
|
export default function TabButtons({
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import AnimateHeight from "react-animate-height";
|
import AnimateHeight from "react-animate-height";
|
||||||
|
|
||||||
import TabButtons from "./TabButtons/index.js";
|
import TabButtons from "./TabButtons/index.tsx";
|
||||||
import SocialButtons from "./SocialButtons/index.js";
|
import SocialButtons from "./SocialButtons/index.tsx";
|
||||||
import Translatable from "#parts/Translatable.js";
|
import Translatable from "#parts/Translatable.tsx";
|
||||||
|
|
||||||
import {type TabDetails, TabContext} from "#contexts";
|
import {type TabDetails, TabContext} from "#contexts";
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Tab from "../Tab.js";
|
import Tab from "../Tab.tsx";
|
||||||
import Translatable from "#parts/Translatable.js";
|
import Translatable from "#parts/Translatable.tsx";
|
||||||
import {UserProfile} from "@carbon/icons-react";
|
import {UserProfile} from "@carbon/icons-react";
|
||||||
import {type TabDetails} from "#contexts";
|
import {type TabDetails} from "#contexts";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
|
|
||||||
export default function About({
|
export default function About({
|
||||||
setTabs,
|
setTabs,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Tab from "../Tab.js";
|
import Tab from "../Tab.tsx";
|
||||||
import {MailAll} from "@carbon/icons-react";
|
import {MailAll} from "@carbon/icons-react";
|
||||||
import CopyField from "#parts/CopyField.js";
|
import CopyField from "#parts/CopyField.tsx";
|
||||||
import ButtonLink from "#parts/ButtonLink.js";
|
import ButtonLink from "#parts/ButtonLink.tsx";
|
||||||
import Translatable from "#parts/Translatable.js";
|
import Translatable from "#parts/Translatable.tsx";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
import {type TabDetails} from "#contexts";
|
import {type TabDetails} from "#contexts";
|
||||||
|
|
||||||
export default function Contact({
|
export default function Contact({
|
||||||
|
@ -28,7 +28,7 @@ export default function Contact({
|
||||||
en={<p className="text-center">So, I've decided to go for <Link link="https://matrix.org/" text="the Matrix protocol!"/> Feel free to get in touch with me on this account:</p>}
|
en={<p className="text-center">So, I've decided to go for <Link link="https://matrix.org/" text="the Matrix protocol!"/> Feel free to get in touch with me on this account:</p>}
|
||||||
fr={<p className="text-center">Alors, j'ai opté pour <Link link="https://matrix.org/" text= "le protocole Matrix !"/> N'hésitez pas à entrer en contact avec moi sur ce compte :</p>}
|
fr={<p className="text-center">Alors, j'ai opté pour <Link link="https://matrix.org/" text= "le protocole Matrix !"/> N'hésitez pas à entrer en contact avec moi sur ce compte :</p>}
|
||||||
/>
|
/>
|
||||||
<CopyField text="@taevas:matrix.org" imageUrl="/logos/matrix.svg"/>
|
<CopyField text="@taevas:matrix.org" imageUrl="assets/logos/matrix.svg"/>
|
||||||
<Translatable
|
<Translatable
|
||||||
en={<ButtonLink link="https://matrix.to/#/@taevas:matrix.org" text="(matrix.to link)" />}
|
en={<ButtonLink link="https://matrix.to/#/@taevas:matrix.org" text="(matrix.to link)" />}
|
||||||
fr={<ButtonLink link="https://matrix.to/#/@taevas:matrix.org" text="(lien matrix.to)" />}
|
fr={<ButtonLink link="https://matrix.to/#/@taevas:matrix.org" text="(lien matrix.to)" />}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Tab from "../Tab.js";
|
import Tab from "../Tab.tsx";
|
||||||
import Translatable from "#parts/Translatable.js";
|
import Translatable from "#parts/Translatable.tsx";
|
||||||
import {Devices} from "@carbon/icons-react";
|
import {Devices} from "@carbon/icons-react";
|
||||||
import {type TabDetails} from "#contexts";
|
import {type TabDetails} from "#contexts";
|
||||||
import Link from "#parts/Link.js";
|
import Link from "#parts/Link.tsx";
|
||||||
|
|
||||||
export default function Projects({
|
export default function Projects({
|
||||||
setTabs,
|
setTabs,
|
||||||
|
@ -48,8 +48,8 @@ export default function Projects({
|
||||||
</div>
|
</div>
|
||||||
<div className="border-b-4 py-4">
|
<div className="border-b-4 py-4">
|
||||||
<Translatable
|
<Translatable
|
||||||
en={<p>Still in early 2023, I've made <b><Link link="https://github.com/TTTaevas/osu-api-v1-js" text="osu-api-v1-js"/>, my first JavaScript (TypeScript) package!</b></p>}
|
en={<p>Still in early 2023, I've made <b><Link link="https://github.com/TTTaevas/osu-api-v1.tsx" text="osu-api-v1.tsx"/>, my first JavaScript (TypeScript) package!</b></p>}
|
||||||
fr={<p>Toujours début 2023, j'ai créé <b><Link link="https://github.com/TTTaevas/osu-api-v1-js" text="osu-api-v1-js"/>, mon premier package JavaScript (TypeScript) !</b></p>}
|
fr={<p>Toujours début 2023, j'ai créé <b><Link link="https://github.com/TTTaevas/osu-api-v1.tsx" text="osu-api-v1.tsx"/>, mon premier package JavaScript (TypeScript) !</b></p>}
|
||||||
/>
|
/>
|
||||||
<br/>
|
<br/>
|
||||||
<Translatable
|
<Translatable
|
||||||
|
@ -86,8 +86,8 @@ export default function Projects({
|
||||||
</div>
|
</div>
|
||||||
<div className="border-b-4 py-4">
|
<div className="border-b-4 py-4">
|
||||||
<Translatable
|
<Translatable
|
||||||
en={<p>So <Link link="https://github.com/TTTaevas/osu-api-v2-js" text="osu-api-v2-js"/> is something I've had to work on during three different and distinct periods. I started working on it in March 2023 roughly around the time I finished working on osu-api-v1-js, then I took a break and worked on it again in November 2023, only to finally finish it in March 2024.</p>}
|
en={<p>So <Link link="https://github.com/TTTaevas/osu-api-v2-js" text="osu-api-v2-js"/> is something I've had to work on during three different and distinct periods. I started working on it in March 2023 roughly around the time I finished working on osu-api-v1.tsx, then I took a break and worked on it again in November 2023, only to finally finish it in March 2024.</p>}
|
||||||
fr={<p>Alors <Link link="https://github.com/TTTaevas/osu-api-v2-js" text="osu-api-v2-js"/>, c'est quelque chose sur lequel j'ai dû travailler durant 3 périodes bien différentes, en commençant en Mars 2023 genre quand j'ai fini osu-api-v1-js, puis Novembre 2023 après une pause, puis enfin Mars 2024 après une autre pause.</p>}
|
fr={<p>Alors <Link link="https://github.com/TTTaevas/osu-api-v2-js" text="osu-api-v2-js"/>, c'est quelque chose sur lequel j'ai dû travailler durant 3 périodes bien différentes, en commençant en Mars 2023 genre quand j'ai fini osu-api-v1.tsx, puis Novembre 2023 après une pause, puis enfin Mars 2024 après une autre pause.</p>}
|
||||||
/>
|
/>
|
||||||
<br/>
|
<br/>
|
||||||
<Translatable
|
<Translatable
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Tab from "../Tab.js";
|
import Tab from "../Tab.tsx";
|
||||||
import Translatable from "#parts/Translatable.js";
|
import Translatable from "#parts/Translatable.tsx";
|
||||||
import {UserFavorite} from "@carbon/icons-react";
|
import {UserFavorite} from "@carbon/icons-react";
|
||||||
import {type TabDetails} from "#contexts";
|
import {type TabDetails} from "#contexts";
|
||||||
import ButtonLink from "#parts/ButtonLink.js";
|
import ButtonLink from "#parts/ButtonLink.tsx";
|
||||||
|
|
||||||
export default function Support({
|
export default function Support({
|
||||||
setTabs,
|
setTabs,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {Component} from "react";
|
import React, {Component} from "react";
|
||||||
import AnimateHeight from "react-animate-height";
|
import AnimateHeight from "react-animate-height";
|
||||||
import type Translatable from "#parts/Translatable.js";
|
import type Translatable from "#parts/Translatable.tsx";
|
||||||
import {type TabDetails, TabContext} from "#contexts";
|
import {type TabDetails, TabContext} from "#contexts";
|
||||||
|
|
||||||
export default class Tab extends Component<{
|
export default class Tab extends Component<{
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import About from "./About/index.js";
|
import About from "./About/index.tsx";
|
||||||
import Contact from "./Contact/index.js";
|
import Contact from "./Contact/index.tsx";
|
||||||
import Projects from "./Projects/index.js";
|
import Projects from "./Projects/index.tsx";
|
||||||
import Support from "./Support/index.js";
|
import Support from "./Support/index.tsx";
|
||||||
import {type TabDetails} from "#contexts";
|
import {type TabDetails} from "#contexts";
|
||||||
|
|
||||||
export default function Tabs({
|
export default function Tabs({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import MainWindow from "./MainWindow/index.js";
|
import MainWindow from "./MainWindow/index.tsx";
|
||||||
import Tabs from "./Tabs/index.js";
|
import Tabs from "./Tabs/index.tsx";
|
||||||
import {type TabDetails, LanguageContext, TabContext} from "#contexts";
|
import {type TabDetails, LanguageContext, TabContext} from "#contexts";
|
||||||
|
|
||||||
export default function MainContent() {
|
export default function MainContent() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "./Link.js";
|
import Link from "./Link.tsx";
|
||||||
|
|
||||||
export default function ButtonLink({
|
export default function ButtonLink({
|
||||||
link,
|
link,
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react",
|
"lib": ["ESNext", "DOM"],
|
||||||
"lib": ["DOM"],
|
"target": "ESNext",
|
||||||
"target": "ES6",
|
"module": "ESNext",
|
||||||
"module": "NodeNext",
|
"moduleDetection": "force",
|
||||||
"declaration": false,
|
"jsx": "react-jsx",
|
||||||
"outDir": "./dist",
|
"allowJs": true,
|
||||||
"strict": true
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
})
|
|
||||||
|
|