Changeset ea09e98 for database


Ignore:
Timestamp:
12/23/25 04:22:06 (7 months ago)
Author:
Tome <gjorgievtome@…>
Branches:
main
Children:
6196d60
Parents:
226fbbb
Message:

Update and add

Location:
database/drizzle
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • database/drizzle/queries/builds.ts

    r226fbbb rea09e98  
    99} from "../schema";
    1010import {eq, desc, and, sql, ilike} from "drizzle-orm";
     11import {inArray} from "drizzle-orm/sql/expressions/conditions";
    1112
    1213export async function getPendingBuilds(db: Database) {
     
    5152        })
    5253        .where(
    53             eq(buildsTable.id, buildId)
    54         );
    55 
    56     return result.rowCount;
     54            and(
     55                eq(buildsTable.id, buildId),
     56                eq(buildsTable.isApproved, !isApproved)
     57            )
     58        )
     59        .returning({
     60            id: buildsTable.id
     61        })
     62
     63    return result.length;
    5764}
    5865
     
    257264            ...buildDetails,
    258265            components: components.map(c => c.component),
    259             reviews: reviews,
     266            reviews: reviews.map(r => ({
     267                username: r.username,
     268                content: r.content,
     269                createdAt: r.createdAt
     270            })),
    260271            ratingStatistics: ratingStatistics,
    261272            userRating,
     
    302313
    303314export async function setBuildRating(db: Database, userId: number, buildId: number, value: number) {
    304     const result = await db
     315    const [result] = await db
    305316        .insert(ratingBuildsTable)
    306317        .values({
     
    314325                value: value,
    315326            },
    316         });
     327        })
     328        .returning({
     329            userId: ratingBuildsTable.userId,
     330            buildId: ratingBuildsTable.buildId,
     331            value: ratingBuildsTable.value
     332        })
     333
     334    return result;
    317335}
    318336
    319337export async function setBuildReview(db: Database, userId: number,  buildId: number, content: string) {
    320     const existing = await db
    321         .select()
    322         .from(reviewsTable)
    323         .where(
    324             and(
    325                 eq(reviewsTable.userId, userId),
    326                 eq(reviewsTable.buildId, buildId)
    327             )
    328         )
    329         .limit(1);
    330 
    331     if (existing.length) {
    332         const result = await db
    333             .update(reviewsTable)
    334             .set({
    335                 content: content,
    336             })
    337             .where(
    338                 and(
    339                     eq(reviewsTable.userId, userId),
    340                     eq(reviewsTable.buildId, buildId)
    341                 )
    342             );
    343 
    344         return;
    345     }
    346 
    347     const result = await db
     338    const [result] = await db
    348339        .insert(reviewsTable)
    349340        .values({
     
    352343            content,
    353344            createdAt: new Date().toISOString().split('T')[0]
    354         });
     345        })
     346        .onConflictDoUpdate({
     347            target: [reviewsTable.userId, reviewsTable.buildId],
     348            set: {
     349                content: content,
     350                createdAt: new Date().toISOString().split('T')[0]
     351            },
     352        })
     353        .returning({
     354            userId: reviewsTable.userId,
     355            buildId: reviewsTable.buildId,
     356            content: reviewsTable.content,
     357            createdAt: reviewsTable.createdAt
     358        })
     359
     360    return result;
    355361}
    356362
    357363export async function cloneBuild(db: Database, userId: number, buildId: number) {
    358     const [buildToClone] = await db
    359         .select()
    360         .from(buildsTable)
    361         .where(
    362             eq(buildsTable.id, buildId)
    363         )
    364         .limit(1);
    365 
    366     if (!buildToClone) return null;
    367 
    368     const [newBuild] = await db
    369         .insert(buildsTable)
    370         .values({
    371             userId: userId,
    372             name: `${buildToClone.name} (copy)`,
    373             createdAt: new Date().toISOString().split('T')[0],
    374             description: buildToClone.description,
    375             totalPrice: buildToClone.totalPrice,
    376             isApproved: false
    377         })
    378         .returning({ id: buildsTable.id });
    379 
    380     const components = await db
    381         .select()
    382         .from(buildComponentsTable)
    383         .where(
    384             eq(buildComponentsTable.buildId, buildId)
    385         );
    386 
    387     if(components.length) {
    388         await db
    389             .insert(buildComponentsTable)
    390             .values(
    391                 components.map(component => ({
    392                     buildId: newBuild.id,
    393                     componentId: component.componentId
    394                 }))
    395             );
    396     }
    397 
    398     return newBuild.id;
    399 }
     364    return db.transaction(async (tx) => {
     365        const [buildToClone] = await tx
     366            .select({
     367                id: buildsTable.id,
     368                userId: buildsTable.userId,
     369                name: buildsTable.name,
     370                description: buildsTable.description,
     371                totalPrice: buildsTable.totalPrice,
     372            })
     373            .from(buildsTable)
     374            .where(
     375                eq(buildsTable.id, buildId)
     376            )
     377            .limit(1);
     378
     379        if (!buildToClone) return null;
     380
     381        const [newBuild] = await tx
     382            .insert(buildsTable)
     383            .values({
     384                userId: userId,
     385                name: `${buildToClone.name} (copy)`,
     386                createdAt: new Date().toISOString().split('T')[0],
     387                description: buildToClone.description,
     388                totalPrice: buildToClone.totalPrice,
     389                isApproved: false
     390            })
     391            .returning({
     392                id: buildsTable.id
     393            });
     394
     395        const components = await tx
     396            .select()
     397            .from(buildComponentsTable)
     398            .where(
     399                eq(buildComponentsTable.buildId, buildId)
     400            );
     401
     402        if(components.length) {
     403            await tx
     404                .insert(buildComponentsTable)
     405                .values(
     406                    components.map(component => ({
     407                        buildId: newBuild.id,
     408                        componentId: component.componentId
     409                    }))
     410                );
     411        }
     412
     413        const [clonedBuild] = await tx
     414            .select({
     415                id: buildsTable.id,
     416                userId: buildsTable.userId,
     417                name: buildsTable.name,
     418                createdAt: buildsTable.createdAt,
     419                description: buildsTable.description,
     420                totalPrice: buildsTable.totalPrice
     421            })
     422            .from(buildsTable)
     423            .innerJoin(
     424                usersTable,
     425                eq(buildsTable.userId, usersTable.id)
     426            )
     427            .where(
     428                eq(buildsTable.id, newBuild.id)
     429            )
     430            .limit(1);
     431
     432        const clonedComponents = await tx
     433            .select({
     434                componentId: buildComponentsTable.componentId,
     435                component: componentsTable
     436            })
     437            .from(buildComponentsTable)
     438            .innerJoin(
     439                componentsTable,
     440                eq(buildComponentsTable.componentId, componentsTable.id)
     441            )
     442            .where(
     443                eq(buildComponentsTable.buildId, newBuild.id)
     444            );
     445
     446        return {
     447            ...clonedBuild,
     448            components: clonedComponents.map(c => c.component)
     449        };
     450    });
     451}
     452
     453export async function deleteBuild(db: Database, userId: number, buildId: number) {
     454    const result = await db
     455        .delete(buildsTable)
     456        .where(
     457            and(
     458                eq(buildsTable.id, buildId),
     459                eq(buildsTable.userId, userId)
     460            )
     461        )
     462        .returning({
     463            id: buildsTable.id
     464        })
     465
     466    return result.length;
     467}
     468
     469export async function addNewBuild(db: Database, userId: number, name: string, description: string, componentIds: number[]) {
     470    return db.transaction(async (tx) => {
     471        const components = await tx
     472            .select({
     473                price:  componentsTable.price
     474            })
     475            .from(componentsTable)
     476            .where(
     477                inArray(componentsTable.id, componentIds)
     478            );
     479
     480        const totalPrice = components.reduce((sum, c) => sum + Number(c.price), 0);
     481
     482        const [newBuild] = await tx
     483            .insert(buildsTable)
     484            .values({
     485                userId: userId,
     486                name: name,
     487                createdAt: new Date().toISOString().split('T')[0],
     488                description: description,
     489                totalPrice: totalPrice.toFixed(2),
     490                isApproved: false
     491            })
     492            .returning({
     493                id: buildsTable.id
     494            });
     495
     496        if(components.length) {
     497            await tx.insert(buildComponentsTable)
     498                .values(
     499                    componentIds.map(componentId => ({
     500                        buildId: newBuild.id,
     501                        componentId: componentId
     502                    }))
     503                );
     504        }
     505
     506        return newBuild.id;
     507    });
     508}
     509
     510export async function editBuild(db: Database, userId: number, buildId: number, name: string, description: string, componentIds: number[]) {
     511    return db.transaction(async (tx) => {
     512        const [build] = await tx
     513            .select({
     514                id: buildsTable.id,
     515                userId: buildsTable.userId,
     516                isApproved: buildsTable.isApproved
     517            })
     518            .from(buildsTable)
     519            .where(
     520                and(
     521                    eq(buildsTable.id, buildId),
     522                    eq(buildsTable.userId, userId)
     523                )
     524            )
     525            .limit(1);
     526
     527        if (!build) return null;
     528        if (build.isApproved) return null;
     529
     530        const components = await tx
     531            .select({
     532                id: componentsTable.id,
     533                price: componentsTable.price
     534            })
     535            .from(componentsTable)
     536            .where(
     537                inArray(componentsTable.id, componentIds)
     538            );
     539
     540        const totalPrice = components.reduce((sum, c) => sum + Number(c.price), 0);
     541
     542        await tx
     543            .update(buildsTable)
     544            .set({
     545                name: name,
     546                description: description,
     547                totalPrice: totalPrice.toFixed(2),
     548            })
     549            .where(
     550                and(
     551                    eq(buildsTable.id, buildId),
     552                    eq(buildsTable.userId, userId)
     553                )
     554            );
     555
     556        await tx
     557            .delete(buildComponentsTable)
     558            .where(
     559                eq(buildComponentsTable.buildId, buildId)
     560            );
     561
     562        if(components.length) {
     563            await tx.insert(buildComponentsTable)
     564                .values(
     565                    componentIds.map(componentId => ({
     566                        buildId: buildId,
     567                        componentId: componentId
     568                    }))
     569                );
     570        }
     571
     572        return build.id;
     573    });
     574}
  • database/drizzle/queries/components.ts

    r226fbbb rea09e98  
    11import type { Database } from "../db";
    2 import {buildsTable, componentsTable } from "../schema";
     2import {
     3    buildsTable, cablesTable, caseMoboFormFactorsTable, casePsFormFactorsTable, caseStorageFormFactorsTable,
     4    componentsTable, coolerCPUSocketsTable, coolersTable,
     5    CPUTable,
     6    GPUTable, memoryCardsTable,
     7    memoryTable, motherboardsTable,
     8    networkAdaptersTable,
     9    networkCardsTable, opticalDrivesTable, pcCasesTable, powerSupplyTable, soundCardsTable, storageTable
     10} from "../schema";
    311import {and, desc, eq, ilike} from "drizzle-orm";
    412
     
    4250
    4351export async function getComponentDetails(db: Database, componentId: number) {
    44     const [componentDetails] = await db
     52    const [component] = await db
    4553        .select()
    4654        .from(componentsTable)
     
    5058        .limit(1);
    5159
    52     return componentDetails ?? null;
     60    if(!component) return null;
     61
     62    if (!component) return null;
     63
     64    let details: any = {};
     65
     66    switch (component.type) {
     67        case 'cpu':
     68            [details] = await db.select().from(CPUTable).where(eq(CPUTable.componentId, componentId));
     69            break;
     70        case 'gpu':
     71            [details] = await db.select().from(GPUTable).where(eq(GPUTable.componentId, componentId));
     72            break;
     73        case 'memory':
     74            [details] = await db.select().from(memoryTable).where(eq(memoryTable.componentId, componentId));
     75            break;
     76        case 'power_supply':
     77            [details] = await db.select().from(powerSupplyTable).where(eq(powerSupplyTable.componentId, componentId));
     78            break;
     79        case 'case':
     80            [details] = await db.select().from(pcCasesTable).where(eq(pcCasesTable.componentId, componentId));
     81            if (details) {
     82                details.storageFormFactors = await db.select().from(caseStorageFormFactorsTable).where(eq(caseStorageFormFactorsTable.caseId, componentId));
     83                details.psFormFactors = await db.select().from(casePsFormFactorsTable).where(eq(casePsFormFactorsTable.caseId, componentId));
     84                details.moboFormFactors = await db.select().from(caseMoboFormFactorsTable).where(eq(caseMoboFormFactorsTable.caseId, componentId));
     85            }
     86            break;
     87        case 'cooler':
     88            [details] = await db.select().from(coolersTable).where(eq(coolersTable.componentId, componentId));
     89            if (details) {
     90                details.cpuSockets = await db.select().from(coolerCPUSocketsTable).where(eq(coolerCPUSocketsTable.coolerId, componentId));
     91            }
     92            break;
     93        case 'motherboard':
     94            [details] = await db.select().from(motherboardsTable).where(eq(motherboardsTable.componentId, componentId));
     95            break;
     96        case 'storage':
     97            [details] = await db.select().from(storageTable).where(eq(storageTable.componentId, componentId));
     98            break;
     99        case 'memory_card':
     100            [details] = await db.select().from(memoryCardsTable).where(eq(memoryCardsTable.componentId, componentId));
     101            break;
     102        case 'optical_drive':
     103            [details] = await db.select().from(opticalDrivesTable).where(eq(opticalDrivesTable.componentId, componentId));
     104            break;
     105        case 'sound_card':
     106            [details] = await db.select().from(soundCardsTable).where(eq(soundCardsTable.componentId, componentId));
     107            break;
     108        case 'cables':
     109            [details] = await db.select().from(cablesTable).where(eq(cablesTable.componentId, componentId));
     110            break;
     111        case 'network_adapter':
     112            [details] = await db.select().from(networkAdaptersTable).where(eq(networkAdaptersTable.componentId, componentId));
     113            break;
     114        case 'network_card':
     115            [details] = await db.select().from(networkCardsTable).where(eq(networkCardsTable.componentId, componentId));
     116            break;
     117    }
     118
     119    return {
     120        ...component,
     121        details: details || null
     122    };
    53123}
     124
     125export async function addNewComponent(db: Database, name: string, brand: string, price: number, imgUrl: string, type: string, specificData: any) {
     126    return db.transaction(async (tx) => {
     127        const [newComponent] = await tx
     128            .insert(componentsTable)
     129            .values({
     130                name: name,
     131                brand: brand,
     132                price: price.toFixed(2),
     133                imgUrl: imgUrl,
     134                type: type
     135            })
     136            .returning({
     137                id: componentsTable.id
     138            });
     139
     140        const componentId = newComponent.id;
     141
     142        switch (type) {
     143            case 'cpu':
     144                await tx.insert(CPUTable).values({
     145                    componentId,
     146                    socket: specificData.socket,
     147                    cores: specificData.cores,
     148                    threads: specificData.threads,
     149                    baseClock: specificData.baseClock,
     150                    boostClock: specificData.boostClock,
     151                    tdp: specificData.tdp,
     152                });
     153                break;
     154
     155            case 'gpu':
     156                await tx.insert(GPUTable).values({
     157                    componentId,
     158                    vram: specificData.vram,
     159                    tdp: specificData.tdp,
     160                    baseClock: specificData.baseClock,
     161                    boostClock: specificData.boostClock,
     162                    chipset: specificData.chipset,
     163                    length: specificData.length,
     164                });
     165                break;
     166
     167            case 'memory':
     168                await tx.insert(memoryTable).values({
     169                    componentId,
     170                    type: specificData.type,
     171                    speed: specificData.speed,
     172                    capacity: specificData.capacity,
     173                    modules: specificData.modules,
     174                });
     175                break;
     176
     177            case 'power_supply':
     178                await tx.insert(powerSupplyTable).values({
     179                    componentId,
     180                    type: specificData.type,
     181                    wattage: specificData.wattage,
     182                    formFactor: specificData.formFactor,
     183                });
     184                break;
     185
     186            case 'case':
     187                await tx.insert(pcCasesTable).values({
     188                    componentId,
     189                    coolerMaxHeight: specificData.coolerMaxHeight,
     190                    gpuMaxLength: specificData.gpuMaxLength,
     191                });
     192
     193                if (specificData.storageFormFactors) {
     194                    await tx.insert(caseStorageFormFactorsTable).values(
     195                        specificData.storageFormFactors.map((sf: any) => ({
     196                            caseId: componentId,
     197                            formFactor: sf.formFactor,
     198                            numSlots: sf.numSlots,
     199                        }))
     200                    );
     201                }
     202
     203                if (specificData.psFormFactors) {
     204                    await tx.insert(casePsFormFactorsTable).values(
     205                        specificData.psFormFactors.map((pf: any) => ({
     206                            caseId: componentId,
     207                            formFactor: pf.formFactor,
     208                        }))
     209                    );
     210                }
     211
     212                if (specificData.moboFormFactors) {
     213                    await tx.insert(caseMoboFormFactorsTable).values(
     214                        specificData.moboFormFactors.map((mf: any) => ({
     215                            caseId: componentId,
     216                            formFactor: mf.formFactor,
     217                        }))
     218                    );
     219                }
     220                break;
     221
     222            case 'cooler':
     223                await tx.insert(coolersTable).values({
     224                    componentId,
     225                    type: specificData.type,
     226                    height: specificData.height,
     227                    maxTdpSupported: specificData.maxTdpSupported,
     228                });
     229
     230                if (specificData.cpuSockets) {
     231                    await tx.insert(coolerCPUSocketsTable).values(
     232                        specificData.cpuSockets.map((socket: string) => ({
     233                            coolerId: componentId,
     234                            socket: socket,
     235                        }))
     236                    );
     237                }
     238                break;
     239
     240            case 'motherboard':
     241                await tx.insert(motherboardsTable).values({
     242                    componentId,
     243                    socket: specificData.socket,
     244                    chipset: specificData.chipset,
     245                    formFactor: specificData.formFactor,
     246                    ramType: specificData.ramType,
     247                    numRamSlots: specificData.numRamSlots,
     248                    maxRamCapacity: specificData.maxRamCapacity,
     249                });
     250                break;
     251
     252            case 'storage':
     253                await tx.insert(storageTable).values({
     254                    componentId,
     255                    type: specificData.type,
     256                    capacity: specificData.capacity,
     257                    formFactor: specificData.formFactor,
     258                });
     259                break;
     260
     261            case 'memory_card':
     262                await tx.insert(memoryCardsTable).values({
     263                    componentId,
     264                    numSlots: specificData.numSlots,
     265                    interface: specificData.interface,
     266                });
     267                break;
     268
     269            case 'optical_drive':
     270                await tx.insert(opticalDrivesTable).values({
     271                    componentId,
     272                    formFactor: specificData.formFactor,
     273                    type: specificData.type,
     274                    interface: specificData.interface,
     275                    writeSpeed: specificData.writeSpeed,
     276                    readSpeed: specificData.readSpeed,
     277                });
     278                break;
     279
     280            case 'sound_card':
     281                await tx.insert(soundCardsTable).values({
     282                    componentId,
     283                    sampleRate: specificData.sampleRate,
     284                    bitDepth: specificData.bitDepth,
     285                    chipset: specificData.chipset,
     286                    interface: specificData.interface,
     287                    channel: specificData.channel,
     288                });
     289                break;
     290
     291            case 'cables':
     292                await tx.insert(cablesTable).values({
     293                    componentId,
     294                    lengthCm: specificData.lengthCm,
     295                    type: specificData.type,
     296                });
     297                break;
     298
     299            case 'network_adapter':
     300                await tx.insert(networkAdaptersTable).values({
     301                    componentId,
     302                    wifiVersion: specificData.wifiVersion,
     303                    interface: specificData.interface,
     304                    numAntennas: specificData.numAntennas,
     305                });
     306                break;
     307
     308            case 'network_card':
     309                await tx.insert(networkCardsTable).values({
     310                    componentId,
     311                    numPorts: specificData.numPorts,
     312                    speed: specificData.speed,
     313                    interface: specificData.interface,
     314                });
     315                break;
     316
     317            default:
     318                return null;
     319        }
     320
     321        return componentId;
     322    });
     323}
  • database/drizzle/queries/users.ts

    r226fbbb rea09e98  
    6969}
    7070
    71 export async function addComponentSuggestion(db: Database, userId: number, link: string, description: string, componentType: string) {
     71export async function addNewComponentSuggestion(db: Database, userId: number, link: string, description: string, componentType: string) {
    7272    const [newSuggestion] = await db
    7373        .insert(suggestionsTable)
  • database/drizzle/schema/builds.ts

    r226fbbb rea09e98  
    1 import {pgTable, serial, integer, text, numeric, boolean, date, primaryKey, check} from "drizzle-orm/pg-core";
     1import {pgTable, serial, integer, text, numeric, boolean, date, primaryKey, check, unique} from "drizzle-orm/pg-core";
    22import { usersTable } from "./users";
    33import { componentsTable } from "./components";
     
    5858
    5959export const reviewsTable = pgTable("review", {
    60     id: serial("id").primaryKey(),
     60        id: serial("id").primaryKey(),
    6161
    62     buildId: integer("build_id")
    63         .notNull()
    64         .references(() => buildsTable.id, { onDelete: "cascade", onUpdate: "cascade" }),
    65     userId: integer("user_id")
    66         .notNull()
    67         .references(() => usersTable.id, { onDelete: "cascade", onUpdate: "cascade" }),
    68     content: text("content").notNull(),
    69     createdAt: date("created_at").notNull(),
    70 });
     62        buildId: integer("build_id")
     63            .notNull()
     64            .references(() => buildsTable.id, { onDelete: "cascade", onUpdate: "cascade" }),
     65        userId: integer("user_id")
     66            .notNull()
     67            .references(() => usersTable.id, { onDelete: "cascade", onUpdate: "cascade" }),
     68        content: text("content").notNull(),
     69        createdAt: date("created_at").notNull()
     70    },
     71    (t) => ({
     72        uniqueUserBuild: unique().on(t.buildId, t.userId),
     73    }),
     74);
    7175
    7276export type buildItem = typeof buildsTable.$inferSelect;
Note: See TracChangeset for help on using the changeset viewer.