141 lines
3.1 KiB
TypeScript
141 lines
3.1 KiB
TypeScript
import type {
|
|
User,
|
|
UserRole,
|
|
LoginCredentials,
|
|
RegisterData,
|
|
} from "../types/user.js";
|
|
import { hashPassword, comparePassword } from "./password.js";
|
|
import { createToken, verifyToken } from "./jwt.js";
|
|
import { logger } from "../monitoring/logger.js";
|
|
|
|
/**
|
|
* Mock User Store (später durch Datenbank ersetzen)
|
|
*/
|
|
const users = new Map<string, User & { passwordHash: string }>();
|
|
|
|
/**
|
|
* User Service für Authentication
|
|
*/
|
|
export class UserService {
|
|
/**
|
|
* Registriert einen neuen User
|
|
*/
|
|
async register(
|
|
data: RegisterData,
|
|
role: UserRole = "customer"
|
|
): Promise<{
|
|
user: User;
|
|
token: string;
|
|
}> {
|
|
// Prüfe ob User bereits existiert
|
|
const existingUser = Array.from(users.values()).find(
|
|
(u) => u.email === data.email
|
|
);
|
|
if (existingUser) {
|
|
throw new Error("User mit dieser E-Mail existiert bereits");
|
|
}
|
|
|
|
// Hashe Passwort
|
|
const passwordHash = await hashPassword(data.password);
|
|
|
|
// Erstelle User
|
|
const user: User = {
|
|
id: `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
email: data.email,
|
|
name: data.name,
|
|
role,
|
|
createdAt: new Date(),
|
|
};
|
|
|
|
// Speichere User
|
|
users.set(user.id, { ...user, passwordHash });
|
|
|
|
// Erstelle Token
|
|
const token = createToken({
|
|
userId: user.id,
|
|
email: user.email,
|
|
role: user.role,
|
|
});
|
|
|
|
logger.info("User registered", { userId: user.id, email: user.email });
|
|
|
|
return { user, token };
|
|
}
|
|
|
|
/**
|
|
* Login eines Users
|
|
*/
|
|
async login(credentials: LoginCredentials): Promise<{
|
|
user: User;
|
|
token: string;
|
|
}> {
|
|
// Finde User
|
|
const userEntry = Array.from(users.values()).find(
|
|
(u) => u.email === credentials.email
|
|
);
|
|
|
|
if (!userEntry) {
|
|
throw new Error("Ungültige E-Mail oder Passwort");
|
|
}
|
|
|
|
// Vergleiche Passwort
|
|
const isValid = await comparePassword(
|
|
credentials.password,
|
|
userEntry.passwordHash
|
|
);
|
|
|
|
if (!isValid) {
|
|
throw new Error("Ungültige E-Mail oder Passwort");
|
|
}
|
|
|
|
// Erstelle User-Objekt ohne Passwort
|
|
const user: User = {
|
|
id: userEntry.id,
|
|
email: userEntry.email,
|
|
name: userEntry.name,
|
|
role: userEntry.role,
|
|
createdAt: userEntry.createdAt,
|
|
};
|
|
|
|
// Erstelle Token
|
|
const token = createToken({
|
|
userId: user.id,
|
|
email: user.email,
|
|
role: user.role,
|
|
});
|
|
|
|
logger.info("User logged in", { userId: user.id, email: user.email });
|
|
|
|
return { user, token };
|
|
}
|
|
|
|
/**
|
|
* Holt User anhand der ID
|
|
*/
|
|
async getUserById(userId: string): Promise<User | null> {
|
|
const userEntry = users.get(userId);
|
|
if (!userEntry) return null;
|
|
|
|
return {
|
|
id: userEntry.id,
|
|
email: userEntry.email,
|
|
name: userEntry.name,
|
|
role: userEntry.role,
|
|
createdAt: userEntry.createdAt,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Holt User anhand des Tokens
|
|
*/
|
|
async getUserFromToken(token: string): Promise<User | null> {
|
|
const payload = verifyToken(token);
|
|
if (!payload) return null;
|
|
|
|
return this.getUserById(payload.userId);
|
|
}
|
|
}
|
|
|
|
// Singleton-Instanz
|
|
export const userService = new UserService();
|