implement backend skeleton with MongoDB and Claude AI integration

- Add controllers (Auth, Chat, Event) with placeholder implementations
- Add services (Auth, Chat, Event) with business logic interfaces
- Add repositories with MongoDB/Mongoose models (User, Event, Chat)
- Add middleware for JWT authentication
- Add Claude AI adapter implementing AIProvider interface
- Add utility modules for JWT and password handling
- Add shared types and DTOs for User, CalendarEvent, ChatMessage
- Configure routes with proper endpoint structure
- Update app.ts with dependency injection setup
- Add required dependencies: mongoose, bcrypt, jsonwebtoken, @anthropic-ai/sdk
This commit is contained in:
2026-01-02 20:09:42 +01:00
parent 5af6cffa9c
commit 5cc1ce7f1c
47 changed files with 1397 additions and 13 deletions

View File

@@ -0,0 +1 @@
export * from './mongo';

View File

@@ -0,0 +1,23 @@
import { ChatMessage, Conversation, CreateMessageDTO, GetMessagesOptions } from '@caldav/shared';
import { ChatRepository } from '../../services/interfaces';
import { ChatMessageModel, ConversationModel } from './models';
export class MongoChatRepository implements ChatRepository {
// Conversations
async getConversationsByUser(userId: string): Promise<Conversation[]> {
throw new Error('Not implemented');
}
async createConversation(userId: string): Promise<Conversation> {
throw new Error('Not implemented');
}
// Messages (cursor-based pagination)
async getMessages(conversationId: string, options?: GetMessagesOptions): Promise<ChatMessage[]> {
throw new Error('Not implemented');
}
async createMessage(conversationId: string, message: CreateMessageDTO): Promise<ChatMessage> {
throw new Error('Not implemented');
}
}

View File

@@ -0,0 +1,29 @@
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared';
import { EventRepository } from '../../services/interfaces';
import { EventModel } from './models';
export class MongoEventRepository implements EventRepository {
async findById(id: string): Promise<CalendarEvent | null> {
throw new Error('Not implemented');
}
async findByUserId(userId: string): Promise<CalendarEvent[]> {
throw new Error('Not implemented');
}
async findByDateRange(userId: string, startDate: Date, endDate: Date): Promise<CalendarEvent[]> {
throw new Error('Not implemented');
}
async create(userId: string, data: CreateEventDTO): Promise<CalendarEvent> {
throw new Error('Not implemented');
}
async update(id: string, data: UpdateEventDTO): Promise<CalendarEvent | null> {
throw new Error('Not implemented');
}
async delete(id: string): Promise<boolean> {
throw new Error('Not implemented');
}
}

View File

@@ -0,0 +1,17 @@
import { User } from '@caldav/shared';
import { UserRepository, CreateUserData } from '../../services/interfaces';
import { UserModel } from './models';
export class MongoUserRepository implements UserRepository {
async findById(id: string): Promise<User | null> {
throw new Error('Not implemented');
}
async findByEmail(email: string): Promise<User | null> {
throw new Error('Not implemented');
}
async create(data: CreateUserData): Promise<User> {
throw new Error('Not implemented');
}
}

View File

@@ -0,0 +1,3 @@
export * from './MongoUserRepository';
export * from './MongoEventRepository';
export * from './MongoChatRepository';

View File

@@ -0,0 +1,74 @@
import mongoose, { Schema, Document } from 'mongoose';
import { ChatMessage, Conversation, CreateEventDTO } from '@caldav/shared';
export interface ChatMessageDocument extends Omit<ChatMessage, 'id'>, Document {}
export interface ConversationDocument extends Omit<Conversation, 'id'>, Document {}
const ProposedEventSchema = new Schema<CreateEventDTO>(
{
title: { type: String, required: true },
description: { type: String },
startTime: { type: Date, required: true },
endTime: { type: Date, required: true },
note: { type: String },
isRecurring: { type: Boolean },
recurrenceRule: { type: String },
},
{ _id: false }
);
const ChatMessageSchema = new Schema<ChatMessageDocument>(
{
conversationId: {
type: String,
required: true,
},
sender: {
type: String,
enum: ['user', 'assistant'],
required: true,
},
content: {
type: String,
required: true,
},
proposedEvent: {
type: ProposedEventSchema,
},
},
{
timestamps: true,
toJSON: {
transform: (_, ret: Record<string, unknown>) => {
ret.id = String(ret._id);
delete ret._id;
delete ret.__v;
return ret;
},
},
}
);
const ConversationSchema = new Schema<ConversationDocument>(
{
userId: {
type: String,
required: true,
index: true,
},
},
{
timestamps: true,
toJSON: {
transform: (_, ret: Record<string, unknown>) => {
ret.id = String(ret._id);
delete ret._id;
delete ret.__v;
return ret;
},
},
}
);
export const ChatMessageModel = mongoose.model<ChatMessageDocument>('ChatMessage', ChatMessageSchema);
export const ConversationModel = mongoose.model<ConversationDocument>('Conversation', ConversationSchema);

View File

@@ -0,0 +1,56 @@
import mongoose, { Schema, Document } from 'mongoose';
import { CalendarEvent } from '@caldav/shared';
export interface EventDocument extends Omit<CalendarEvent, 'id'>, Document {}
const EventSchema = new Schema<EventDocument>(
{
userId: {
type: String,
required: true,
index: true,
},
title: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
trim: true,
},
startTime: {
type: Date,
required: true,
},
endTime: {
type: Date,
required: true,
},
note: {
type: String,
},
isRecurring: {
type: Boolean,
default: false,
},
recurrenceRule: {
type: String,
},
},
{
timestamps: true,
toJSON: {
transform: (_, ret: Record<string, unknown>) => {
ret.id = String(ret._id);
delete ret._id;
delete ret.__v;
return ret;
},
},
}
);
EventSchema.index({ userId: 1, startTime: 1, endTime: 1 });
export const EventModel = mongoose.model<EventDocument>('Event', EventSchema);

View File

@@ -0,0 +1,39 @@
import mongoose, { Schema, Document } from 'mongoose';
import { User } from '@caldav/shared';
export interface UserDocument extends Omit<User, 'id'>, Document {}
const UserSchema = new Schema<UserDocument>(
{
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
},
displayName: {
type: String,
required: true,
trim: true,
},
passwordHash: {
type: String,
required: true,
},
},
{
timestamps: true,
toJSON: {
transform: (_, ret: Record<string, unknown>) => {
ret.id = String(ret._id);
delete ret._id;
delete ret.__v;
delete ret.passwordHash;
return ret;
},
},
}
);
export const UserModel = mongoose.model<UserDocument>('User', UserSchema);

View File

@@ -0,0 +1,3 @@
export * from './UserModel';
export * from './EventModel';
export * from './ChatModel';