updated backed

This commit is contained in:
MasterGordon 2025-06-13 17:19:24 +02:00
parent 86163a61b0
commit d591553872
13 changed files with 116 additions and 22 deletions

View File

@ -2,10 +2,11 @@
import type { ServerWebSocket } from "bun";
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
import type { z, ZodType } from "zod";
import * as schema from "../schema";
interface RequestContext {
user?: string;
db: BunSQLiteDatabase;
db: BunSQLiteDatabase<typeof schema>;
ws: ServerWebSocket<unknown>;
}

View File

@ -19,6 +19,9 @@ import {
import { getWeight, lootboxes } from "../../shared/lootboxes";
import { weightedPickRandom } from "../../shared/utils";
import { emit } from "../events";
import { Game } from "../schema";
import { and, eq, gt } from "drizzle-orm";
import dayjs from "dayjs";
const secret = process.env.SECRET!;
@ -176,4 +179,28 @@ export const userController = createController({
});
},
),
getHeatmap: createEndpoint(
z.object({
id: z.string(),
}),
async ({ id }, { db }) => {
const now = dayjs();
const firstOfYear = now
.set("day", 0)
.set("month", 0)
.set("hour", 0)
.set("minute", 0);
const gamesOfUser = await db.query.Game.findMany({
where: and(eq(Game.user, id), gt(Game.finished, firstOfYear.valueOf())),
});
const heat = Array.from<number>({
length: now.diff(firstOfYear, "days") + 1,
}).fill(0);
gamesOfUser.forEach((game) => {
const day = dayjs(game.finished).diff(firstOfYear, "days");
heat[day] += 1;
});
return heat;
},
),
});

View File

@ -1,8 +1,9 @@
import { drizzle } from "drizzle-orm/bun-sqlite";
import * as schema from "../schema";
import { Database } from "bun:sqlite";
export const getDb = (filename: string = "sqlite.db") => {
const sqlite = new Database(filename);
const db = drizzle(sqlite);
const db = drizzle(sqlite, { schema });
return db;
};

View File

@ -3,9 +3,10 @@ import { Collection, type CollectionType } from "../schema";
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
import { decode, encode } from "@msgpack/msgpack";
import type { UserCollection } from "../../shared/gameType";
import * as schema from "../schema";
export const getCollection = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user: string,
): Promise<UserCollection> => {
const res = (
@ -24,7 +25,7 @@ export const getCollection = async (
};
export const upsertCollection = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user: string,
collection: UserCollection,
) => {

View File

@ -3,12 +3,19 @@ import { Game, type GameType } from "../schema";
import { eq, sql, desc, and, not } from "drizzle-orm";
import type { ServerGame } from "../../shared/game";
import { decode, encode } from "@msgpack/msgpack";
import * as schema from "../schema";
export const getGame = async (db: BunSQLiteDatabase, uuid: string) => {
export const getGame = async (
db: BunSQLiteDatabase<typeof schema>,
uuid: string,
) => {
return (await db.select().from(Game).where(eq(Game.uuid, uuid)))[0];
};
export const getGames = async (db: BunSQLiteDatabase, user: string) => {
export const getGames = async (
db: BunSQLiteDatabase<typeof schema>,
user: string,
) => {
return await db
.select()
.from(Game)
@ -16,7 +23,10 @@ export const getGames = async (db: BunSQLiteDatabase, user: string) => {
.orderBy(desc(Game.started));
};
export const getCurrentGame = async (db: BunSQLiteDatabase, user: string) => {
export const getCurrentGame = async (
db: BunSQLiteDatabase<typeof schema>,
user: string,
) => {
return (
await db
.select()
@ -27,7 +37,10 @@ export const getCurrentGame = async (db: BunSQLiteDatabase, user: string) => {
)[0];
};
export const getGamesCount = async (db: BunSQLiteDatabase, user: string) => {
export const getGamesCount = async (
db: BunSQLiteDatabase<typeof schema>,
user: string,
) => {
return (
await db
.select({ count: sql<number>`count(*)` })
@ -36,7 +49,10 @@ export const getGamesCount = async (db: BunSQLiteDatabase, user: string) => {
)[0].count;
};
export const upsertGame = async (db: BunSQLiteDatabase, game: GameType) => {
export const upsertGame = async (
db: BunSQLiteDatabase<typeof schema>,
game: GameType,
) => {
const { uuid, user, stage, gameState, finished, started } = game;
const games = await db.select().from(Game).where(eq(Game.uuid, uuid));
if (games.length > 0) {
@ -62,7 +78,7 @@ export const upsertGame = async (db: BunSQLiteDatabase, game: GameType) => {
};
export const upsertGameState = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
game: ServerGame,
) => {
const { uuid, user, stage, finished, started } = game;
@ -77,7 +93,7 @@ export const upsertGameState = async (
};
export const getTotalGamesPlayed = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user?: string,
) => {
if (user)

View File

@ -1,8 +1,12 @@
import { eq } from "drizzle-orm";
import { Gems } from "../schema";
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
import * as schema from "../schema";
export const getGems = async (db: BunSQLiteDatabase, user: string) => {
export const getGems = async (
db: BunSQLiteDatabase<typeof schema>,
user: string,
) => {
const res = (await db.select().from(Gems).where(eq(Gems.user, user)))[0];
const count = res?.count ?? 0;
const totalCount = res?.totalCount ?? 0;
@ -10,7 +14,7 @@ export const getGems = async (db: BunSQLiteDatabase, user: string) => {
};
export const addGems = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user: string,
gems: number,
) => {
@ -28,7 +32,7 @@ export const addGems = async (
};
export const removeGems = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user: string,
gems: number,
) => {

View File

@ -1,8 +1,9 @@
import { eq, sql, not } from "drizzle-orm";
import { Game } from "../schema";
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
import * as schema from "../schema";
export const getScoreBoard = async (db: BunSQLiteDatabase) => {
export const getScoreBoard = async (db: BunSQLiteDatabase<typeof schema>) => {
return (
await db
.select({ stage: sql<number>`max(${Game.stage})`, user: Game.user })

View File

@ -5,9 +5,10 @@ import {
userSettings as userSettingsSchema,
type UserSettings as UserSettingsType,
} from "../../shared/user-settings";
import * as schema from "../schema";
export const registerUser = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
name: string,
password: string,
) => {
@ -23,7 +24,7 @@ export const registerUser = async (
};
export const loginUser = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
name: string,
password: string,
) => {
@ -41,7 +42,7 @@ export const loginUser = async (
};
export const getUser = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
name: string,
): Promise<UserType | undefined> => {
const user = await db
@ -52,7 +53,7 @@ export const getUser = async (
};
export const getUserSettings = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user: string,
): Promise<UserSettingsType> => {
const userSettings = await db
@ -64,7 +65,7 @@ export const getUserSettings = async (
};
export const upsertUserSettings = async (
db: BunSQLiteDatabase,
db: BunSQLiteDatabase<typeof schema>,
user: string,
settings: UserSettingsType,
) => {
@ -87,7 +88,7 @@ export const upsertUserSettings = async (
}
};
export const getUserCount = async (db: BunSQLiteDatabase) => {
export const getUserCount = async (db: BunSQLiteDatabase<typeof schema>) => {
return (await db.select({ count: sql<number>`count(*)` }).from(User))[0]
.count;
};

BIN
bun.lockb

Binary file not shown.

View File

@ -32,6 +32,7 @@
"@uidotdev/usehooks": "^2.4.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"drizzle-orm": "0.33.0",
"framer-motion": "^11.11.8",
"jotai": "^2.10.0",

View File

@ -246,4 +246,4 @@ export const halloween: Lootbox = {
],
};
export const lootboxes = [series1, halloween];
export const lootboxes = [series1];

View File

@ -14,6 +14,7 @@ import MatchHistory from "./views/match-history/MatchHistory.tsx";
import Collection from "./views/collection/Collection.tsx";
import { AnimatePresence } from "framer-motion";
import Store from "./views/store/Store.tsx";
import Profile from "./views/profile/Profile.tsx";
const setup = async () => {
const token = localStorage.getItem("loginToken");
@ -44,6 +45,7 @@ setup().then(() => {
<Route path="/settings" component={Settings} />
<Route path="/collection" component={Collection} />
<Route path="/store" component={Store} />
<Route path="/profile" component={Profile} />
</Switch>
</AnimatePresence>
</Shell>

View File

@ -0,0 +1,39 @@
import { useMemo } from "react";
import { useWSQuery } from "../../hooks";
import dayjs from "dayjs";
const Profile: React.FC = () => {
const { data: heatmap } = useWSQuery("user.getHeatmap", { id: "Gordon" });
const now = useMemo(() => dayjs(), []);
const firstOfYear = useMemo(
() => now.set("day", 0).set("month", 0).set("hour", 0).set("minute", 0),
[now],
);
const weeks = now.diff(firstOfYear, "weeks") + 1;
const maxHeat = heatmap ? Math.max(...heatmap) : 0;
return (
<div>
{heatmap && (
<div className="flex gap-2">
{Array.from({ length: weeks }).map((_, w) => (
<div key={w} className="w-4 flex gap-2 flex-col">
{Array.from({ length: 7 }).map((_, d) => (
<div key={d} className="w-4 h-4 border border-white">
<div
className="w-4 h-4 bg-purple-600 -m-px"
style={{
opacity: heatmap[w * 7 + d] / maxHeat,
}}
/>
</div>
))}
</div>
))}
</div>
)}
</div>
);
};
export default Profile;