From 4b7a22b4408634a92e3a828d4daa7dfdebc6de3b Mon Sep 17 00:00:00 2001 From: Richa Juvekar Date: Tue, 16 Sep 2025 22:11:13 -0400 Subject: [PATCH 1/6] initial backend setup + error cleanup --- apps/backend/src/app.module.ts | 3 +-- apps/backend/src/data-source.ts | 3 +-- .../src/migrations/1754254886189-add_task.ts | 19 ------------------- example.env | 2 +- 4 files changed, 3 insertions(+), 24 deletions(-) delete mode 100644 apps/backend/src/migrations/1754254886189-add_task.ts diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 54fb044e..9af0cf4b 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -3,11 +3,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { TaskModule } from './task/task.module'; import AppDataSource from './data-source'; @Module({ - imports: [TypeOrmModule.forRoot(AppDataSource.options), TaskModule], + imports: [TypeOrmModule.forRoot(AppDataSource.options)], controllers: [AppController], providers: [AppService], }) diff --git a/apps/backend/src/data-source.ts b/apps/backend/src/data-source.ts index 4cd06624..e9c75481 100644 --- a/apps/backend/src/data-source.ts +++ b/apps/backend/src/data-source.ts @@ -1,6 +1,5 @@ import { DataSource } from 'typeorm'; import { PluralNamingStrategy } from './strategies/plural-naming.strategy'; -import { Task } from './task/types/task.entity'; import * as dotenv from 'dotenv'; dotenv.config(); @@ -12,7 +11,7 @@ const AppDataSource = new DataSource({ username: process.env.NX_DB_USERNAME, password: process.env.NX_DB_PASSWORD, database: process.env.NX_DB_DATABASE, - entities: [Task], + entities: [], migrations: ['apps/backend/src/migrations/*.js'], // Setting synchronize: true shouldn't be used in production - otherwise you can lose production data synchronize: false, diff --git a/apps/backend/src/migrations/1754254886189-add_task.ts b/apps/backend/src/migrations/1754254886189-add_task.ts deleted file mode 100644 index 450a6415..00000000 --- a/apps/backend/src/migrations/1754254886189-add_task.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTask1754254886189 implements MigrationInterface { - name = 'AddTask1754254886189'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."tasks_category_enum" AS ENUM('Draft', 'To Do', 'In Progress', 'Completed')`, - ); - await queryRunner.query( - `CREATE TABLE "task" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "description" character varying, "dateCreated" TIMESTAMP NOT NULL DEFAULT now(), "dueDate" TIMESTAMP, "labels" jsonb NOT NULL DEFAULT '[]', "category" "public"."tasks_category_enum" NOT NULL DEFAULT 'Draft', CONSTRAINT "PK_8d12ff38fcc62aaba2cab748772" PRIMARY KEY ("id"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "task"`); - await queryRunner.query(`DROP TYPE "public"."tasks_category_enum"`); - } -} diff --git a/example.env b/example.env index 211b1472..19d989f2 100644 --- a/example.env +++ b/example.env @@ -1,5 +1,5 @@ NX_DB_HOST=localhost, NX_DB_USERNAME=postgres, NX_DB_PASSWORD=, -NX_DB_DATABASE=jumpstart, +NX_DB_DATABASE=826boston, NX_DB_PORT=5432, \ No newline at end of file From 33235025ad55c596875519d49109962bdaa39d24 Mon Sep 17 00:00:00 2001 From: Troy Gunawardene Date: Sat, 4 Oct 2025 15:52:22 -0400 Subject: [PATCH 2/6] create anthology entity --- .../backend/src/anthology/anthology.entity.ts | 49 +++++++++++++++++++ apps/backend/src/anthology/types.ts | 13 +++++ 2 files changed, 62 insertions(+) create mode 100644 apps/backend/src/anthology/anthology.entity.ts create mode 100644 apps/backend/src/anthology/types.ts diff --git a/apps/backend/src/anthology/anthology.entity.ts b/apps/backend/src/anthology/anthology.entity.ts new file mode 100644 index 00000000..be22827f --- /dev/null +++ b/apps/backend/src/anthology/anthology.entity.ts @@ -0,0 +1,49 @@ +import { Entity, Column, IntegerType } from 'typeorm'; + +import { AnthologyStatus, AnthologyPubLevel } from './types'; + +@Entity() +export class Anthology { + @Column({ primary: true }) + id: number; + + @Column() + title: string; + + @Column() + description: string; + + @Column() + published_year: IntegerType; + + @Column({ nullable: true }) + programs?: string[] | string; + + @Column({ nullable: true }) + inventory?: IntegerType; + + @Column() + status: AnthologyStatus; + + @Column() + pub_level: AnthologyPubLevel; + + @Column({ nullable: true }) + photo_url: string; + + @Column({ nullable: true }) + genre: string; + + @Column({ nullable: true }) + theme: string; + + @Column({ nullable: true }) + isbn: string; + + @Column({ nullable: true }) + shopify_url: string; + + // TODO once Library is implemented + // @Column() + // library_id: +} diff --git a/apps/backend/src/anthology/types.ts b/apps/backend/src/anthology/types.ts new file mode 100644 index 00000000..214797ed --- /dev/null +++ b/apps/backend/src/anthology/types.ts @@ -0,0 +1,13 @@ +export enum AnthologyStatus { + ARCHIVED = 'Archived', + NOT_STARTED = 'NotStarted', + DRAFTING = 'Drafting', + CAN_BE_SHARED = 'CanBeShared', +} + +export enum AnthologyPubLevel { + ZINE = 'Zine', + CHAPBOOK = 'Chapbook', + PERFECT_BOUND = 'PerfectBound', + SIGNATURE = 'Signature', +} From 718d69e108d554bef8f547c250ff26761d666f45 Mon Sep 17 00:00:00 2001 From: Troy Gunawardene Date: Sat, 4 Oct 2025 16:25:45 -0400 Subject: [PATCH 3/6] add anthology service --- .../src/anthology/anthology.service.ts | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 apps/backend/src/anthology/anthology.service.ts diff --git a/apps/backend/src/anthology/anthology.service.ts b/apps/backend/src/anthology/anthology.service.ts new file mode 100644 index 00000000..35bd39c2 --- /dev/null +++ b/apps/backend/src/anthology/anthology.service.ts @@ -0,0 +1,123 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Anthology } from './anthology.entity'; +import { AnthologyStatus, AnthologyPubLevel } from './types'; + +@Injectable() +export class AnthologyService { + constructor( + @InjectRepository(Anthology) private repo: Repository, + ) {} + + async create( + title: string, + description: string, + published_year: number, + status: AnthologyStatus, + pub_level: AnthologyPubLevel, + programs?: string[] | string, + inventory?: number, + photo_url?: string, + genre?: string, + theme?: string, + isbn?: string, + shopify_url?: string, + ) { + const anthologyId = (await this.repo.count()) + 1; + const anthology = this.repo.create({ + id: anthologyId, + title, + description, + published_year, + status, + pub_level, + programs, + inventory, + photo_url, + genre, + theme, + isbn, + shopify_url, + }); + + return this.repo.save(anthology); + } + + findOne(id: number) { + if (!id) { + return null; + } + + return this.repo.findOneBy({ id }); + } + + findAll() { + return this.repo.find(); + } + + findByStatus(status: AnthologyStatus) { + return this.repo.find({ where: { status } }); + } + + findByPubLevel(pub_level: AnthologyPubLevel) { + return this.repo.find({ where: { pub_level } }); + } + + findByYear(published_year: number) { + return this.repo.find({ where: { published_year } }); + } + + findByGenre(genre: string) { + return this.repo.find({ where: { genre } }); + } + + findByTheme(theme: string) { + return this.repo.find({ where: { theme } }); + } + + async update(id: number, attrs: Partial) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + Object.assign(anthology, attrs); + + return this.repo.save(anthology); + } + + async remove(id: number) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + return this.repo.remove(anthology); + } + + async updateInventory(id: number, inventory: number) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + anthology.inventory = inventory; + return this.repo.save(anthology); + } + + async updateStatus(id: number, status: AnthologyStatus) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + anthology.status = status; + return this.repo.save(anthology); + } +} From 1792de1f6dbcd2ef3a96c65b95d88ea70c323223 Mon Sep 17 00:00:00 2001 From: robpatterson13 Date: Sun, 5 Oct 2025 13:02:15 -0400 Subject: [PATCH 4/6] added library controller, entity, module, service --- apps/backend/src/library/library.controller.ts | 0 apps/backend/src/library/library.entity.ts | 0 apps/backend/src/library/library.module.ts | 0 apps/backend/src/library/library.service.ts | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/backend/src/library/library.controller.ts create mode 100644 apps/backend/src/library/library.entity.ts create mode 100644 apps/backend/src/library/library.module.ts create mode 100644 apps/backend/src/library/library.service.ts diff --git a/apps/backend/src/library/library.controller.ts b/apps/backend/src/library/library.controller.ts new file mode 100644 index 00000000..e69de29b diff --git a/apps/backend/src/library/library.entity.ts b/apps/backend/src/library/library.entity.ts new file mode 100644 index 00000000..e69de29b diff --git a/apps/backend/src/library/library.module.ts b/apps/backend/src/library/library.module.ts new file mode 100644 index 00000000..e69de29b diff --git a/apps/backend/src/library/library.service.ts b/apps/backend/src/library/library.service.ts new file mode 100644 index 00000000..e69de29b From deb072801825c86b07de0a8673ac9def0035427c Mon Sep 17 00:00:00 2001 From: robpatterson13 Date: Sun, 5 Oct 2025 13:05:00 -0400 Subject: [PATCH 5/6] actually saved library files this time --- .../backend/src/library/library.controller.ts | 32 ++++++++ apps/backend/src/library/library.entity.ts | 11 +++ apps/backend/src/library/library.module.ts | 15 ++++ apps/backend/src/library/library.service.ts | 77 +++++++++++++++++++ 4 files changed, 135 insertions(+) diff --git a/apps/backend/src/library/library.controller.ts b/apps/backend/src/library/library.controller.ts index e69de29b..bb217ae7 100644 --- a/apps/backend/src/library/library.controller.ts +++ b/apps/backend/src/library/library.controller.ts @@ -0,0 +1,32 @@ +import { + Controller, + Delete, + Get, + Param, + ParseIntPipe, + UseGuards, +} from '@nestjs/common'; +import { LibraryService } from './library.service'; +import { AuthGuard } from '@nestjs/passport'; +import { Library } from './library.entity'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; + +@ApiTags('Library') +@ApiBearerAuth() +@Controller('library') +@UseGuards(AuthGuard('jwt')) +export class LibraryController { + constructor(private libraryService: LibraryService) {} + + @Get('/:libraryId') + async getLibrary( + @Param('libraryId', ParseIntPipe) libraryId: number, + ): Promise { + return this.libraryService.findOne(libraryId); + } + + @Delete('/:id') + removeLibrary(@Param('id') id: string) { + return this.libraryService.remove(parseInt(id)); + } +} diff --git a/apps/backend/src/library/library.entity.ts b/apps/backend/src/library/library.entity.ts index e69de29b..516d71a2 100644 --- a/apps/backend/src/library/library.entity.ts +++ b/apps/backend/src/library/library.entity.ts @@ -0,0 +1,11 @@ +import { Entity, Column } from 'typeorm'; +import { Anthology } from '../anthology/anthology.entity'; + +@Entity() +export class Library { + @Column({ primary: true }) + id: number; + + @Column() + anthologies: Anthology[]; +} diff --git a/apps/backend/src/library/library.module.ts b/apps/backend/src/library/library.module.ts index e69de29b..2f035d44 100644 --- a/apps/backend/src/library/library.module.ts +++ b/apps/backend/src/library/library.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { LibraryController } from './library.controller'; +import { LibraryService } from './library.service'; +import { Library } from './library.entity'; +import { JwtStrategy } from '../auth/jwt.strategy'; +import { CurrentUserInterceptor } from '../interceptors/current-user.interceptor'; +import { AuthService } from '../auth/auth.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Library])], + controllers: [LibraryController], + providers: [LibraryService, AuthService, JwtStrategy, CurrentUserInterceptor], +}) +export class LibraryModule {} diff --git a/apps/backend/src/library/library.service.ts b/apps/backend/src/library/library.service.ts index e69de29b..d85cef9a 100644 --- a/apps/backend/src/library/library.service.ts +++ b/apps/backend/src/library/library.service.ts @@ -0,0 +1,77 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Library } from './library.entity'; +import { Anthology } from '../anthology/anthology.entity'; + +@Injectable() +export class LibraryService { + constructor(@InjectRepository(Library) private repo: Repository) {} + + async create(anthologies: Anthology[]) { + const libraryId = (await this.repo.count()) + 1; + const library = this.repo.create({ + id: libraryId, + anthologies: anthologies, + }); + + return this.repo.save(library); + } + + findOne(id: number) { + if (!id) { + return null; + } + + return this.repo.findOneBy({ id }); + } + + findAll() { + return this.repo.find(); + } + + async update(id: number, attrs: Partial) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + Object.assign(library, attrs); + + return this.repo.save(library); + } + + async remove(id: number) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + return this.repo.remove(library); + } + + async addAnthology(id: number, anthology: Anthology | Anthology[]) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + library.anthologies.concat(anthology); + return this.repo.save(library); + } + + async removeAnthology(id: number, anthology: Anthology) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + library.anthologies.filter((a) => a !== anthology); + return this.repo.save(library); + } +} From eed27a1de39c5730688a40d4e9278a96afb66fdb Mon Sep 17 00:00:00 2001 From: robpatterson13 Date: Mon, 13 Oct 2025 11:00:36 -0400 Subject: [PATCH 6/6] removed unnecessary imports from library controller --- apps/backend/src/library/library.module.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/backend/src/library/library.module.ts b/apps/backend/src/library/library.module.ts index 2f035d44..4afe3a37 100644 --- a/apps/backend/src/library/library.module.ts +++ b/apps/backend/src/library/library.module.ts @@ -3,13 +3,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { LibraryController } from './library.controller'; import { LibraryService } from './library.service'; import { Library } from './library.entity'; -import { JwtStrategy } from '../auth/jwt.strategy'; -import { CurrentUserInterceptor } from '../interceptors/current-user.interceptor'; -import { AuthService } from '../auth/auth.service'; @Module({ imports: [TypeOrmModule.forFeature([Library])], controllers: [LibraryController], - providers: [LibraryService, AuthService, JwtStrategy, CurrentUserInterceptor], + providers: [LibraryService], }) export class LibraryModule {}