diff --git a/src/db/relations/user-item.relation.ts b/src/db/relations/user-item.relation.ts new file mode 100644 index 0000000000000000000000000000000000000000..1cf7ca81e8c6992001834bf99b9fb0dd489ccd2e --- /dev/null +++ b/src/db/relations/user-item.relation.ts @@ -0,0 +1,52 @@ +import { serial, pgTable, integer } from "drizzle-orm/pg-core" +import { itemsTable, userTable } from "../schema" +import { relations } from "drizzle-orm" +import { createInsertSchema, createSelectSchema } from "drizzle-zod" +import type { z } from "zod" + +const userItemRelationTable = pgTable('user_item', { + id: serial('id').primaryKey() + .unique() + .notNull(), + user_id: integer('user_id') + .references(() => userTable.id, {onDelete: 'cascade'}) + .notNull(), + item_id: integer('item_id') + .references(() => itemsTable.id, {onDelete: 'cascade'}) + .notNull(), +}) + +export const userItemRelationTableRelations = relations( + userItemRelationTable, + ({ one }) => ({ + user: one(userTable, { + fields: [userItemRelationTable.user_id], + references: [userTable.id] + }), + item: one(itemsTable, { + fields: [userItemRelationTable.item_id], + references: [itemsTable.id] + }), + }) +) + +const userItemRelationModelSchema = createSelectSchema(userItemRelationTable) +const userItemRelationDtoSchema = userItemRelationModelSchema +const userItemRelationInputSchema = createInsertSchema(userItemRelationTable) +const userItemRelationUpdateSchema = userItemRelationInputSchema + .partial() + .required({id: true}) + +export type UserItemRelationModel = z.infer<typeof userItemRelationModelSchema> +export type UserItemRelationDto = z.infer<typeof userItemRelationDtoSchema> +export type UserItemRelationInput = z.infer<typeof userItemRelationInputSchema> +export type UserItemRelationUpdate = z.infer<typeof userItemRelationUpdateSchema> + +export const userItemRelationSchemas = { + model: userItemRelationModelSchema, + dto: userItemRelationDtoSchema, + input: userItemRelationInputSchema, + update: userItemRelationUpdateSchema +} + +export default userItemRelationTable \ No newline at end of file diff --git a/src/db/repo/items.repo.ts b/src/db/repo/items.repo.ts index d2b8e4394fef128b8c3e250b13a256017095642b..e9d24c78cc5846d9954ab50a84d8d7e87d8b8495 100644 --- a/src/db/repo/items.repo.ts +++ b/src/db/repo/items.repo.ts @@ -48,7 +48,7 @@ export class ItemsRepo { } - async find(id: ItemsModel['id']): Promise<ItemsModel | undefined> { + async findById(id: ItemsModel['id']): Promise<ItemsModel | undefined> { const item = await db.query.itemsTable.findFirst({ where: and(eq(itemsTable.id, id), eq(itemsTable.is_active, true)) }) diff --git a/src/db/repo/user-item.repo.ts b/src/db/repo/user-item.repo.ts new file mode 100644 index 0000000000000000000000000000000000000000..c8d0becb81856eae5a79a53981ce2b8bb0524fa0 --- /dev/null +++ b/src/db/repo/user-item.repo.ts @@ -0,0 +1,80 @@ +import { Service } from "typedi"; +import { z } from "zod"; +import db from "@/db"; +import { and, eq } from "drizzle-orm"; +import userItemRelationTable, { userItemRelationSchemas, type UserItemRelationInput, type UserItemRelationModel, type UserItemRelationUpdate } from "../relations/user-item.relation"; + +@Service() +export class UserItemRepo{ + + async findMany(): Promise<UserItemRelationModel[]>{ + return z + .array(userItemRelationSchemas.model) + .parse(await db.query.userItemRelationTable.findMany()) + } + + async findByUserItem(userItemRelation: UserItemRelationInput): Promise<number> { + console.log(userItemRelation) + const userItem = await db.query.userItemRelationTable.findFirst({ + where: and(eq(userItemRelationTable.item_id, userItemRelation.item_id), eq(userItemRelationTable.user_id, userItemRelation.user_id)) + }) + + if(!userItem) return 0 + + return userItem.id + } + + async findById(id: UserItemRelationModel['id']): Promise<UserItemRelationModel | null>{ + const userItemRelation = await db.query.userItemRelationTable.findFirst({ + where: eq(userItemRelationTable.id, id) + }) + if(!userItemRelation) return null + + return userItemRelationSchemas.model.parse(userItemRelation) + } + + async findByUserId(user_id: UserItemRelationModel['user_id']): Promise<UserItemRelationModel[]>{ + + return z + .array(userItemRelationSchemas.model).parse(await db.query.userItemRelationTable.findMany({ + where: eq(userItemRelationTable.user_id, user_id)})) + } + + async findByItemId(userItem_id: UserItemRelationModel['item_id']): Promise<UserItemRelationModel[]>{ + return z + .array(userItemRelationSchemas.model).parse(await db.query.userItemRelationTable.findMany({ + where: eq(userItemRelationTable.item_id, userItem_id)})) + } + + async create(userItemRelation: UserItemRelationInput, tx?: db): Promise<UserItemRelationModel> { + const repo = tx ?? db + console.log(userItemRelation) + + const [ret] = await repo + .insert(userItemRelationTable) + .values(userItemRelation) + .returning() + + return userItemRelationSchemas.model.parse(ret) + } + + async update(userItemRelation: UserItemRelationUpdate): Promise<UserItemRelationModel>{ + const [ret] = await db + .update(userItemRelationTable) + .set(userItemRelation) + .where(eq(userItemRelationTable.id, userItemRelation.id)) + .returning() + + return userItemRelationSchemas.model.parse(ret) + } + + async delete(id: UserItemRelationModel['id']): Promise<UserItemRelationModel>{ + const [ret] = await db + .delete(userItemRelationTable) + .where(eq(userItemRelationTable.id, id)) + .returning() + + return userItemRelationSchemas.model.parse(ret) + } + +} \ No newline at end of file diff --git a/src/db/schema/index.ts b/src/db/schema/index.ts index b9f5f89124b76a8ab7b45335a0728acc912c765b..c69e574f7301579e55f1ce6dc06ca39c741bb2d3 100644 --- a/src/db/schema/index.ts +++ b/src/db/schema/index.ts @@ -31,6 +31,7 @@ import itemsTable from './items.schema' import userAchievementsTable from '../relations/user-achievements.relation' import actionTable from './action.schema' import notificationRelationTable from '../relations/notification.relation' +import userItemRelationTable from '../relations/user-item.relation' export { userTable, @@ -64,6 +65,7 @@ export { userAchievementsTable, actionTable, notificationRelationTable, + userItemRelationTable, } export const tables = [ @@ -94,4 +96,5 @@ export const tables = [ userAchievementsTable, actionTable, notificationRelationTable, + userItemRelationTable, ] diff --git a/src/index.ts b/src/index.ts index 606dfe5813c296e240ed69864769f08b158c03ea..6ab19a2e2d5da25368538551c96b35c077925caf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,7 @@ import { itemsRouter, publicItemsRouter } from './routes/items.route' import { publicUserAchievementsRoute, userAchievementsRouter } from './routes/user-achievements.route' import { actionRouter } from './routes/action.route' import { notificationRouter } from './routes/notification.route' +import { userItemRouter } from './routes/user-item.route' const app = new Hono() @@ -128,6 +129,7 @@ app .route('/resourceLikes', resourceLikesRoutes) .route('/userAchievements', userAchievementsRouter) .route('/notifications', notificationRouter) + .route('/user-item', userItemRouter) export default app export type AppType = typeof app diff --git a/src/routes/items.route.ts b/src/routes/items.route.ts index 1ecf9b3c4733be318ea72ddf0e63495e74c04662..83977e0f6056c73e86a5739ec797b9a736186c03 100644 --- a/src/routes/items.route.ts +++ b/src/routes/items.route.ts @@ -127,7 +127,7 @@ export const publicItemsRouter = new Hono() async (c) => { try { const id = +c.req.param('id') - const item = itemsSchema.dto.parse(await service.find(id)) + const item = itemsSchema.dto.parse(await service.findById(id)) return c.json({ item }) } catch (e) { diff --git a/src/routes/user-item.route.ts b/src/routes/user-item.route.ts new file mode 100644 index 0000000000000000000000000000000000000000..02ea59701b72ace59616591496d9c2c465a7ef03 --- /dev/null +++ b/src/routes/user-item.route.ts @@ -0,0 +1,106 @@ +import { honoWithJwt } from ".."; +import Container from "typedi"; +import { zValidator } from "@hono/zod-validator"; +import { createApexError, HttpStatus } from "@/services/error.service"; +import { userItemRelationSchemas } from "@/db/relations/user-item.relation"; +import { z } from "zod"; +import { UserService } from "@/services/user.service"; +import { userSchemas, type UserProfile } from "@/db/schema/user.schema"; +import { ItemsService } from "@/services/items.service"; +import { itemsSchema, type ItemsModel } from "@/db/schema/items.schema"; +import { UserItemRelationService } from "@/services/user-item.relation.service"; + +const service = Container.get(UserItemRelationService) +const userService = Container.get(UserService) +const itemService = Container.get(ItemsService) + +export const userItemRouter = honoWithJwt() + .post('/create', zValidator('json', userItemRelationSchemas.input), + async (c) => { + try{ + const input = await c.req.valid('json') + let user = await userService.findById(input.user_id) + let item = await itemService.findById(input.item_id) //tinha que ver questão de um usuario sem ser admin poder criar um admin + let alreadyItem = await service.findByUserItem(input) != 0 + if(user == null || item == null || alreadyItem){ + throw new Error() + } + + const user_item = userItemRelationSchemas.dto.parse( + await service.create(input) + ) + return c.json({ user_item }) + } catch (e) { + return c.json( + createApexError({ + status: 'error', + message: 'could not create user stats', + code: HttpStatus.BAD_REQUEST, + path: c.req.routePath, + suggestion: 'check the input and try again', + }), + HttpStatus.BAD_REQUEST + ) + } + }) + .get('/items/user/:user_id', async (c) =>{ + try{ + const user_id = +c.req.param('user_id') + const items_by_user = z.array(userItemRelationSchemas.model).parse(await service.findByUserId(user_id)) + var items: ItemsModel[] = [] + + for(var element of items_by_user){ + let item = await itemService.findById(element.item_id) + if(item != null) + items.push(itemsSchema.dto.parse(item)) + } + + return c.json({ items }) + + } catch(e){ + return c.notFound() + } + }) + .get('/users/item/:item_id', async (c) =>{ + try{ + const item_id = +c.req.param('item_id') + const users_by_item = z.array(userItemRelationSchemas.model).parse(await service.findByItemId(item_id)) + + var users: UserProfile[] = [] + + for(var element of users_by_item){ + let user = await userService.findById(element.user_id) + if(user != null) + users.push(userSchemas.userProfileSchema.parse(user)) + } + + + return c.json({ users }) + + } catch(e){ + return c.notFound() + } + }) + .delete('/delete', + zValidator('json', userItemRelationSchemas.input), + async (c) =>{ + try{ + const input = await c.req.valid('json') + const userItem_id = await service.findByUserItem(input) + const ret = userItemRelationSchemas.dto.parse( + await service.delete(userItem_id) + ) + return c.json(ret) + } catch (e){ + return c.json( + createApexError({ + status: 'error', + message: 'could delete user stats', + code: HttpStatus.BAD_REQUEST, + path: c.req.routePath, + suggestion: 'check the input and try again', + }), + HttpStatus.BAD_REQUEST + ) + } + }) \ No newline at end of file diff --git a/src/services/items.service.ts b/src/services/items.service.ts index d413c53b574f62fa2b2801d647a070ed419c1d2a..4c63b0314f9758ed5e02c0332efdc3b3ecedffd8 100644 --- a/src/services/items.service.ts +++ b/src/services/items.service.ts @@ -24,8 +24,8 @@ export class ItemsService { return this.repo.active(id) } - async find(id: ItemsModel['id']): Promise<ItemsModel | undefined> { - return this.repo.find(id) + async findById(id: ItemsModel['id']): Promise<ItemsModel | undefined> { + return this.repo.findById(id) } async findMany(): Promise<ItemsModel[]> { diff --git a/src/services/user-item.relation.service.ts b/src/services/user-item.relation.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c59417d2f399771d9c27db4dbe697240088c4ac --- /dev/null +++ b/src/services/user-item.relation.service.ts @@ -0,0 +1,63 @@ +import type db from "@/db"; +import { + type UserItemRelationInput, + type UserItemRelationModel, + type UserItemRelationUpdate + } from "@/db/relations/user-item.relation"; +import { UserItemRepo } from "@/db/repo/user-item.repo"; +import { Inject, Service } from "typedi"; + +@Service() +export class UserItemRelationService { + + @Inject() + private readonly repo: UserItemRepo + + async findMany(): Promise<UserItemRelationModel[]> { + + return await this.repo.findMany() + } + + async findByUserItem(userItemRelation: UserItemRelationInput): Promise<number> { + + return await this.repo.findByUserItem(userItemRelation) + } + + async findById( + id: UserItemRelationModel['id'] + ): Promise<UserItemRelationModel | null>{ + + return await this.repo.findById(id) + } + + async findByUserId( + userId: UserItemRelationModel['user_id'] + ): Promise<UserItemRelationModel[]>{ + + return await this.repo.findByUserId(userId) + } + + async findByItemId( + itemId: UserItemRelationModel['item_id'] + ): Promise<UserItemRelationModel[]>{ + + return await this.repo.findByItemId(itemId) + } + + async create(followRelation: UserItemRelationInput, tx?: db): Promise<UserItemRelationModel> { + + return this.repo.create(followRelation, tx) + } + + async update(followRelation: UserItemRelationUpdate): Promise<UserItemRelationModel> { + + return this.repo.update(followRelation) + } + + + async delete(user: UserItemRelationModel['id']): Promise<UserItemRelationModel> { + + return this.repo.delete(user) + } + +} \ No newline at end of file