@trafficgroup/knex-rel 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.claude/settings.local.json +2 -5
  2. package/CLAUDE.md +11 -2
  3. package/dist/constants/folder.constants.d.ts +12 -0
  4. package/dist/constants/folder.constants.js +28 -0
  5. package/dist/constants/folder.constants.js.map +1 -0
  6. package/dist/constants/video.constants.d.ts +2 -2
  7. package/dist/constants/video.constants.js +9 -5
  8. package/dist/constants/video.constants.js.map +1 -1
  9. package/dist/dao/VideoMinuteResultDAO.d.ts +1 -1
  10. package/dist/dao/VideoMinuteResultDAO.js +29 -23
  11. package/dist/dao/VideoMinuteResultDAO.js.map +1 -1
  12. package/dist/dao/auth/auth.dao.js +4 -1
  13. package/dist/dao/auth/auth.dao.js.map +1 -1
  14. package/dist/dao/batch/batch.dao.js +13 -14
  15. package/dist/dao/batch/batch.dao.js.map +1 -1
  16. package/dist/dao/camera/camera.dao.js +10 -7
  17. package/dist/dao/camera/camera.dao.js.map +1 -1
  18. package/dist/dao/chat/chat.dao.d.ts +1 -1
  19. package/dist/dao/chat/chat.dao.js +27 -40
  20. package/dist/dao/chat/chat.dao.js.map +1 -1
  21. package/dist/dao/folder/folder.dao.d.ts +10 -1
  22. package/dist/dao/folder/folder.dao.js +44 -6
  23. package/dist/dao/folder/folder.dao.js.map +1 -1
  24. package/dist/dao/location/location.dao.js +16 -9
  25. package/dist/dao/location/location.dao.js.map +1 -1
  26. package/dist/dao/message/message.dao.d.ts +1 -1
  27. package/dist/dao/message/message.dao.js +18 -26
  28. package/dist/dao/message/message.dao.js.map +1 -1
  29. package/dist/dao/report-configuration/report-configuration.dao.js +32 -31
  30. package/dist/dao/report-configuration/report-configuration.dao.js.map +1 -1
  31. package/dist/dao/study/study.dao.js +7 -2
  32. package/dist/dao/study/study.dao.js.map +1 -1
  33. package/dist/dao/user/user.dao.js +4 -1
  34. package/dist/dao/user/user.dao.js.map +1 -1
  35. package/dist/dao/user-push-notification-token/user-push-notification-token.dao.js +26 -8
  36. package/dist/dao/user-push-notification-token/user-push-notification-token.dao.js.map +1 -1
  37. package/dist/dao/video/video.dao.js +30 -28
  38. package/dist/dao/video/video.dao.js.map +1 -1
  39. package/dist/index.d.ts +8 -5
  40. package/dist/index.js +5 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/interfaces/batch/batch.interfaces.d.ts +1 -1
  43. package/dist/interfaces/camera/camera.interfaces.d.ts +1 -1
  44. package/dist/interfaces/chat/chat.interfaces.d.ts +3 -3
  45. package/dist/interfaces/folder/folder.interfaces.d.ts +1 -1
  46. package/dist/interfaces/message/message.interfaces.d.ts +2 -2
  47. package/dist/interfaces/study/study.interfaces.d.ts +2 -2
  48. package/dist/interfaces/user/user.interfaces.d.ts +1 -1
  49. package/dist/interfaces/user-push-notification-token/user-push-notification-token.interfaces.d.ts +1 -1
  50. package/dist/interfaces/video/video.interfaces.d.ts +2 -2
  51. package/migrations/20250717160737_migration.ts +1 -1
  52. package/migrations/20250717160908_migration.ts +5 -2
  53. package/migrations/20250717161310_migration.ts +1 -1
  54. package/migrations/20250717161406_migration.ts +3 -3
  55. package/migrations/20250717162431_migration.ts +1 -1
  56. package/migrations/20250717173228_migration.ts +2 -2
  57. package/migrations/20250717204731_migration.ts +1 -1
  58. package/migrations/20250722210109_migration.ts +8 -4
  59. package/migrations/20250722211019_migration.ts +1 -1
  60. package/migrations/20250723153852_migration.ts +13 -10
  61. package/migrations/20250723162257_migration.ts +4 -7
  62. package/migrations/20250723171109_migration.ts +4 -7
  63. package/migrations/20250723205331_migration.ts +6 -9
  64. package/migrations/20250724191345_migration.ts +8 -11
  65. package/migrations/20250730180932_migration.ts +14 -13
  66. package/migrations/20250730213625_migration.ts +8 -11
  67. package/migrations/20250804124509_migration.ts +26 -21
  68. package/migrations/20250804132053_migration.ts +5 -8
  69. package/migrations/20250804164518_migration.ts +7 -7
  70. package/migrations/20250823223016_migration.ts +32 -21
  71. package/migrations/20250910015452_migration.ts +18 -6
  72. package/migrations/20250911000000_migration.ts +18 -4
  73. package/migrations/20250917144153_migration.ts +14 -7
  74. package/migrations/20250930200521_migration.ts +8 -4
  75. package/migrations/20251010143500_migration.ts +27 -6
  76. package/migrations/20251020225758_migration.ts +51 -15
  77. package/migrations/20251112120000_migration.ts +10 -2
  78. package/migrations/20251112120200_migration.ts +19 -7
  79. package/migrations/20251112120300_migration.ts +7 -2
  80. package/package.json +1 -1
  81. package/src/constants/folder.constants.ts +29 -0
  82. package/src/constants/video.constants.ts +11 -7
  83. package/src/d.types.ts +18 -14
  84. package/src/dao/VideoMinuteResultDAO.ts +72 -49
  85. package/src/dao/auth/auth.dao.ts +58 -55
  86. package/src/dao/batch/batch.dao.ts +101 -98
  87. package/src/dao/camera/camera.dao.ts +124 -121
  88. package/src/dao/chat/chat.dao.ts +45 -45
  89. package/src/dao/folder/folder.dao.ts +112 -55
  90. package/src/dao/location/location.dao.ts +109 -87
  91. package/src/dao/message/message.dao.ts +32 -32
  92. package/src/dao/report-configuration/report-configuration.dao.ts +370 -342
  93. package/src/dao/study/study.dao.ts +88 -63
  94. package/src/dao/user/user.dao.ts +52 -50
  95. package/src/dao/user-push-notification-token/user-push-notification-token.dao.ts +80 -48
  96. package/src/dao/video/video.dao.ts +385 -334
  97. package/src/entities/BaseEntity.ts +1 -1
  98. package/src/index.ts +42 -17
  99. package/src/interfaces/auth/auth.interfaces.ts +10 -10
  100. package/src/interfaces/batch/batch.interfaces.ts +1 -1
  101. package/src/interfaces/camera/camera.interfaces.ts +9 -9
  102. package/src/interfaces/chat/chat.interfaces.ts +4 -4
  103. package/src/interfaces/folder/folder.interfaces.ts +2 -2
  104. package/src/interfaces/location/location.interfaces.ts +7 -7
  105. package/src/interfaces/message/message.interfaces.ts +3 -3
  106. package/src/interfaces/report-configuration/report-configuration.interfaces.ts +16 -16
  107. package/src/interfaces/study/study.interfaces.ts +3 -3
  108. package/src/interfaces/user/user.interfaces.ts +9 -9
  109. package/src/interfaces/user-push-notification-token/user-push-notification-token.interfaces.ts +9 -9
  110. package/src/interfaces/video/video.interfaces.ts +34 -34
@@ -69,15 +69,15 @@ interface IGroupedResponse {
69
69
  }
70
70
 
71
71
  interface IStudyTimeGroupResult {
72
- absoluteTime: string; // ISO 8601 start of bucket
72
+ absoluteTime: string; // ISO 8601 start of bucket
73
73
  groupIndex: number;
74
- startMinute: number; // Start minute number (0-based from video start)
75
- endMinute: number; // End minute number (0-based from video start)
76
- label: string; // Formatted label
74
+ startMinute: number; // Start minute number (0-based from video start)
75
+ endMinute: number; // End minute number (0-based from video start)
76
+ label: string; // Formatted label
77
77
  results: ITMCResult | IATRResult;
78
78
  minuteCount: number;
79
79
  videoCount: number;
80
- contributingVideos: string[]; // Video UUIDs
80
+ contributingVideos: string[]; // Video UUIDs
81
81
  }
82
82
 
83
83
  interface IGroupedStudyResponse {
@@ -87,7 +87,7 @@ interface IGroupedStudyResponse {
87
87
  study: {
88
88
  uuid: string;
89
89
  name: string;
90
- type: 'TMC' | 'ATR';
90
+ type: "TMC" | "ATR";
91
91
  status: string;
92
92
  };
93
93
  videoCount: number;
@@ -423,37 +423,41 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
423
423
  // Use Knex query builder for safe parameter binding
424
424
  const groupingQuery = this.knex(this.tableName)
425
425
  .select(
426
- this.knex.raw('FLOOR(minute_number / ?) as group_index', [groupingMinutes]),
427
- this.knex.raw('MIN(minute_number) as start_minute'),
428
- this.knex.raw('MAX(minute_number) as end_minute'),
429
- this.knex.raw('COUNT(*) as minute_count'),
430
- this.knex.raw('array_agg(results ORDER BY minute_number) as all_results')
426
+ this.knex.raw("FLOOR(minute_number / ?) as group_index", [
427
+ groupingMinutes,
428
+ ]),
429
+ this.knex.raw("MIN(minute_number) as start_minute"),
430
+ this.knex.raw("MAX(minute_number) as end_minute"),
431
+ this.knex.raw("COUNT(*) as minute_count"),
432
+ this.knex.raw(
433
+ "array_agg(results ORDER BY minute_number) as all_results",
434
+ ),
431
435
  )
432
- .where('video_id', video.id);
436
+ .where("video_id", video.id);
433
437
 
434
438
  if (startMinute !== undefined) {
435
- groupingQuery.where('minute_number', '>=', startMinute);
439
+ groupingQuery.where("minute_number", ">=", startMinute);
436
440
  }
437
441
 
438
442
  if (endMinute !== undefined) {
439
- groupingQuery.where('minute_number', '<=', endMinute);
443
+ groupingQuery.where("minute_number", "<=", endMinute);
440
444
  }
441
445
 
442
446
  const rows = await groupingQuery
443
- .groupBy('group_index')
444
- .orderBy('group_index');
447
+ .groupBy("group_index")
448
+ .orderBy("group_index");
445
449
 
446
450
  // Aggregate the results in TypeScript based on video type
447
451
  const aggregatedGroups: IGroupedResult[] = rows.map((row: any) => {
448
- if (!row || typeof row !== 'object') {
449
- throw new Error('Invalid row data received from database query');
452
+ if (!row || typeof row !== "object") {
453
+ throw new Error("Invalid row data received from database query");
450
454
  }
451
455
 
452
456
  const allResults = Array.isArray(row.all_results) ? row.all_results : [];
453
-
457
+
454
458
  // Determine video type based on multiple factors
455
- let studyType = video.videoType || 'ATR'; // Default fallback to ATR
456
-
459
+ let studyType = video.videoType || "ATR"; // Default fallback to ATR
460
+
457
461
  // Check if minute data has study_type field (ATR usually does)
458
462
  if (allResults.length > 0 && allResults[0].study_type) {
459
463
  studyType = allResults[0].study_type;
@@ -465,20 +469,24 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
465
469
  const vehicleKeys = Object.keys(sampleResult.vehicles);
466
470
  if (vehicleKeys.length > 0) {
467
471
  const firstVehicleType = sampleResult.vehicles[vehicleKeys[0]];
468
- if (firstVehicleType && typeof firstVehicleType === 'object') {
472
+ if (firstVehicleType && typeof firstVehicleType === "object") {
469
473
  const directions = Object.keys(firstVehicleType);
470
- if (directions.includes('NORTH') || directions.includes('SOUTH') ||
471
- directions.includes('EAST') || directions.includes('WEST')) {
472
- studyType = 'TMC';
474
+ if (
475
+ directions.includes("NORTH") ||
476
+ directions.includes("SOUTH") ||
477
+ directions.includes("EAST") ||
478
+ directions.includes("WEST")
479
+ ) {
480
+ studyType = "TMC";
473
481
  }
474
482
  }
475
483
  }
476
484
  }
477
485
  }
478
-
486
+
479
487
  // Aggregate based on determined video type
480
488
  let aggregatedResult;
481
- if (studyType === 'TMC') {
489
+ if (studyType === "TMC") {
482
490
  aggregatedResult = this.aggregateTMCResults(allResults);
483
491
  } else {
484
492
  aggregatedResult = this.aggregateATRResults(allResults);
@@ -569,8 +577,8 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
569
577
  "v.uuid as videoUuid",
570
578
  "v.recordingStartedAt",
571
579
  this.knex.raw(
572
- '"v"."recordingStartedAt" + (vmr.minute_number || \' minutes\')::INTERVAL as "absoluteTime"'
573
- )
580
+ '"v"."recordingStartedAt" + (vmr.minute_number || \' minutes\')::INTERVAL as "absoluteTime"',
581
+ ),
574
582
  )
575
583
  .whereIn("v.id", videoIds)
576
584
  .orderBy("absoluteTime", "asc");
@@ -608,7 +616,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
608
616
  for (const minute of minuteResults) {
609
617
  const absoluteTime = new Date(minute.absoluteTime);
610
618
  const minutesSinceEarliest = Math.floor(
611
- (absoluteTime.getTime() - earliestTime.getTime()) / (1000 * 60)
619
+ (absoluteTime.getTime() - earliestTime.getTime()) / (1000 * 60),
612
620
  );
613
621
  const bucketIndex = Math.floor(minutesSinceEarliest / groupingMinutes);
614
622
 
@@ -616,7 +624,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
616
624
  // Calculate bucket start time
617
625
  const bucketStartMinutes = bucketIndex * groupingMinutes;
618
626
  const bucketStartTime = new Date(
619
- earliestTime.getTime() + bucketStartMinutes * 60 * 1000
627
+ earliestTime.getTime() + bucketStartMinutes * 60 * 1000,
620
628
  );
621
629
 
622
630
  buckets.set(bucketIndex, {
@@ -633,12 +641,14 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
633
641
  }
634
642
 
635
643
  // Step 5: Aggregate using existing methods based on study type
636
- const aggregatedGroups: IStudyTimeGroupResult[] = Array.from(buckets.values())
644
+ const aggregatedGroups: IStudyTimeGroupResult[] = Array.from(
645
+ buckets.values(),
646
+ )
637
647
  .sort((a, b) => a.groupIndex - b.groupIndex)
638
648
  .map((bucket) => {
639
649
  let aggregatedResult: ITMCResult | IATRResult;
640
650
 
641
- if (study.type === 'TMC') {
651
+ if (study.type === "TMC") {
642
652
  aggregatedResult = this.aggregateTMCResults(bucket.results);
643
653
  } else {
644
654
  aggregatedResult = this.aggregateATRResults(bucket.results);
@@ -649,7 +659,10 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
649
659
  groupIndex: bucket.groupIndex,
650
660
  startMinute: bucket.groupIndex * groupingMinutes,
651
661
  endMinute: (bucket.groupIndex + 1) * groupingMinutes - 1,
652
- label: this.formatStudyTimeLabel(bucket.absoluteTime, groupingMinutes),
662
+ label: this.formatStudyTimeLabel(
663
+ bucket.absoluteTime,
664
+ groupingMinutes,
665
+ ),
653
666
  results: aggregatedResult,
654
667
  minuteCount: bucket.results.length,
655
668
  videoCount: bucket.videoUuids.size,
@@ -712,25 +725,27 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
712
725
  vehicles: {},
713
726
  counts: {
714
727
  total_vehicles: 0,
715
- entry_vehicles: 0
728
+ entry_vehicles: 0,
716
729
  },
717
730
  total: 0,
718
731
  totalcount: 0,
719
732
  detected_classes: {},
720
- study_type: "TMC"
733
+ study_type: "TMC",
721
734
  };
722
735
 
723
736
  for (const minute of minutes) {
724
737
  const results = minute; // minute is already the results object from array_agg
725
738
 
726
739
  // Aggregate vehicle movements by class and direction
727
- if (results.vehicles && typeof results.vehicles === 'object') {
728
- for (const [vehicleClass, directions] of Object.entries(results.vehicles)) {
740
+ if (results.vehicles && typeof results.vehicles === "object") {
741
+ for (const [vehicleClass, directions] of Object.entries(
742
+ results.vehicles,
743
+ )) {
729
744
  // Skip the 'total' pseudo vehicle class - validate it's actually aggregate data
730
- if (vehicleClass === 'total' && typeof directions === 'number') {
745
+ if (vehicleClass === "total" && typeof directions === "number") {
731
746
  continue; // This is aggregate total data, not a vehicle class
732
747
  }
733
-
748
+
734
749
  if (!aggregated.vehicles[vehicleClass]) {
735
750
  aggregated.vehicles[vehicleClass] = {};
736
751
  }
@@ -753,11 +768,12 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
753
768
  for (const [turnType, count] of Object.entries(turns)) {
754
769
  const turnCount = (count as number) || 0;
755
770
  aggregated.vehicles[vehicleClass][direction][turnType] =
756
- (aggregated.vehicles[vehicleClass][direction][turnType] || 0) + turnCount;
757
-
771
+ (aggregated.vehicles[vehicleClass][direction][turnType] ||
772
+ 0) + turnCount;
773
+
758
774
  // Add to detected_classes count for this vehicle type
759
775
  aggregated.detected_classes[vehicleClass] += turnCount;
760
-
776
+
761
777
  // Add to total counts
762
778
  aggregated.total += turnCount;
763
779
  aggregated.totalcount += turnCount;
@@ -765,10 +781,12 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
765
781
  }
766
782
  }
767
783
  }
768
-
784
+
769
785
  // Also process the 'total' entry for validation but don't count it as a vehicle class
770
786
  if (results.vehicles.total) {
771
- for (const [direction, turns] of Object.entries(results.vehicles.total as any)) {
787
+ for (const [direction, turns] of Object.entries(
788
+ results.vehicles.total as any,
789
+ )) {
772
790
  if (typeof turns === "object" && turns !== null) {
773
791
  for (const [turnType, count] of Object.entries(turns)) {
774
792
  const turnCount = (count as number) || 0;
@@ -781,7 +799,8 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
781
799
  }
782
800
 
783
801
  // Set entry_vehicles same as total_vehicles for TMC
784
- aggregated.counts.entry_vehicles = aggregated.counts.total_vehicles || aggregated.total;
802
+ aggregated.counts.entry_vehicles =
803
+ aggregated.counts.total_vehicles || aggregated.total;
785
804
 
786
805
  return aggregated;
787
806
  }
@@ -805,7 +824,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
805
824
  if (results.vehicles) {
806
825
  for (const [vehicleClass, lanes] of Object.entries(results.vehicles)) {
807
826
  // Skip 'total' pseudo-class if present
808
- if (vehicleClass === 'total') {
827
+ if (vehicleClass === "total") {
809
828
  continue;
810
829
  }
811
830
 
@@ -814,7 +833,8 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
814
833
  }
815
834
 
816
835
  for (const [laneId, count] of Object.entries(lanes as any)) {
817
- const numericCount = typeof count === 'number' ? count : (parseInt(String(count)) || 0);
836
+ const numericCount =
837
+ typeof count === "number" ? count : parseInt(String(count)) || 0;
818
838
  aggregated.vehicles[vehicleClass][laneId] =
819
839
  (aggregated.vehicles[vehicleClass][laneId] || 0) + numericCount;
820
840
  }
@@ -921,7 +941,10 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
921
941
  * Used when results are already grouped by date in the UI
922
942
  * Uses UTC time for consistency with absoluteTime field
923
943
  */
924
- private formatStudyTimeLabel(startTime: Date, durationMinutes: number): string {
944
+ private formatStudyTimeLabel(
945
+ startTime: Date,
946
+ durationMinutes: number,
947
+ ): string {
925
948
  const endTime = new Date(startTime.getTime() + durationMinutes * 60 * 1000);
926
949
 
927
950
  const formatTime = (date: Date): string => {
@@ -4,58 +4,61 @@ import { IAuth } from "../../interfaces/auth/auth.interfaces";
4
4
  import KnexManager from "../../KnexConnection";
5
5
 
6
6
  export class AuthDAO implements IBaseDAO<IAuth> {
7
- private _knex: Knex<any, unknown[]> = KnexManager.getConnection();
8
-
9
- async create(item: IAuth): Promise<IAuth> {
10
- const [createdAuth] = await this._knex("auth").insert(item).returning("*");
11
- return createdAuth;
12
- }
13
-
14
- async getById(id: number): Promise<IAuth | null> {
15
- const auth = await this._knex("auth").where({ id }).first();
16
- return auth || null;
17
- }
18
-
19
- async getByUuid(uuid: string): Promise<IAuth | null> {
20
- // Auth table doesn't have uuid, so we'll return null or throw error
21
- return null;
22
- }
23
-
24
- async getByUserId(userId: number): Promise<IAuth | null> {
25
- const auth = await this._knex("auth").where({ userId }).first();
26
- return auth || null;
27
- }
28
-
29
- async getByEmailToken(emailToken: string): Promise<IAuth | null> {
30
- const auth = await this._knex("auth").where({ emailToken }).first();
31
- return auth || null;
32
- }
33
-
34
- async update(id: number, item: Partial<IAuth>): Promise<IAuth | null> {
35
- const [updatedAuth] = await this._knex("auth").where({ id }).update(item).returning("*");
36
- return updatedAuth || null;
37
- }
38
-
39
- async delete(id: number): Promise<boolean> {
40
- const result = await this._knex("auth").where({ id }).del();
41
- return result > 0;
42
- }
43
-
44
- async getAll(page: number, limit: number): Promise<IDataPaginator<IAuth>> {
45
- const offset = (page - 1) * limit;
46
-
47
- const [countResult] = await this._knex("auth").count("* as count");
48
- const totalCount = +countResult.count;
49
- const auths = await this._knex("auth").limit(limit).offset(offset);
50
-
51
- return {
52
- success: true,
53
- data: auths,
54
- page,
55
- limit,
56
- count: auths.length,
57
- totalCount,
58
- totalPages: Math.ceil(totalCount / limit),
59
- };
60
- }
61
- }
7
+ private _knex: Knex<any, unknown[]> = KnexManager.getConnection();
8
+
9
+ async create(item: IAuth): Promise<IAuth> {
10
+ const [createdAuth] = await this._knex("auth").insert(item).returning("*");
11
+ return createdAuth;
12
+ }
13
+
14
+ async getById(id: number): Promise<IAuth | null> {
15
+ const auth = await this._knex("auth").where({ id }).first();
16
+ return auth || null;
17
+ }
18
+
19
+ async getByUuid(uuid: string): Promise<IAuth | null> {
20
+ // Auth table doesn't have uuid, so we'll return null or throw error
21
+ return null;
22
+ }
23
+
24
+ async getByUserId(userId: number): Promise<IAuth | null> {
25
+ const auth = await this._knex("auth").where({ userId }).first();
26
+ return auth || null;
27
+ }
28
+
29
+ async getByEmailToken(emailToken: string): Promise<IAuth | null> {
30
+ const auth = await this._knex("auth").where({ emailToken }).first();
31
+ return auth || null;
32
+ }
33
+
34
+ async update(id: number, item: Partial<IAuth>): Promise<IAuth | null> {
35
+ const [updatedAuth] = await this._knex("auth")
36
+ .where({ id })
37
+ .update(item)
38
+ .returning("*");
39
+ return updatedAuth || null;
40
+ }
41
+
42
+ async delete(id: number): Promise<boolean> {
43
+ const result = await this._knex("auth").where({ id }).del();
44
+ return result > 0;
45
+ }
46
+
47
+ async getAll(page: number, limit: number): Promise<IDataPaginator<IAuth>> {
48
+ const offset = (page - 1) * limit;
49
+
50
+ const [countResult] = await this._knex("auth").count("* as count");
51
+ const totalCount = +countResult.count;
52
+ const auths = await this._knex("auth").limit(limit).offset(offset);
53
+
54
+ return {
55
+ success: true,
56
+ data: auths,
57
+ page,
58
+ limit,
59
+ count: auths.length,
60
+ totalCount,
61
+ totalPages: Math.ceil(totalCount / limit),
62
+ };
63
+ }
64
+ }
@@ -4,115 +4,118 @@ import { IBatch } from "../../interfaces/batch/batch.interfaces";
4
4
  import KnexManager from "../../KnexConnection";
5
5
 
6
6
  export class BatchDAO implements IBaseDAO<IBatch> {
7
- private _knex: Knex<any, unknown[]> = KnexManager.getConnection();
7
+ private _knex: Knex<any, unknown[]> = KnexManager.getConnection();
8
8
 
9
- async create(item: IBatch): Promise<IBatch> {
10
- const [createdBatch] = await this._knex("video_batch").insert(item).returning("*");
11
- return createdBatch;
12
- }
9
+ async create(item: IBatch): Promise<IBatch> {
10
+ const [createdBatch] = await this._knex("video_batch")
11
+ .insert(item)
12
+ .returning("*");
13
+ return createdBatch;
14
+ }
13
15
 
14
- async getById(id: number): Promise<IBatch | null> {
15
- const batch = await this._knex("video_batch as b")
16
- .innerJoin("folders as f", "b.folderId", "f.id")
17
- .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"))
18
- .where("b.id", id)
19
- .first();
20
- return batch || null;
21
- }
16
+ async getById(id: number): Promise<IBatch | null> {
17
+ const batch = await this._knex("video_batch as b")
18
+ .innerJoin("folders as f", "b.folderId", "f.id")
19
+ .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"))
20
+ .where("b.id", id)
21
+ .first();
22
+ return batch || null;
23
+ }
22
24
 
23
- async getByUuid(uuid: string): Promise<IBatch | null> {
24
- const batch = await this._knex("video_batch as b")
25
- .innerJoin("folders as f", "b.folderId", "f.id")
26
- .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"))
27
- .where("b.uuid", uuid)
28
- .first();
29
- return batch || null;
30
- }
25
+ async getByUuid(uuid: string): Promise<IBatch | null> {
26
+ const batch = await this._knex("video_batch as b")
27
+ .innerJoin("folders as f", "b.folderId", "f.id")
28
+ .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"))
29
+ .where("b.uuid", uuid)
30
+ .first();
31
+ return batch || null;
32
+ }
31
33
 
32
- async update(id: number, item: Partial<IBatch>): Promise<IBatch | null> {
33
- const [updatedBatch] = await this._knex("video_batch").where({ id }).update(item).returning("*");
34
- return updatedBatch || null;
35
- }
34
+ async update(id: number, item: Partial<IBatch>): Promise<IBatch | null> {
35
+ const [updatedBatch] = await this._knex("video_batch")
36
+ .where({ id })
37
+ .update(item)
38
+ .returning("*");
39
+ return updatedBatch || null;
40
+ }
36
41
 
37
- async delete(id: number): Promise<boolean> {
38
- const result = await this._knex("video_batch").where({ id }).del();
39
- return result > 0;
40
- }
42
+ async delete(id: number): Promise<boolean> {
43
+ const result = await this._knex("video_batch").where({ id }).del();
44
+ return result > 0;
45
+ }
41
46
 
42
- async getAll(page: number, limit: number, folderId?: number | null): Promise<IDataPaginator<IBatch>> {
43
- const offset = (page - 1) * limit;
47
+ async getAll(
48
+ page: number,
49
+ limit: number,
50
+ folderId?: number | null,
51
+ ): Promise<IDataPaginator<IBatch>> {
52
+ const offset = (page - 1) * limit;
44
53
 
45
- const query = this._knex("video_batch as b")
46
- .innerJoin("folders as f", "b.folderId", "f.id")
47
- .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"));
48
- if (folderId !== undefined && folderId !== null) {
49
- query.where("b.folderId", folderId);
50
- }
54
+ const query = this._knex("video_batch as b")
55
+ .innerJoin("folders as f", "b.folderId", "f.id")
56
+ .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"));
57
+ if (folderId !== undefined && folderId !== null) {
58
+ query.where("b.folderId", folderId);
59
+ }
51
60
 
52
- const [countResult] = await query.clone().clearSelect().count("* as count");
53
- const totalCount = +countResult.count;
54
- const batches = await query.clone().limit(limit).offset(offset);
61
+ const [countResult] = await query.clone().clearSelect().count("* as count");
62
+ const totalCount = +countResult.count;
63
+ const batches = await query.clone().limit(limit).offset(offset);
55
64
 
56
- return {
57
- success: true,
58
- data: batches,
59
- page,
60
- limit,
61
- count: batches.length,
62
- totalCount,
63
- totalPages: Math.ceil(totalCount / limit),
64
- };
65
- }
65
+ return {
66
+ success: true,
67
+ data: batches,
68
+ page,
69
+ limit,
70
+ count: batches.length,
71
+ totalCount,
72
+ totalPages: Math.ceil(totalCount / limit),
73
+ };
74
+ }
66
75
 
67
- /**
68
- * Get batches by folder ID
69
- */
70
- async getByFolderId(folderId: number): Promise<IBatch[]> {
71
- return this._knex("video_batch as b")
72
- .innerJoin("folders as f", "b.folderId", "f.id")
73
- .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"))
74
- .where("b.folderId", folderId)
75
- .orderBy("b.created_at", "desc");
76
- }
76
+ /**
77
+ * Get batches by folder ID
78
+ */
79
+ async getByFolderId(folderId: number): Promise<IBatch[]> {
80
+ return this._knex("video_batch as b")
81
+ .innerJoin("folders as f", "b.folderId", "f.id")
82
+ .select("b.*", this._knex.raw("to_jsonb(f.*) as folder"))
83
+ .where("b.folderId", folderId)
84
+ .orderBy("b.created_at", "desc");
85
+ }
77
86
 
78
- /**
79
- * Update batch progress
80
- */
81
- async updateProgress(
82
- id: number,
83
- completedVideos: number,
84
- failedVideos: number
85
- ): Promise<void> {
86
- await this._knex("video_batch")
87
- .where({ id })
88
- .update({
89
- completedVideos,
90
- failedVideos,
91
- updated_at: this._knex.fn.now()
92
- });
93
- }
87
+ /**
88
+ * Update batch progress
89
+ */
90
+ async updateProgress(
91
+ id: number,
92
+ completedVideos: number,
93
+ failedVideos: number,
94
+ ): Promise<void> {
95
+ await this._knex("video_batch").where({ id }).update({
96
+ completedVideos,
97
+ failedVideos,
98
+ updated_at: this._knex.fn.now(),
99
+ });
100
+ }
94
101
 
95
- /**
96
- * Mark batch as completed
97
- */
98
- async markCompleted(id: number): Promise<void> {
99
- await this._knex("video_batch")
100
- .where({ id })
101
- .update({
102
- status: "COMPLETED",
103
- updated_at: this._knex.fn.now()
104
- });
105
- }
102
+ /**
103
+ * Mark batch as completed
104
+ */
105
+ async markCompleted(id: number): Promise<void> {
106
+ await this._knex("video_batch").where({ id }).update({
107
+ status: "COMPLETED",
108
+ updated_at: this._knex.fn.now(),
109
+ });
110
+ }
106
111
 
107
- /**
108
- * Mark batch as failed
109
- */
110
- async markFailed(id: number): Promise<void> {
111
- await this._knex("video_batch")
112
- .where({ id })
113
- .update({
114
- status: "FAILED",
115
- updated_at: this._knex.fn.now()
116
- });
117
- }
112
+ /**
113
+ * Mark batch as failed
114
+ */
115
+ async markFailed(id: number): Promise<void> {
116
+ await this._knex("video_batch").where({ id }).update({
117
+ status: "FAILED",
118
+ updated_at: this._knex.fn.now(),
119
+ });
120
+ }
118
121
  }