chore: update reservation platform to newest codebase

This commit is contained in:
TOHAACK
2024-05-27 11:49:02 +02:00
parent ea9283e167
commit 3fd586caaf
130 changed files with 9395 additions and 3636 deletions

View File

@ -0,0 +1,29 @@
import type * as schema from "@/server/db/schema";
import type { BuildQueryResult, DBQueryConfig, ExtractTablesWithRelations } from "drizzle-orm";
type Schema = typeof schema;
type TSchema = ExtractTablesWithRelations<Schema>;
/**
* Infer the relation type of a table.
*/
export type IncludeRelation<TableName extends keyof TSchema> = DBQueryConfig<
"one" | "many",
boolean,
TSchema,
TSchema[TableName]
>["with"];
/**
* Infer the result type of a query with optional relations.
*/
export type InferResultType<
TableName extends keyof TSchema,
With extends IncludeRelation<TableName> | undefined = undefined,
> = BuildQueryResult<
TSchema,
TSchema[TableName],
{
with: With;
}
>;

View File

@ -0,0 +1,13 @@
import { z } from "zod";
/**
* Environment variables
*/
export const env = {
RUNTIME_ENVIRONMENT: z.enum(["prod", "dev"]).parse(process.env.RUNTIME_ENVIRONMENT),
DB_PATH: "db/sqlite.db", // As drizzle-kit currently can't load env variables, use a hardcoded value
OAUTH: {
CLIENT_ID: z.string().parse(process.env.OAUTH_CLIENT_ID),
CLIENT_SECRET: z.string().parse(process.env.OAUTH_CLIENT_SECRET),
},
};

View File

@ -0,0 +1,59 @@
import strings from "@/utils/strings";
/**
* Base error class.
*/
class BaseError extends Error {
constructor(message?: string) {
// Pass the message to the Error constructor
super(message);
// Set the name of the error
this.name = this.constructor.name;
// Capture the stack trace
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = new Error(message).stack;
}
}
}
/**
* Permission error class.
*/
export class PermissionError extends BaseError {
constructor() {
super(strings.ERROR.PERMISSION);
}
}
/**
* Authentication error class.
*/
export class AuthenticationError extends BaseError {
constructor() {
super(strings.ERROR.NO_SESSION);
}
}
/**
* Validation error class.
*/
export class ValidationError extends BaseError {
constructor() {
super(strings.ERROR.VALIDATION);
}
}
/**
* Not found error class.
*/
export class NotFoundError extends BaseError {
constructor() {
super(strings.ERROR.NOT_FOUND);
}
}
export default BaseError;

View File

@ -0,0 +1 @@
export const fetcher = (url: string) => fetch(url).then((response) => response.json());

View File

@ -0,0 +1,35 @@
import type { UserRole } from "@/server/auth/permissions";
import type { users } from "@/server/db/schema";
import type { InferSelectModel } from "drizzle-orm";
import type { RegisteredDatabaseUserAttributes } from "lucia";
// Constants for better readability
export const IS = false;
export const IS_NOT = true;
/**
* Checks if a user has the required role(s).
* @param user - The user to check.
* @param negate - Whether to negate the result.
* @param roleRequirements - The required role(s).
* @returns Whether the user has the required role(s).
*/
export function guard(
user: RegisteredDatabaseUserAttributes | InferSelectModel<typeof users> | undefined | null,
negate: boolean,
roleRequirements: UserRole | UserRole[],
) {
// Early return for unauthenticated users
if (!user) {
return true;
}
// Normalize roleRequirements to an array
const requiredRoles = Array.isArray(roleRequirements) ? roleRequirements : [roleRequirements];
// Check if the user's role is in the required roles
const userHasRequiredRole = requiredRoles.includes(user.role as UserRole);
// Return the result, negated if necessary
return negate ? !userHasRequiredRole : userHasRequiredRole;
}

View File

@ -0,0 +1,33 @@
import type { UserRole } from "@/server/auth/permissions";
import type { users } from "@/server/db/schema";
import type { InferSelectModel } from "drizzle-orm";
import type { RegisteredDatabaseUserAttributes } from "lucia";
// Helpers to improve readability of the guard function
export const is = false;
export const is_not = true;
/**
* @deprecated
*/
export function guard(
user: RegisteredDatabaseUserAttributes | InferSelectModel<typeof users> | undefined | null,
negate: boolean,
roleRequirements: UserRole | UserRole[],
) {
if (!user) {
return true; // Guard against unauthenticated users
}
const hasRole = Array.isArray(roleRequirements)
? roleRequirements.includes(user?.role as UserRole)
: user?.role === roleRequirements;
return negate ? !hasRole : hasRole;
}
export class PermissionError extends Error {
constructor() {
super("Du besitzt nicht die erforderlichen Berechtigungen um diese Aktion auszuführen.");
}
}

View File

@ -0,0 +1,41 @@
import type { InferResultType } from "@/utils/drizzle";
export enum PrinterStatus {
IDLE = 0,
OUT_OF_ORDER = 1,
RESERVED = 2,
}
export function derivePrinterStatus(
printer: InferResultType<"printers", { printJobs: true }>,
) {
if (printer.status === PrinterStatus.OUT_OF_ORDER) {
return PrinterStatus.OUT_OF_ORDER;
}
const activePrintJob = printer.printJobs[0];
if (!activePrintJob || activePrintJob.aborted) {
return PrinterStatus.IDLE;
}
const now = Date.now();
const startAt = new Date(activePrintJob.startAt).getTime();
const endAt = startAt + activePrintJob.durationInMinutes * 60 * 1000;
if (now < endAt) {
return PrinterStatus.RESERVED;
}
return PrinterStatus.IDLE;
}
export function translatePrinterStatus(status: PrinterStatus) {
switch (status) {
case PrinterStatus.IDLE:
return "Verfügbar";
case PrinterStatus.OUT_OF_ORDER:
return "Außer Betrieb";
case PrinterStatus.RESERVED:
return "Reserviert";
}
}

View File

@ -0,0 +1,11 @@
/**
* Contains all strings used in the application.
*/
export default {
ERROR: {
PERMISSION: "Du besitzt nicht die erforderlichen Berechtigungen um diese Aktion auszuführen.",
VALIDATION: "Die Eingabe ist ungültig.",
NOT_FOUND: "Die angeforderten Daten konnten nicht gefunden werden.",
NO_SESSION: "Du bist nicht angemeldet.",
},
};

View File

@ -0,0 +1,11 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
/**
* Utility function to merge classes with tailwindcss.
* @param inputs Classes to merge
* @returns classes
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}