Login System + API

This commit is contained in:
Moritz Utcke
2023-03-25 19:51:35 +04:00
parent dcbdf0e8d0
commit 5285f832bf
19 changed files with 551 additions and 7 deletions

38
src/lib/APIResponse.ts Normal file
View File

@@ -0,0 +1,38 @@
import type { ZodError } from "zod";
export function success(data: any = {}) {
return new Response(JSON.stringify({
success: true,
data
}))
}
export function error(errors: any[]) {
return new Response(JSON.stringify({
success: false,
errors
}))
}
export function MissingPropertyError(properties: string[]) {
return error(properties.map(property => {
return `Missing property '${property}' in request body.`
}));
}
/**
* Signalisiert ein fehlendes Objekt und gibt den Fehler als HTTP Response zurück.
* @param name Der Name der Entität, die nicht gefunden werden konnte.
* @returns {Response} HTTP Response Objekt
*/
export function MissingEntityError(name: string): Response {
return error([`Missing entity, ${name} does not exist.`])
}
export function ActionFailedError(): Response {
return error(["Failed executing action, error encountered."]);
}
export function InvalidDataError(err: ZodError): Response {
return error(err.issues);
}

9
src/lib/Ausweis/index.ts Normal file
View File

@@ -0,0 +1,9 @@
export class Ausweis {
public static fromPublicId(public_id: string) {
}
public static fromPrivateId(id: number) {
}
}

0
src/lib/Ausweis/type.ts Normal file
View File

10
src/lib/JsonWebToken.ts Normal file
View File

@@ -0,0 +1,10 @@
import * as jwt from "jwt-simple";
export function encodeToken(data: Record<string, any>) {
const token = jwt.encode(data, "yIvbgS$k7Bfc+mpV%TWDZAhje9#uJad4", "HS256");
return token;
}
export function decodeToken(token: string) {
return jwt.decode(token, "yIvbgS$k7Bfc+mpV%TWDZAhje9#uJad4");
}

22
src/lib/Password.ts Normal file
View File

@@ -0,0 +1,22 @@
import * as bcrypt from "bcrypt";
export async function hashPassword(password: string): Promise<string | null> {
const saltRounds = 4;
try {
const salt = await bcrypt.genSalt(saltRounds);
const hash = await bcrypt.hash(password, salt);
return hash;
} catch(e) {
return null;
}
}
export async function validatePassword(known: string, unknown: string): Promise<boolean> {
try {
const compare = await bcrypt.compare(unknown, known)
return compare
} catch(e) {
return false;
}
}

88
src/lib/User/index.ts Normal file
View File

@@ -0,0 +1,88 @@
import { db } from "../shared";
import { UserRegisterValidator, UserType, UserTypeValidator } from "./type";
import { v4 as uuid } from "uuid";
import { hashPassword } from "../Password";
export class User {
/**
* Sucht einen Nutzer in der Datenbank anhand seiner Public/Unique id und gibt ihn zurück.
* @param uid Die unique/public id des gesuchten Benutzers.
* @returns {UserType | null} Die Daten des Nutzers oder null falls dieser nicht gefunden werden kann.
*/
public static async fromPublicId(uid: string): Promise<UserType | null> {
if (!uid || typeof uid !== "string") {
return null;
}
const user = await db<UserType>("users").select("*").where("uid", uid).first();
if (!user) {
return null;
}
return user;
}
public static async fromUsername(username: string): Promise<UserType | null> {
if (!username || typeof username !== "string") {
return null;
}
const user = await db<UserType>("users").select("*").where("username", username).first();
if (!user) {
return null;
}
return user;
}
/**
* Sucht einen Nutzer in der Datenbank anhand seiner Private id und gibt ihn zurück.
* @param uid Die private id des gesuchten Benutzers.
* @returns {UserType | null} Die Daten des Nutzers oder null falls dieser nicht gefunden werden kann.
*/
public static async fromPrivateId(id: number): Promise<UserType | null> {
if (!id || typeof id !== "number") {
return null;
}
const user = await db<UserType>("users").select("*").where("id", id).first();
if (!user) {
return null;
}
return user;
}
public static async create(user: UserType): Promise<{uid: string, id: number} | null> {
if (!user || UserRegisterValidator.safeParse(user).success == false) {
return null;
}
const uid = uuid();
const hashedPassword = await hashPassword(user.password);
if (!hashedPassword) {
return null;
}
const result = await db<UserType>("users").insert({
username: user.username,
email: user.email,
password: hashedPassword,
uid: uid
}, ["id"])
if (!result) {
return null;
}
return {
uid,
id: result[0].id
}
}
}

17
src/lib/User/type.ts Normal file
View File

@@ -0,0 +1,17 @@
import { z } from "zod"
export const UserTypeValidator = z.object({
username: z.string().min(4).max(64),
id: z.number(),
uid: z.string().length(36),
email: z.string().max(255),
password: z.string().min(6),
})
export const UserRegisterValidator = z.object({
username: z.string().min(4).max(64),
email: z.string().max(255),
password: z.string().min(6),
})
export type UserType = z.infer<typeof UserTypeValidator>

19
src/lib/shared.ts Normal file
View File

@@ -0,0 +1,19 @@
import knex, { Knex } from "knex";
export function dbOpen(): Knex {
const db = knex({
client: 'pg',
connection: {
host : '127.0.0.1',
port : 5436,
user : 'main',
password : 'hHMP8cd^N3SnzGRR',
database : 'main'
}
})
return db;
}
export const db = dbOpen();