update from mon table to hirarch. table

This commit is contained in:
Your Name
2025-03-26 00:12:03 +01:00
parent 3228f0033b
commit fc29ee95df
10 changed files with 867 additions and 390 deletions

5
api/drizzle.config.ts Normal file
View File

@@ -0,0 +1,5 @@
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'postgresql', // 'mysql' | 'sqlite' | 'turso'
schema: './src/db/schema.ts',
});

View File

@@ -1,3 +1,4 @@
import { relations } from 'drizzle-orm';
import * as t from 'drizzle-orm/pg-core';
import { pgEnum, pgTable as table } from 'drizzle-orm/pg-core';
@@ -24,10 +25,75 @@ export const deck = table(
inserted: t.timestamp('inserted', { mode: 'date' }).defaultNow(),
updated: t.timestamp('updated', { mode: 'date' }).defaultNow(),
},
table => [t.uniqueIndex('deck_idx').on(table.id)],
(table) => [t.uniqueIndex('deck_idx').on(table.id)],
);
export type InsertDeck = typeof deck.$inferInsert;
export type SelectDeck = typeof deck.$inferSelect;
export const decks_table = table('decks', {
id: t.integer('id').primaryKey().generatedAlwaysAsIdentity(),
name: t.varchar('name').notNull(),
boxOrder: t.varchar('box_order').notNull().default('shuffle'),
user: t.varchar('user').notNull(),
inserted: t.timestamp('inserted', { mode: 'date' }).defaultNow(),
updated: t.timestamp('updated', { mode: 'date' }).defaultNow(),
});
export type InsertDecks = typeof decks_table.$inferInsert;
export type SelectDecks = typeof decks_table.$inferSelect;
export const images_table = table('images', {
id: t.integer('id').primaryKey().generatedAlwaysAsIdentity(),
deckId: t.integer('deck_id').references(() => decks_table.id),
name: t.varchar('name').notNull(),
bildid: t.varchar('bildid').notNull(),
inserted: t.timestamp('inserted', { mode: 'date' }).defaultNow(),
updated: t.timestamp('updated', { mode: 'date' }).defaultNow(),
});
export type InsertImages = typeof images_table.$inferInsert;
export type SelectImages = typeof images_table.$inferSelect;
export const boxes_table = table('boxes', {
id: t.integer('id').primaryKey().generatedAlwaysAsIdentity(),
imageId: t.integer('image_id').references(() => images_table.id),
x1: t.real('x1').notNull(),
x2: t.real('x2').notNull(),
y1: t.real('y1').notNull(),
y2: t.real('y2').notNull(),
due: t.integer('due'),
ivl: t.real('ivl'),
factor: t.real('factor'),
reps: t.integer('reps'),
lapses: t.integer('lapses'),
isGraduated: t.integer('is_graduated').notNull().default(0),
inserted: t.timestamp('inserted', { mode: 'date' }).defaultNow(),
updated: t.timestamp('updated', { mode: 'date' }).defaultNow(),
});
export type InsertBoxes = typeof boxes_table.$inferInsert;
export type SelectBoxes = typeof boxes_table.$inferSelect;
// Relations (optional, aber hilfreich für Abfragen)
export const decksRelations = relations(decks_table, ({ many }) => ({
images: many(images_table),
}));
export const imagesRelations = relations(images_table, ({ one, many }) => ({
deck: one(decks_table, {
fields: [images_table.deckId],
references: [decks_table.id],
}),
boxes: many(boxes_table),
}));
export const boxesRelations = relations(boxes_table, ({ one }) => ({
image: one(images_table, {
fields: [boxes_table.imageId],
references: [images_table.id],
}),
}));
// -------------------------------------
// USERS
// -------------------------------------
export const users = table(
'users',
{
@@ -39,7 +105,7 @@ export const users = table(
lastLogin: t.timestamp('lastLogin', { mode: 'date' }).defaultNow(),
numberOfLogins: t.integer('numberOfLogins').default(1), // Neue Spalte
},
table => [t.uniqueIndex('users_idx').on(table.id)],
(table) => [t.uniqueIndex('users_idx').on(table.id)],
);
export type InsertUser = typeof users.$inferInsert;
export type SelectUser = typeof users.$inferSelect;

View File

@@ -25,36 +25,7 @@ export class DecksController {
async getDecks(@Request() req) {
const user: User = req['user'];
const entries = await this.drizzleService.getDecks(user);
const decks = {};
for (const entry of entries) {
const deckname = entry.deckname!!;
if (!decks[deckname]) {
decks[deckname] = {
name: deckname,
images: [],
};
}
if (entry.bildname && entry.bildid) {
decks[deckname].images.push({
name: entry.bildname,
bildid: entry.bildid,
id: entry.id,
x1: entry.x1,
x2: entry.x2,
y1: entry.y1,
y2: entry.y2,
due: entry.due,
ivl: entry.ivl,
factor: entry.factor,
reps: entry.reps,
lapses: entry.lapses,
isGraduated: Boolean(entry.isGraduated),
inserted: new Date(entry.inserted!!),
updated: new Date(entry.updated!!),
});
}
}
return Object.values(decks);
return entries;
}
@Post()
@@ -66,43 +37,6 @@ export class DecksController {
return this.drizzleService.createDeck(data.deckname, user);
}
@Get(':deckname')
async getDeck(@Request() req, @Param('deckname') deckname: string) {
const user: User = req['user'];
const entries = await this.drizzleService.getDeckByName(deckname, user);
if (entries.length === 0) {
throw new HttpException('Deck not found', HttpStatus.NOT_FOUND);
}
const deck = {
name: deckname,
images: [] as any,
};
for (const entry of entries) {
if (entry.bildname && entry.bildid) {
deck.images.push({
name: entry.bildname,
bildid: entry.bildid,
id: entry.id,
x1: entry.x1,
x2: entry.x2,
y1: entry.y1,
y2: entry.y2,
due: entry.due,
ivl: entry.ivl,
factor: entry.factor,
reps: entry.reps,
lapses: entry.lapses,
isGraduated: Boolean(entry.isGraduated),
inserted: new Date(entry.inserted!!),
updated: new Date(entry.updated!!),
});
}
}
return deck;
}
@Delete(':deckname')
async deleteDeck(@Request() req, @Param('deckname') deckname: string) {
const user: User = req['user'];
@@ -122,9 +56,21 @@ export class DecksController {
);
}
const user: User = req['user'];
return this.drizzleService.renameDeck(oldDeckname, data.newDeckName, user);
return this.drizzleService.updateDeck(
oldDeckname,
{ newName: data.newDeckName },
user,
);
}
@Put(':oldDeckname/update')
async updateDeck(
@Request() req,
@Param('oldDeckname') oldDeckname: string,
@Body() data: { newDeckName: string; boxOrder?: 'shuffle' | 'position' },
) {
const user: User = req['user'];
return this.drizzleService.updateDeck(oldDeckname, data, user);
}
@Post('image')
async updateImage(@Request() req, @Body() data: any) {
if (!data) {

File diff suppressed because it is too large Load Diff

View File

@@ -76,58 +76,58 @@ export class ProxyController {
// --------------------
// Cleanup Endpoint
// --------------------
@Post('cleanup')
async cleanupEndpoint(
@Body() data: { dryrun?: boolean },
@Res() res: express.Response,
) {
try {
const user = res.req['user']; // Benutzerinformationen aus dem Request
// @Post('cleanup')
// async cleanupEndpoint(
// @Body() data: { dryrun?: boolean },
// @Res() res: express.Response,
// ) {
// try {
// const user = res.req['user']; // Benutzerinformationen aus dem Request
// 2. Nur Benutzer mit der spezifischen E-Mail dürfen fortfahren
if (user.email !== 'andreas.knuth@gmail.com') {
throw new HttpException('Zugriff verweigert.', HttpStatus.FORBIDDEN);
}
// // 2. Nur Benutzer mit der spezifischen E-Mail dürfen fortfahren
// if (user.email !== 'andreas.knuth@gmail.com') {
// throw new HttpException('Zugriff verweigert.', HttpStatus.FORBIDDEN);
// }
// 1. Abrufen der distinct bildid aus der Datenbank
const usedIds = await this.drizzleService.getDistinctBildIds(user);
// 3. Verarbeitung des dryrun Parameters
const dryrun = data.dryrun !== undefined ? data.dryrun : true;
if (typeof dryrun !== 'boolean') {
throw new HttpException(
"'dryrun' muss ein boolescher Wert sein.",
HttpStatus.BAD_REQUEST,
);
}
// // 1. Abrufen der distinct bildid aus der Datenbank
// const usedIds = await this.drizzleService.getDistinctBildIds(user);
// // 3. Verarbeitung des dryrun Parameters
// const dryrun = data.dryrun !== undefined ? data.dryrun : true;
// if (typeof dryrun !== 'boolean') {
// throw new HttpException(
// "'dryrun' muss ein boolescher Wert sein.",
// HttpStatus.BAD_REQUEST,
// );
// }
// 4. Aufruf des Flask-Backend-Endpunkts
const response = await fetch('http://localhost:5000/api/cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ dryrun, usedIds }),
});
// // 4. Aufruf des Flask-Backend-Endpunkts
// const response = await fetch('http://localhost:5000/api/cleanup', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({ dryrun, usedIds }),
// });
const result = await response.json();
// const result = await response.json();
if (!response.ok) {
throw new HttpException(
result.error || 'Cleanup failed',
response.status,
);
}
// if (!response.ok) {
// throw new HttpException(
// result.error || 'Cleanup failed',
// response.status,
// );
// }
// 5. Rückgabe der Ergebnisse an den Client
return res.status(HttpStatus.OK).json(result);
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
'Interner Serverfehler',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
// // 5. Rückgabe der Ergebnisse an den Client
// return res.status(HttpStatus.OK).json(result);
// } catch (error) {
// if (error instanceof HttpException) {
// throw error;
// }
// throw new HttpException(
// 'Interner Serverfehler',
// HttpStatus.INTERNAL_SERVER_ERROR,
// );
// }
// }
}