logging#19, max 5MB#18,addinsert/update#14,drizzle
This commit is contained in:
@@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';
|
||||
import { DecksController } from './decks.controller';
|
||||
import { DrizzleService } from './drizzle.service';
|
||||
import { ProxyController } from './proxy.controller';
|
||||
import { SqlLoggerService } from './sql-logger.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [DecksController,ProxyController],
|
||||
providers: [DrizzleService],
|
||||
controllers: [DecksController, ProxyController],
|
||||
providers: [DrizzleService, SqlLoggerService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -25,9 +25,8 @@ export class DecksController {
|
||||
if (entry.bildname && entry.bildid) {
|
||||
decks[deckname].images.push({
|
||||
name: entry.bildname,
|
||||
id: entry.bildid,
|
||||
iconindex: entry.iconindex,
|
||||
boxid: entry.id,
|
||||
bildid: entry.bildid,
|
||||
id: entry.id,
|
||||
x1: entry.x1,
|
||||
x2: entry.x2,
|
||||
y1: entry.y1,
|
||||
@@ -72,9 +71,8 @@ export class DecksController {
|
||||
if (entry.bildname && entry.bildid) {
|
||||
deck.images.push({
|
||||
name: entry.bildname,
|
||||
id: entry.bildid,
|
||||
iconindex: entry.iconindex,
|
||||
boxid: entry.id,
|
||||
bildid: entry.bildid,
|
||||
id: entry.id,
|
||||
x1: entry.x1,
|
||||
x2: entry.x2,
|
||||
y1: entry.y1,
|
||||
@@ -141,10 +139,10 @@ export class DecksController {
|
||||
return this.drizzleService.moveImage(bildid, data.targetDeckId, user);
|
||||
}
|
||||
|
||||
@Put('boxes/:boxId')
|
||||
@Put('boxes/:id')
|
||||
async updateBox(
|
||||
@Request() req,
|
||||
@Param('boxId') boxId: number,
|
||||
@Param('id') id: number,
|
||||
@Body()
|
||||
data: {
|
||||
due?: number;
|
||||
@@ -156,6 +154,6 @@ export class DecksController {
|
||||
},
|
||||
) {
|
||||
const user: User = req['user'];
|
||||
return this.drizzleService.updateBox(boxId, data, user);
|
||||
return this.drizzleService.updateBox(id, data, user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
// drizzle.service.ts
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { and, eq, isNull, sql } from 'drizzle-orm';
|
||||
import { and, eq, sql } from 'drizzle-orm';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { Deck, User } from '../db/schema';
|
||||
import { Deck, SelectDeck, User } from '../db/schema';
|
||||
import { SqlLoggerService } from './sql-logger.service';
|
||||
|
||||
@Injectable()
|
||||
export class DrizzleService {
|
||||
private db = drizzle('file:local.db');
|
||||
// private readonly logger = new Logger(DrizzleService.name);
|
||||
private db: any;
|
||||
constructor(private sqlLogger: SqlLoggerService) {
|
||||
this.db = drizzle('file:local.db', {
|
||||
logger: {
|
||||
logQuery: (query: string, params: any[]) => {
|
||||
this.sqlLogger.logQuery(query, params);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode zum Abrufen aller Decks eines Benutzers
|
||||
@@ -80,48 +91,82 @@ export class DrizzleService {
|
||||
|
||||
/**
|
||||
* Methode zum Aktualisieren eines Bildes innerhalb eines Decks
|
||||
* Die Methode erhält jetzt auch den 'inserted' Timestamp
|
||||
*/
|
||||
async updateImage(
|
||||
data: {
|
||||
deckname: string;
|
||||
bildname: string;
|
||||
bildid: string;
|
||||
boxes: Array<{ x1: number; x2: number; y1: number; y2: number }>;
|
||||
inserted: string; // Neuer Parameter für den 'inserted' Timestamp
|
||||
boxes: Array<{ x1: number; x2: number; y1: number; y2: number; id?: number }>;
|
||||
},
|
||||
user: User,
|
||||
) {
|
||||
const existingDeck = await this.getDeckByName(data.deckname, user);
|
||||
if (existingDeck.length === 0) {
|
||||
// Schritt 1: Überprüfen, ob das Deck existiert
|
||||
const existingDecks: SelectDeck[] = await this.getDeckByName(data.deckname, user);
|
||||
if (existingDecks.length === 0) {
|
||||
throw new HttpException('Deck not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
// Lösche vorhandene Einträge für das Bild
|
||||
await this.db.delete(Deck).where(and(eq(Deck.deckname, data.deckname), eq(Deck.bildid, data.bildid), eq(Deck.user, user.email)));
|
||||
// Schritt 2: Trennen der neuen und bestehenden Einträge
|
||||
const newEntries = data.boxes.filter(b => !b.id);
|
||||
const existingEntries = data.boxes.filter(b => b.id);
|
||||
|
||||
// Schritt 3: Einfügen neuer Einträge
|
||||
const insertedImages: any = [];
|
||||
for (let index = 0; index < data.boxes.length; index++) {
|
||||
const box = data.boxes[index];
|
||||
for (let index = 0; index < newEntries.length; index++) {
|
||||
const box = newEntries[index];
|
||||
const result = await this.db
|
||||
.insert(Deck)
|
||||
.values({
|
||||
deckname: data.deckname,
|
||||
bildname: data.bildname,
|
||||
bildid: data.bildid,
|
||||
iconindex: index,
|
||||
x1: box.x1,
|
||||
x2: box.x2,
|
||||
y1: box.y1,
|
||||
y2: box.y2,
|
||||
user: user.email,
|
||||
inserted: data.inserted, // Setze 'inserted' auf den übergebenen Wert
|
||||
// 'updated' wird automatisch von der DB gesetzt
|
||||
// 'inserted' und 'updated' werden automatisch von der DB gesetzt
|
||||
})
|
||||
.returning();
|
||||
insertedImages.push(result);
|
||||
}
|
||||
|
||||
// Schritt 4: Aktualisieren bestehender Einträge
|
||||
for (let index = 0; index < existingEntries.length; index++) {
|
||||
const box = existingEntries[index];
|
||||
const existingDeck = existingDecks.find(d => d.id === box.id);
|
||||
if (existingDeck && (existingDeck.x1 !== box.x1 || existingDeck.x2 !== box.x2 || existingDeck.y1 !== box.y1 || existingDeck.y2 !== box.y2)) {
|
||||
const result = await this.db
|
||||
.update(Deck)
|
||||
.set({
|
||||
x1: box.x1,
|
||||
x2: box.x2,
|
||||
y1: box.y1,
|
||||
y2: box.y2,
|
||||
updated: sql`CURRENT_TIMESTAMP`, // Setze 'updated' auf CURRENT_TIMESTAMP
|
||||
})
|
||||
.where(and(eq(Deck.user, user.email), eq(Deck.bildid, data.bildid), eq(Deck.deckname, data.deckname), eq(Deck.id, box.id!!)));
|
||||
if (result.rowsAffected === 0) {
|
||||
throw new HttpException(`Box with id ${box.id} not found`, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Schritt 5: Löschen von nicht mehr vorhandenen Einträgen
|
||||
// Sammle alle bestehenden IDs aus der Datenbank für das gegebene deckname und bildid
|
||||
const existingIdsInDb = existingDecks.filter(entry => entry.bildid === data.bildid && entry.deckname === data.deckname).map(entry => entry.id);
|
||||
|
||||
// Sammle alle eingehenden IDs aus den vorhandenen Einträgen
|
||||
const incomingIds = existingEntries.map(entry => entry.id);
|
||||
|
||||
// Bestimme die IDs, die in der Datenbank existieren, aber nicht mehr in den eingehenden Daten vorhanden sind
|
||||
const idsToDelete = existingIdsInDb.filter(id => !incomingIds.includes(id));
|
||||
|
||||
if (idsToDelete.length > 0) {
|
||||
await this.db.delete(Deck).where(and(eq(Deck.deckname, data.deckname), eq(Deck.bildid, data.bildid), eq(Deck.user, user.email), sql`id in ${idsToDelete}`));
|
||||
}
|
||||
|
||||
return { status: 'success', inserted_images: insertedImages };
|
||||
}
|
||||
|
||||
@@ -141,30 +186,6 @@ export class DrizzleService {
|
||||
|
||||
await this.db.delete(Deck).where(and(eq(Deck.bildid, bildid), eq(Deck.user, user.email)));
|
||||
|
||||
for (const deck of affectedDecks) {
|
||||
const remainingImages = await this.db
|
||||
.select()
|
||||
.from(Deck)
|
||||
.where(and(eq(Deck.deckname, deck.deckname), eq(Deck.bildid, bildid), eq(Deck.user, user.email)))
|
||||
.all();
|
||||
|
||||
if (remainingImages.length === 0) {
|
||||
const emptyDeckEntry = await this.db
|
||||
.select()
|
||||
.from(Deck)
|
||||
.where(and(eq(Deck.deckname, deck.deckname), isNull(Deck.bildid), eq(Deck.user, user.email)))
|
||||
.all();
|
||||
|
||||
if (emptyDeckEntry.length === 0) {
|
||||
await this.db.insert(Deck).values({
|
||||
deckname: deck.deckname,
|
||||
user: user.email,
|
||||
// 'inserted' und 'updated' werden automatisch von der DB gesetzt
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
message: `All entries for image ID "${bildid}" have been deleted.`,
|
||||
@@ -200,7 +221,7 @@ export class DrizzleService {
|
||||
* Methode zum Aktualisieren einer Box
|
||||
*/
|
||||
async updateBox(
|
||||
boxId: number,
|
||||
id: number,
|
||||
data: {
|
||||
due?: number;
|
||||
ivl?: number;
|
||||
@@ -220,7 +241,7 @@ export class DrizzleService {
|
||||
const result = await this.db
|
||||
.update(Deck)
|
||||
.set(updateData)
|
||||
.where(and(eq(Deck.id, boxId), eq(Deck.user, user.email)));
|
||||
.where(and(eq(Deck.id, id), eq(Deck.user, user.email)));
|
||||
|
||||
if (result.rowsAffected === 0) {
|
||||
throw new HttpException('Box not found', HttpStatus.NOT_FOUND);
|
||||
|
||||
36
api/src/app/sql-logger.service.ts
Normal file
36
api/src/app/sql-logger.service.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// sql-logger.service.ts
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import type { ChalkInstance } from 'chalk';
|
||||
import chalk from 'chalk';
|
||||
|
||||
@Injectable()
|
||||
export class SqlLoggerService {
|
||||
private readonly logger = new Logger(SqlLoggerService.name);
|
||||
private chalkInstance: ChalkInstance;
|
||||
|
||||
constructor() {
|
||||
this.chalkInstance = chalk;
|
||||
}
|
||||
|
||||
logQuery(query: string, params: any[], sourceIp?: string) {
|
||||
const timestamp = new Date().toISOString().replace('T', ' ').replace('Z', '');
|
||||
|
||||
let logMessage = `[DrizzleORM] Info\t${timestamp}`;
|
||||
|
||||
// Optional source IP
|
||||
if (sourceIp) {
|
||||
logMessage += ` IP: ${sourceIp}`;
|
||||
}
|
||||
|
||||
// Colored query and params using chalk directly
|
||||
const coloredQuery = chalk.blueBright(`Query: ${query}`);
|
||||
const coloredParams = chalk.yellow(`Params: ${JSON.stringify(params)}`);
|
||||
|
||||
logMessage += ` - ${coloredQuery} - ${coloredParams}`;
|
||||
|
||||
// Add timing indicator
|
||||
logMessage += chalk.gray(' +2ms');
|
||||
|
||||
console.log(logMessage);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ export const Deck = table(
|
||||
deckname: text('deckname').notNull(),
|
||||
bildname: text('bildname'),
|
||||
bildid: text('bildid'),
|
||||
iconindex: integer('iconindex'),
|
||||
x1: real('x1'),
|
||||
x2: real('x2'),
|
||||
y1: real('y1'),
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app/app.module';
|
||||
import { json, urlencoded } from 'express';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
logger: ['log', 'error', 'warn', 'debug', 'verbose'], // Aktiviere alle Log-Level
|
||||
});
|
||||
app.use(json({ limit: '50mb' }));
|
||||
app.use(urlencoded({ limit: '50mb', extended: true }));
|
||||
const globalPrefix = 'api';
|
||||
app.setGlobalPrefix(globalPrefix);
|
||||
const port = process.env['PORT'] || 3000;
|
||||
await app.listen(port);
|
||||
Logger.log(
|
||||
`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`
|
||||
);
|
||||
Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`);
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user