Changeset ea09e98
- Timestamp:
- 12/23/25 04:22:06 (7 months ago)
- Branches:
- main
- Children:
- 6196d60
- Parents:
- 226fbbb
- Location:
- database/drizzle
- Files:
-
- 4 edited
-
queries/builds.ts (modified) (6 diffs)
-
queries/components.ts (modified) (3 diffs)
-
queries/users.ts (modified) (1 diff)
-
schema/builds.ts (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
database/drizzle/queries/builds.ts
r226fbbb rea09e98 9 9 } from "../schema"; 10 10 import {eq, desc, and, sql, ilike} from "drizzle-orm"; 11 import {inArray} from "drizzle-orm/sql/expressions/conditions"; 11 12 12 13 export async function getPendingBuilds(db: Database) { … … 51 52 }) 52 53 .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; 57 64 } 58 65 … … 257 264 ...buildDetails, 258 265 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 })), 260 271 ratingStatistics: ratingStatistics, 261 272 userRating, … … 302 313 303 314 export async function setBuildRating(db: Database, userId: number, buildId: number, value: number) { 304 const result= await db315 const [result] = await db 305 316 .insert(ratingBuildsTable) 306 317 .values({ … … 314 325 value: value, 315 326 }, 316 }); 327 }) 328 .returning({ 329 userId: ratingBuildsTable.userId, 330 buildId: ratingBuildsTable.buildId, 331 value: ratingBuildsTable.value 332 }) 333 334 return result; 317 335 } 318 336 319 337 export 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 348 339 .insert(reviewsTable) 349 340 .values({ … … 352 343 content, 353 344 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; 355 361 } 356 362 357 363 export 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 453 export 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 469 export 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 510 export 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 1 1 import type { Database } from "../db"; 2 import {buildsTable, componentsTable } from "../schema"; 2 import { 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"; 3 11 import {and, desc, eq, ilike} from "drizzle-orm"; 4 12 … … 42 50 43 51 export async function getComponentDetails(db: Database, componentId: number) { 44 const [component Details] = await db52 const [component] = await db 45 53 .select() 46 54 .from(componentsTable) … … 50 58 .limit(1); 51 59 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 }; 53 123 } 124 125 export 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 69 69 } 70 70 71 export async function add ComponentSuggestion(db: Database, userId: number, link: string, description: string, componentType: string) {71 export async function addNewComponentSuggestion(db: Database, userId: number, link: string, description: string, componentType: string) { 72 72 const [newSuggestion] = await db 73 73 .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";1 import {pgTable, serial, integer, text, numeric, boolean, date, primaryKey, check, unique} from "drizzle-orm/pg-core"; 2 2 import { usersTable } from "./users"; 3 3 import { componentsTable } from "./components"; … … 58 58 59 59 export const reviewsTable = pgTable("review", { 60 id: serial("id").primaryKey(),60 id: serial("id").primaryKey(), 61 61 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 ); 71 75 72 76 export type buildItem = typeof buildsTable.$inferSelect;
Note:
See TracChangeset
for help on using the changeset viewer.
