mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-25 16:34:39 +02:00
Merge branch 'dev-1' into dev
This commit is contained in:
commit
d41d060bb3
1770 changed files with 168028 additions and 31031 deletions
20
apps-archived/moodlit/apps/backend/src/app.module.ts
Normal file
20
apps-archived/moodlit/apps/backend/src/app.module.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { DatabaseModule } from './db/database.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { MoodsModule } from './moods/moods.module';
|
||||
import { SequencesModule } from './sequences/sequences.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: '.env',
|
||||
}),
|
||||
DatabaseModule,
|
||||
HealthModule,
|
||||
MoodsModule,
|
||||
SequencesModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
38
apps-archived/moodlit/apps/backend/src/db/connection.ts
Normal file
38
apps-archived/moodlit/apps/backend/src/db/connection.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import * as schema from './schema';
|
||||
|
||||
// Use require for postgres to avoid ESM/CommonJS interop issues
|
||||
|
||||
const postgres = require('postgres');
|
||||
|
||||
let connection: ReturnType<typeof postgres> | null = null;
|
||||
let db: ReturnType<typeof drizzle> | null = null;
|
||||
|
||||
export function getConnection(databaseUrl: string) {
|
||||
if (!connection) {
|
||||
connection = postgres(databaseUrl, {
|
||||
max: 10,
|
||||
idle_timeout: 20,
|
||||
connect_timeout: 10,
|
||||
});
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
export function getDb(databaseUrl: string) {
|
||||
if (!db) {
|
||||
const conn = getConnection(databaseUrl);
|
||||
db = drizzle(conn, { schema });
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
export async function closeConnection() {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
connection = null;
|
||||
db = null;
|
||||
}
|
||||
}
|
||||
|
||||
export type Database = ReturnType<typeof getDb>;
|
||||
29
apps-archived/moodlit/apps/backend/src/db/database.module.ts
Normal file
29
apps-archived/moodlit/apps/backend/src/db/database.module.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { Module, Global, OnModuleDestroy } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { getDb, closeConnection } from './connection';
|
||||
import type { Database } from './connection';
|
||||
|
||||
export const DATABASE_CONNECTION = 'DATABASE_CONNECTION';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: DATABASE_CONNECTION,
|
||||
useFactory: (configService: ConfigService): Database => {
|
||||
const databaseUrl = configService.get<string>('DATABASE_URL');
|
||||
if (!databaseUrl) {
|
||||
throw new Error('DATABASE_URL environment variable is not set');
|
||||
}
|
||||
return getDb(databaseUrl);
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
],
|
||||
exports: [DATABASE_CONNECTION],
|
||||
})
|
||||
export class DatabaseModule implements OnModuleDestroy {
|
||||
async onModuleDestroy() {
|
||||
await closeConnection();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export * from './moods.schema';
|
||||
export * from './sequences.schema';
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { pgTable, uuid, text, jsonb, boolean, timestamp } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const moods = pgTable('moods', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: text('user_id').notNull(),
|
||||
name: text('name').notNull(),
|
||||
colors: jsonb('colors').notNull().$type<string[]>(),
|
||||
animation: text('animation'),
|
||||
isDefault: boolean('is_default').default(false),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
});
|
||||
|
||||
export type Mood = typeof moods.$inferSelect;
|
||||
export type NewMood = typeof moods.$inferInsert;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { pgTable, uuid, text, jsonb, integer, timestamp } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const sequences = pgTable('sequences', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: text('user_id').notNull(),
|
||||
name: text('name').notNull(),
|
||||
moodIds: jsonb('mood_ids').notNull().$type<string[]>(),
|
||||
duration: integer('duration').default(30),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
});
|
||||
|
||||
export type Sequence = typeof sequences.$inferSelect;
|
||||
export type NewSequence = typeof sequences.$inferInsert;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
@Get()
|
||||
check() {
|
||||
return {
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
service: 'moods-backend',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { HealthController } from './health.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [HealthController],
|
||||
})
|
||||
export class HealthModule {}
|
||||
40
apps-archived/moodlit/apps/backend/src/main.ts
Normal file
40
apps-archived/moodlit/apps/backend/src/main.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
// Enable CORS for mobile and web apps
|
||||
const corsOrigins = process.env.CORS_ORIGINS?.split(',').map((origin) => origin.trim()) || [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:5173',
|
||||
'http://localhost:5182',
|
||||
'http://localhost:8081',
|
||||
'exp://localhost:8081',
|
||||
'http://localhost:3001',
|
||||
];
|
||||
|
||||
app.enableCors({
|
||||
origin: corsOrigins,
|
||||
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
// Enable validation
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
transform: true,
|
||||
forbidNonWhitelisted: true,
|
||||
})
|
||||
);
|
||||
|
||||
// Set global prefix for API routes
|
||||
app.setGlobalPrefix('api/v1');
|
||||
|
||||
const port = process.env.PORT || 3012;
|
||||
await app.listen(port);
|
||||
console.log(`Moods backend running on http://localhost:${port}`);
|
||||
}
|
||||
bootstrap();
|
||||
37
apps-archived/moodlit/apps/backend/src/moods/dto/index.ts
Normal file
37
apps-archived/moodlit/apps/backend/src/moods/dto/index.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { IsString, IsArray, IsBoolean, IsOptional } from 'class-validator';
|
||||
|
||||
export class CreateMoodDto {
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
colors: string[];
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
animation?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
export class UpdateMoodDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
name?: string;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
colors?: string[];
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
animation?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth';
|
||||
import { MoodsService } from './moods.service';
|
||||
import { CreateMoodDto, UpdateMoodDto } from './dto';
|
||||
|
||||
@Controller('moods')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class MoodsController {
|
||||
constructor(private readonly moodsService: MoodsService) {}
|
||||
|
||||
@Get()
|
||||
async findAll(@CurrentUser() user: CurrentUserData) {
|
||||
return this.moodsService.findAllByUser(user.userId);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string, @CurrentUser() user: CurrentUserData) {
|
||||
return this.moodsService.findOne(id, user.userId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() dto: CreateMoodDto, @CurrentUser() user: CurrentUserData) {
|
||||
return this.moodsService.create(user.userId, dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdateMoodDto,
|
||||
@CurrentUser() user: CurrentUserData
|
||||
) {
|
||||
return this.moodsService.update(id, user.userId, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') id: string, @CurrentUser() user: CurrentUserData) {
|
||||
await this.moodsService.delete(id, user.userId);
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
10
apps-archived/moodlit/apps/backend/src/moods/moods.module.ts
Normal file
10
apps-archived/moodlit/apps/backend/src/moods/moods.module.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { MoodsController } from './moods.controller';
|
||||
import { MoodsService } from './moods.service';
|
||||
|
||||
@Module({
|
||||
controllers: [MoodsController],
|
||||
providers: [MoodsService],
|
||||
exports: [MoodsService],
|
||||
})
|
||||
export class MoodsModule {}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import { Injectable, Inject, NotFoundException } from '@nestjs/common';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { DATABASE_CONNECTION } from '../db/database.module';
|
||||
import { type Database } from '../db/connection';
|
||||
import { moods, type Mood, type NewMood } from '../db/schema/moods.schema';
|
||||
import { CreateMoodDto, UpdateMoodDto } from './dto';
|
||||
|
||||
@Injectable()
|
||||
export class MoodsService {
|
||||
constructor(@Inject(DATABASE_CONNECTION) private db: Database) {}
|
||||
|
||||
async findAllByUser(userId: string): Promise<Mood[]> {
|
||||
return this.db.select().from(moods).where(eq(moods.userId, userId));
|
||||
}
|
||||
|
||||
async findOne(id: string, userId: string): Promise<Mood> {
|
||||
const [mood] = await this.db
|
||||
.select()
|
||||
.from(moods)
|
||||
.where(and(eq(moods.id, id), eq(moods.userId, userId)));
|
||||
|
||||
if (!mood) {
|
||||
throw new NotFoundException(`Mood with ID ${id} not found`);
|
||||
}
|
||||
|
||||
return mood;
|
||||
}
|
||||
|
||||
async create(userId: string, dto: CreateMoodDto): Promise<Mood> {
|
||||
const newMood: NewMood = {
|
||||
userId,
|
||||
name: dto.name,
|
||||
colors: dto.colors,
|
||||
animation: dto.animation,
|
||||
isDefault: dto.isDefault ?? false,
|
||||
};
|
||||
|
||||
const [mood] = await this.db.insert(moods).values(newMood).returning();
|
||||
return mood;
|
||||
}
|
||||
|
||||
async update(id: string, userId: string, dto: UpdateMoodDto): Promise<Mood> {
|
||||
// Verify the mood exists and belongs to the user
|
||||
await this.findOne(id, userId);
|
||||
|
||||
const [updated] = await this.db
|
||||
.update(moods)
|
||||
.set({
|
||||
...dto,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(and(eq(moods.id, id), eq(moods.userId, userId)))
|
||||
.returning();
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
async delete(id: string, userId: string): Promise<void> {
|
||||
// Verify the mood exists and belongs to the user
|
||||
await this.findOne(id, userId);
|
||||
|
||||
await this.db.delete(moods).where(and(eq(moods.id, id), eq(moods.userId, userId)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import { IsString, IsArray, IsNumber, IsOptional } from 'class-validator';
|
||||
|
||||
export class CreateSequenceDto {
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
moodIds: string[];
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export class UpdateSequenceDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
name?: string;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
moodIds?: string[];
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
duration?: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth';
|
||||
import { SequencesService } from './sequences.service';
|
||||
import { CreateSequenceDto, UpdateSequenceDto } from './dto';
|
||||
|
||||
@Controller('sequences')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class SequencesController {
|
||||
constructor(private readonly sequencesService: SequencesService) {}
|
||||
|
||||
@Get()
|
||||
async findAll(@CurrentUser() user: CurrentUserData) {
|
||||
return this.sequencesService.findAllByUser(user.userId);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string, @CurrentUser() user: CurrentUserData) {
|
||||
return this.sequencesService.findOne(id, user.userId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() dto: CreateSequenceDto, @CurrentUser() user: CurrentUserData) {
|
||||
return this.sequencesService.create(user.userId, dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdateSequenceDto,
|
||||
@CurrentUser() user: CurrentUserData
|
||||
) {
|
||||
return this.sequencesService.update(id, user.userId, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') id: string, @CurrentUser() user: CurrentUserData) {
|
||||
await this.sequencesService.delete(id, user.userId);
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { SequencesController } from './sequences.controller';
|
||||
import { SequencesService } from './sequences.service';
|
||||
|
||||
@Module({
|
||||
controllers: [SequencesController],
|
||||
providers: [SequencesService],
|
||||
exports: [SequencesService],
|
||||
})
|
||||
export class SequencesModule {}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import { Injectable, Inject, NotFoundException } from '@nestjs/common';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { DATABASE_CONNECTION } from '../db/database.module';
|
||||
import { type Database } from '../db/connection';
|
||||
import { sequences, type Sequence, type NewSequence } from '../db/schema/sequences.schema';
|
||||
import { CreateSequenceDto, UpdateSequenceDto } from './dto';
|
||||
|
||||
@Injectable()
|
||||
export class SequencesService {
|
||||
constructor(@Inject(DATABASE_CONNECTION) private db: Database) {}
|
||||
|
||||
async findAllByUser(userId: string): Promise<Sequence[]> {
|
||||
return this.db.select().from(sequences).where(eq(sequences.userId, userId));
|
||||
}
|
||||
|
||||
async findOne(id: string, userId: string): Promise<Sequence> {
|
||||
const [sequence] = await this.db
|
||||
.select()
|
||||
.from(sequences)
|
||||
.where(and(eq(sequences.id, id), eq(sequences.userId, userId)));
|
||||
|
||||
if (!sequence) {
|
||||
throw new NotFoundException(`Sequence with ID ${id} not found`);
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
async create(userId: string, dto: CreateSequenceDto): Promise<Sequence> {
|
||||
const newSequence: NewSequence = {
|
||||
userId,
|
||||
name: dto.name,
|
||||
moodIds: dto.moodIds,
|
||||
duration: dto.duration ?? 30,
|
||||
};
|
||||
|
||||
const [sequence] = await this.db.insert(sequences).values(newSequence).returning();
|
||||
return sequence;
|
||||
}
|
||||
|
||||
async update(id: string, userId: string, dto: UpdateSequenceDto): Promise<Sequence> {
|
||||
// Verify the sequence exists and belongs to the user
|
||||
await this.findOne(id, userId);
|
||||
|
||||
const [updated] = await this.db
|
||||
.update(sequences)
|
||||
.set({
|
||||
...dto,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(and(eq(sequences.id, id), eq(sequences.userId, userId)))
|
||||
.returning();
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
async delete(id: string, userId: string): Promise<void> {
|
||||
// Verify the sequence exists and belongs to the user
|
||||
await this.findOne(id, userId);
|
||||
|
||||
await this.db.delete(sequences).where(and(eq(sequences.id, id), eq(sequences.userId, userId)));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue