sendou.ink/app/features/api/ApiRepository.server.ts
2026-01-25 22:13:00 +02:00

81 lines
2.1 KiB
TypeScript

import { nanoid } from "nanoid";
import { db } from "~/db/sql";
import type { ApiTokenType } from "~/db/tables";
const API_TOKEN_LENGTH = 20;
/** Finds an API token for the given user ID and type. */
export function findTokenByUserId(userId: number, type: ApiTokenType) {
return db
.selectFrom("ApiToken")
.selectAll()
.where("userId", "=", userId)
.where("type", "=", type)
.executeTakeFirst();
}
/** Generates a new API token for the given user. Deletes any existing token of the same type before creating a new one. */
export function generateToken(userId: number, type: ApiTokenType) {
const token = nanoid(API_TOKEN_LENGTH);
return db.transaction().execute(async (trx) => {
await trx
.deleteFrom("ApiToken")
.where("userId", "=", userId)
.where("type", "=", type)
.execute();
return trx
.insertInto("ApiToken")
.values({
userId,
token,
type,
})
.returning("token")
.executeTakeFirstOrThrow();
});
}
/** Retrieves all valid API tokens and their types from users with API access. */
export async function allApiTokens() {
const tokens = await db
.selectFrom("ApiToken")
.innerJoin("User", "User.id", "ApiToken.userId")
.leftJoin(
"TournamentOrganizationMember",
"TournamentOrganizationMember.userId",
"ApiToken.userId",
)
.leftJoin(
"TournamentOrganization",
"TournamentOrganization.id",
"TournamentOrganizationMember.organizationId",
)
.select(["ApiToken.token", "ApiToken.type", "ApiToken.userId"])
// NOTE: permissions logic also exists in checkUserHasApiAccess function
.where((eb) =>
eb.or([
eb("User.isApiAccesser", "=", 1),
eb("User.isTournamentOrganizer", "=", 1),
eb("User.patronTier", ">=", 2),
eb.and([
eb("TournamentOrganization.isEstablished", "=", 1),
eb.or([
eb("TournamentOrganizationMember.role", "=", "ADMIN"),
eb("TournamentOrganizationMember.role", "=", "ORGANIZER"),
eb("TournamentOrganizationMember.role", "=", "STREAMER"),
]),
]),
]),
)
.groupBy("ApiToken.token")
.execute();
return tokens.map((row) => ({
token: row.token,
type: row.type,
userId: row.userId,
}));
}