format codebase with prettier

This commit is contained in:
2026-01-04 16:17:36 +01:00
parent 77f15b6dd1
commit e3f7a778c7
63 changed files with 786 additions and 542 deletions

View File

@@ -1,7 +1,7 @@
import { User, CreateUserDTO, LoginDTO, AuthResponse } from '@caldav/shared';
import { UserRepository } from './interfaces';
import * as jwt from '../utils/jwt';
import * as password from '../utils/password';
import { User, CreateUserDTO, LoginDTO, AuthResponse } from "@caldav/shared";
import { UserRepository } from "./interfaces";
import * as jwt from "../utils/jwt";
import * as password from "../utils/password";
export class AuthService {
constructor(private userRepo: UserRepository) {}
@@ -9,21 +9,21 @@ export class AuthService {
async login(data: LoginDTO): Promise<AuthResponse> {
const user = await this.userRepo.findByEmail(data.email);
if (!user || !user.passwordHash) {
throw new Error('Invalid credentials');
throw new Error("Invalid credentials");
}
const isValid = await password.compare(data.password, user.passwordHash);
if (!isValid) {
throw new Error('Invalid credentials');
throw new Error("Invalid credentials");
}
return { user, accessToken: '' };
return { user, accessToken: "" };
}
async register(data: CreateUserDTO): Promise<AuthResponse> {
const existingUser = await this.userRepo.findByEmail(data.email);
if (existingUser) {
throw new Error('Email already exists');
throw new Error("Email already exists");
}
const passwordHash = await password.hash(data.password);
@@ -33,14 +33,14 @@ export class AuthService {
passwordHash,
});
return { user, accessToken: '' };
return { user, accessToken: "" };
}
async refreshToken(refreshToken: string): Promise<AuthResponse> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
async logout(userId: string): Promise<void> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
}

View File

@@ -1,6 +1,17 @@
import { ChatMessage, ChatResponse, SendMessageDTO, ConversationSummary, GetMessagesOptions, ProposedEventChange, getDay, CreateEventDTO, UpdateEventDTO, EventAction } from '@caldav/shared';
import { ChatRepository, EventRepository, AIProvider } from './interfaces';
import { getWeeksOverview, getMonthOverview } from '../utils/eventFormatters';
import {
ChatMessage,
ChatResponse,
SendMessageDTO,
ConversationSummary,
GetMessagesOptions,
ProposedEventChange,
getDay,
CreateEventDTO,
UpdateEventDTO,
EventAction,
} from "@caldav/shared";
import { ChatRepository, EventRepository, AIProvider } from "./interfaces";
import { getWeeksOverview, getMonthOverview } from "../utils/eventFormatters";
type TestResponse = { content: string; proposedChange?: ProposedEventChange };
@@ -9,139 +20,147 @@ let responseIndex = 8;
// Static test responses (event proposals)
const staticResponses: TestResponse[] = [
// {{{
// Response 0: Meeting mit Jens - next Friday 14:00
{
content: "Alles klar! Ich erstelle dir einen Termin für das Meeting mit Jens am nächsten Freitag um 14:00 Uhr:",
proposedChange: {
action: 'create',
event: {
title: "Meeting mit Jens",
startTime: getDay('Friday', 1, 14, 0),
endTime: getDay('Friday', 1, 15, 0),
description: "Arbeitstreffen",
}
}
// {{{
// Response 0: Meeting mit Jens - next Friday 14:00
{
content:
"Alles klar! Ich erstelle dir einen Termin für das Meeting mit Jens am nächsten Freitag um 14:00 Uhr:",
proposedChange: {
action: "create",
event: {
title: "Meeting mit Jens",
startTime: getDay("Friday", 1, 14, 0),
endTime: getDay("Friday", 1, 15, 0),
description: "Arbeitstreffen",
},
},
// Response 1: Recurring event - every Saturday 10:00
{
content: "Verstanden! Ich erstelle einen wiederkehrenden Termin: Jeden Samstag um 10:00 Uhr Badezimmer putzen:",
proposedChange: {
action: 'create',
event: {
title: "Badezimmer putzen",
startTime: getDay('Saturday', 1, 10, 0),
endTime: getDay('Saturday', 1, 11, 0),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=SA",
}
}
},
// Response 1: Recurring event - every Saturday 10:00
{
content:
"Verstanden! Ich erstelle einen wiederkehrenden Termin: Jeden Samstag um 10:00 Uhr Badezimmer putzen:",
proposedChange: {
action: "create",
event: {
title: "Badezimmer putzen",
startTime: getDay("Saturday", 1, 10, 0),
endTime: getDay("Saturday", 1, 11, 0),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=SA",
},
},
// Response 2: 2-week overview (DYNAMIC - placeholder)
{ content: '' },
// Response 3: Delete "Meeting mit Jens" (DYNAMIC - placeholder)
{ content: '' },
// Response 4: Doctor appointment with description
{
content: "Ich habe dir einen Arzttermin eingetragen. Denk daran, deine Versichertenkarte mitzunehmen!",
proposedChange: {
action: 'create',
event: {
title: "Arzttermin Dr. Müller",
startTime: getDay('Wednesday', 1, 9, 30),
endTime: getDay('Wednesday', 1, 10, 30),
description: "Routineuntersuchung - Versichertenkarte nicht vergessen",
}
}
},
// Response 2: 2-week overview (DYNAMIC - placeholder)
{ content: "" },
// Response 3: Delete "Meeting mit Jens" (DYNAMIC - placeholder)
{ content: "" },
// Response 4: Doctor appointment with description
{
content:
"Ich habe dir einen Arzttermin eingetragen. Denk daran, deine Versichertenkarte mitzunehmen!",
proposedChange: {
action: "create",
event: {
title: "Arzttermin Dr. Müller",
startTime: getDay("Wednesday", 1, 9, 30),
endTime: getDay("Wednesday", 1, 10, 30),
description: "Routineuntersuchung - Versichertenkarte nicht vergessen",
},
},
// Response 5: Birthday - yearly recurring
{
content: "Geburtstage vergisst man leicht - aber nicht mit mir! Ich habe Mamas Geburtstag eingetragen:",
proposedChange: {
action: 'create',
event: {
title: "Mamas Geburtstag",
startTime: getDay('Thursday', 2, 0, 0),
endTime: getDay('Thursday', 2, 23, 59),
isRecurring: true,
recurrenceRule: "FREQ=YEARLY",
}
}
},
// Response 5: Birthday - yearly recurring
{
content:
"Geburtstage vergisst man leicht - aber nicht mit mir! Ich habe Mamas Geburtstag eingetragen:",
proposedChange: {
action: "create",
event: {
title: "Mamas Geburtstag",
startTime: getDay("Thursday", 2, 0, 0),
endTime: getDay("Thursday", 2, 23, 59),
isRecurring: true,
recurrenceRule: "FREQ=YEARLY",
},
},
// Response 6: Gym - recurring for 2 months (8 weeks)
{
content: "Perfekt! Ich habe dein Probetraining eingetragen - jeden Dienstag für die nächsten 2 Monate:",
proposedChange: {
action: 'create',
event: {
title: "Fitnessstudio Probetraining",
startTime: getDay('Tuesday', 1, 18, 0),
endTime: getDay('Tuesday', 1, 19, 30),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=TU;COUNT=8",
}
}
},
// Response 6: Gym - recurring for 2 months (8 weeks)
{
content:
"Perfekt! Ich habe dein Probetraining eingetragen - jeden Dienstag für die nächsten 2 Monate:",
proposedChange: {
action: "create",
event: {
title: "Fitnessstudio Probetraining",
startTime: getDay("Tuesday", 1, 18, 0),
endTime: getDay("Tuesday", 1, 19, 30),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=TU;COUNT=8",
},
},
// Response 7: 1-week overview (DYNAMIC - placeholder)
{ content: '' },
// Response 8: Help response (text only)
{
content: "Ich bin dein Kalender-Assistent! Du kannst mir einfach sagen, welche Termine du erstellen, ändern oder löschen möchtest. Zum Beispiel:\n\n" +
"• \"Erstelle einen Termin für morgen um 15 Uhr\"\n" +
"• \"Was habe ich nächste Woche vor?\"\n" +
"• \"Verschiebe das Meeting auf Donnerstag\"\n\n" +
"Wie kann ich dir helfen?",
},
// Response 7: 1-week overview (DYNAMIC - placeholder)
{ content: "" },
// Response 8: Help response (text only)
{
content:
"Ich bin dein Kalender-Assistent! Du kannst mir einfach sagen, welche Termine du erstellen, ändern oder löschen möchtest. Zum Beispiel:\n\n" +
'• "Erstelle einen Termin für morgen um 15 Uhr"\n' +
'• "Was habe ich nächste Woche vor?"\n' +
'• "Verschiebe das Meeting auf Donnerstag"\n\n' +
"Wie kann ich dir helfen?",
},
// Response 9: Phone call - short appointment
{
content:
"Alles klar! Ich habe das Telefonat mit deiner Mutter eingetragen:",
proposedChange: {
action: "create",
event: {
title: "Telefonat mit Mama",
startTime: getDay("Sunday", 0, 11, 0),
endTime: getDay("Sunday", 0, 11, 30),
},
},
// Response 9: Phone call - short appointment
{
content: "Alles klar! Ich habe das Telefonat mit deiner Mutter eingetragen:",
proposedChange: {
action: 'create',
event: {
title: "Telefonat mit Mama",
startTime: getDay('Sunday', 0, 11, 0),
endTime: getDay('Sunday', 0, 11, 30),
}
}
},
// Response 10: Update "Telefonat mit Mama" +2 days (DYNAMIC - placeholder)
{ content: "" },
// Response 11: Birthday party - evening event
{
content: "Super! Die Geburtstagsfeier ist eingetragen. Viel Spaß!",
proposedChange: {
action: "create",
event: {
title: "Geburtstagsfeier Lisa",
startTime: getDay("Saturday", 2, 19, 0),
endTime: getDay("Saturday", 2, 23, 0),
description: "Geschenk: Buch über Fotografie",
},
},
// Response 10: Update "Telefonat mit Mama" +2 days (DYNAMIC - placeholder)
{ content: '' },
// Response 11: Birthday party - evening event
{
content: "Super! Die Geburtstagsfeier ist eingetragen. Viel Spaß!",
proposedChange: {
action: 'create',
event: {
title: "Geburtstagsfeier Lisa",
startTime: getDay('Saturday', 2, 19, 0),
endTime: getDay('Saturday', 2, 23, 0),
description: "Geschenk: Buch über Fotografie",
}
}
},
// Response 12: Language course - limited to 8 weeks
{
content:
"Dein Spanischkurs ist eingetragen! Er läuft jeden Donnerstag für die nächsten 8 Wochen:",
proposedChange: {
action: "create",
event: {
title: "Spanischkurs VHS",
startTime: getDay("Thursday", 1, 19, 0),
endTime: getDay("Thursday", 1, 20, 30),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=TH;COUNT=8",
},
},
// Response 12: Language course - limited to 8 weeks
{
content: "Dein Spanischkurs ist eingetragen! Er läuft jeden Donnerstag für die nächsten 8 Wochen:",
proposedChange: {
action: 'create',
event: {
title: "Spanischkurs VHS",
startTime: getDay('Thursday', 1, 19, 0),
endTime: getDay('Thursday', 1, 20, 30),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=TH;COUNT=8",
}
}
},
// Response 13: Monthly overview (DYNAMIC - placeholder)
{ content: '' },
// }}}
},
// Response 13: Monthly overview (DYNAMIC - placeholder)
{ content: "" },
// }}}
];
async function getTestResponse(
index: number,
eventRepo: EventRepository,
userId: string
userId: string,
): Promise<TestResponse> {
const responseIdx = index % staticResponses.length;
@@ -153,14 +172,15 @@ async function getTestResponse(
if (responseIdx === 3) {
// Delete "Meeting mit Jens"
const events = await eventRepo.findByUserId(userId);
const jensEvent = events.find(e => e.title === 'Meeting mit Jens');
const jensEvent = events.find((e) => e.title === "Meeting mit Jens");
if (jensEvent) {
return {
content: "Alles klar, ich lösche den Termin 'Meeting mit Jens' für dich:",
content:
"Alles klar, ich lösche den Termin 'Meeting mit Jens' für dich:",
proposedChange: {
action: 'delete',
action: "delete",
eventId: jensEvent.id,
}
},
};
}
return { content: "Ich konnte keinen Termin 'Meeting mit Jens' finden." };
@@ -173,16 +193,17 @@ async function getTestResponse(
if (responseIdx === 10) {
// Update "Telefonat mit Mama" +2 days
const events = await eventRepo.findByUserId(userId);
const mamaEvent = events.find(e => e.title === 'Telefonat mit Mama');
const mamaEvent = events.find((e) => e.title === "Telefonat mit Mama");
if (mamaEvent) {
const newStart = new Date(mamaEvent.startTime);
newStart.setDate(newStart.getDate() + 2);
const newEnd = new Date(mamaEvent.endTime);
newEnd.setDate(newEnd.getDate() + 2);
return {
content: "Alles klar, ich verschiebe das Telefonat mit Mama um 2 Tage nach hinten:",
content:
"Alles klar, ich verschiebe das Telefonat mit Mama um 2 Tage nach hinten:",
proposedChange: {
action: 'update',
action: "update",
eventId: mamaEvent.id,
updates: { startTime: newStart, endTime: newEnd },
// Include event with new times for display
@@ -191,8 +212,8 @@ async function getTestResponse(
startTime: newStart,
endTime: newEnd,
description: mamaEvent.description,
}
}
},
},
};
}
return { content: "Ich konnte keinen Termin 'Telefonat mit Mama' finden." };
@@ -200,7 +221,14 @@ async function getTestResponse(
if (responseIdx === 13) {
const now = new Date();
return { content: await getMonthOverview(eventRepo, userId, now.getFullYear(), now.getMonth()) };
return {
content: await getMonthOverview(
eventRepo,
userId,
now.getFullYear(),
now.getMonth(),
),
};
}
return staticResponses[responseIdx];
@@ -210,17 +238,24 @@ export class ChatService {
constructor(
private chatRepo: ChatRepository,
private eventRepo: EventRepository,
private aiProvider: AIProvider
private aiProvider: AIProvider,
) {}
async processMessage(userId: string, data: SendMessageDTO): Promise<ChatResponse> {
const response = await getTestResponse(responseIndex, this.eventRepo, userId);
async processMessage(
userId: string,
data: SendMessageDTO,
): Promise<ChatResponse> {
const response = await getTestResponse(
responseIndex,
this.eventRepo,
userId,
);
responseIndex++;
const message: ChatMessage = {
id: Date.now().toString(),
conversationId: data.conversationId || 'temp-conv-id',
sender: 'assistant',
conversationId: data.conversationId || "temp-conv-id",
sender: "assistant",
content: response.content,
proposedChange: response.proposedChange,
};
@@ -235,49 +270,57 @@ export class ChatService {
action: EventAction,
event?: CreateEventDTO,
eventId?: string,
updates?: UpdateEventDTO
updates?: UpdateEventDTO,
): Promise<ChatResponse> {
let content: string;
if (action === 'create' && event) {
if (action === "create" && event) {
const createdEvent = await this.eventRepo.create(userId, event);
content = `Der Termin "${createdEvent.title}" wurde erstellt.`;
} else if (action === 'update' && eventId && updates) {
} else if (action === "update" && eventId && updates) {
const updatedEvent = await this.eventRepo.update(eventId, updates);
content = updatedEvent
? `Der Termin "${updatedEvent.title}" wurde aktualisiert.`
: 'Termin nicht gefunden.';
} else if (action === 'delete' && eventId) {
: "Termin nicht gefunden.";
} else if (action === "delete" && eventId) {
await this.eventRepo.delete(eventId);
content = 'Der Termin wurde gelöscht.';
content = "Der Termin wurde gelöscht.";
} else {
content = 'Ungültige Aktion.';
content = "Ungültige Aktion.";
}
const message: ChatMessage = {
id: Date.now().toString(),
conversationId,
sender: 'assistant',
sender: "assistant",
content,
};
return { message, conversationId };
}
async rejectEvent(userId: string, conversationId: string, messageId: string): Promise<ChatResponse> {
async rejectEvent(
userId: string,
conversationId: string,
messageId: string,
): Promise<ChatResponse> {
const message: ChatMessage = {
id: Date.now().toString(),
conversationId,
sender: 'assistant',
content: 'Der Vorschlag wurde abgelehnt.',
sender: "assistant",
content: "Der Vorschlag wurde abgelehnt.",
};
return { message, conversationId };
}
async getConversations(userId: string): Promise<ConversationSummary[]> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
async getConversation(userId: string, conversationId: string, options?: GetMessagesOptions): Promise<ChatMessage[]> {
throw new Error('Not implemented');
async getConversation(
userId: string,
conversationId: string,
options?: GetMessagesOptions,
): Promise<ChatMessage[]> {
throw new Error("Not implemented");
}
}

View File

@@ -1,30 +1,38 @@
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared';
import { EventRepository } from './interfaces';
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared";
import { EventRepository } from "./interfaces";
export class EventService {
constructor(private eventRepo: EventRepository) {}
async create(userId: string, data: CreateEventDTO): Promise<CalendarEvent> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
async getById(id: string, userId: string): Promise<CalendarEvent | null> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
async getAll(userId: string): Promise<CalendarEvent[]> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
async getByDateRange(userId: string, startDate: Date, endDate: Date): Promise<CalendarEvent[]> {
throw new Error('Not implemented');
async getByDateRange(
userId: string,
startDate: Date,
endDate: Date,
): Promise<CalendarEvent[]> {
throw new Error("Not implemented");
}
async update(id: string, userId: string, data: UpdateEventDTO): Promise<CalendarEvent | null> {
throw new Error('Not implemented');
async update(
id: string,
userId: string,
data: UpdateEventDTO,
): Promise<CalendarEvent | null> {
throw new Error("Not implemented");
}
async delete(id: string, userId: string): Promise<boolean> {
throw new Error('Not implemented');
throw new Error("Not implemented");
}
}

View File

@@ -1,4 +1,4 @@
export * from './AuthService';
export * from './ChatService';
export * from './EventService';
export * from './interfaces';
export * from "./AuthService";
export * from "./ChatService";
export * from "./EventService";
export * from "./interfaces";

View File

@@ -1,4 +1,8 @@
import { CalendarEvent, ChatMessage, ProposedEventChange } from '@caldav/shared';
import {
CalendarEvent,
ChatMessage,
ProposedEventChange,
} from "@caldav/shared";
export interface AIContext {
userId: string;

View File

@@ -1,4 +1,9 @@
import { ChatMessage, Conversation, CreateMessageDTO, GetMessagesOptions } from '@caldav/shared';
import {
ChatMessage,
Conversation,
CreateMessageDTO,
GetMessagesOptions,
} from "@caldav/shared";
export interface ChatRepository {
// Conversations
@@ -6,6 +11,12 @@ export interface ChatRepository {
createConversation(userId: string): Promise<Conversation>;
// Messages (cursor-based pagination)
getMessages(conversationId: string, options?: GetMessagesOptions): Promise<ChatMessage[]>;
createMessage(conversationId: string, message: CreateMessageDTO): Promise<ChatMessage>;
getMessages(
conversationId: string,
options?: GetMessagesOptions,
): Promise<ChatMessage[]>;
createMessage(
conversationId: string,
message: CreateMessageDTO,
): Promise<ChatMessage>;
}

View File

@@ -1,9 +1,13 @@
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared';
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared";
export interface EventRepository {
findById(id: string): Promise<CalendarEvent | null>;
findByUserId(userId: string): Promise<CalendarEvent[]>;
findByDateRange(userId: string, startDate: Date, endDate: Date): Promise<CalendarEvent[]>;
findByDateRange(
userId: string,
startDate: Date,
endDate: Date,
): Promise<CalendarEvent[]>;
create(userId: string, data: CreateEventDTO): Promise<CalendarEvent>;
update(id: string, data: UpdateEventDTO): Promise<CalendarEvent | null>;
delete(id: string): Promise<boolean>;

View File

@@ -1,4 +1,4 @@
import { User } from '@caldav/shared';
import { User } from "@caldav/shared";
export interface CreateUserData {
email: string;

View File

@@ -1,4 +1,4 @@
export * from './AIProvider';
export * from './UserRepository';
export * from './EventRepository';
export * from './ChatRepository';
export * from "./AIProvider";
export * from "./UserRepository";
export * from "./EventRepository";
export * from "./ChatRepository";