@trafficgroup/knex-rel 0.1.9 → 0.1.11-rc0

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 (74) hide show
  1. package/.claude/settings.local.json +5 -2
  2. package/CLAUDE.md +2 -11
  3. package/dist/constants/video.constants.d.ts +12 -0
  4. package/dist/constants/video.constants.js +22 -0
  5. package/dist/constants/video.constants.js.map +1 -0
  6. package/dist/dao/chat/chat.dao.d.ts +4 -1
  7. package/dist/dao/chat/chat.dao.js +21 -12
  8. package/dist/dao/chat/chat.dao.js.map +1 -1
  9. package/dist/dao/user-push-notification-token/user-push-notification-token.dao.js.map +1 -1
  10. package/dist/dao/video/video.dao.d.ts +2 -1
  11. package/dist/dao/video/video.dao.js +18 -2
  12. package/dist/dao/video/video.dao.js.map +1 -1
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.js +6 -1
  15. package/dist/index.js.map +1 -1
  16. package/migrations/20250717160737_migration.ts +1 -1
  17. package/migrations/20250717160908_migration.ts +2 -5
  18. package/migrations/20250717161310_migration.ts +1 -1
  19. package/migrations/20250717161406_migration.ts +3 -3
  20. package/migrations/20250717162431_migration.ts +1 -1
  21. package/migrations/20250717173228_migration.ts +2 -2
  22. package/migrations/20250717204731_migration.ts +1 -1
  23. package/migrations/20250722210109_migration.ts +4 -8
  24. package/migrations/20250722211019_migration.ts +1 -1
  25. package/migrations/20250723153852_migration.ts +10 -13
  26. package/migrations/20250723162257_migration.ts +7 -4
  27. package/migrations/20250723171109_migration.ts +7 -4
  28. package/migrations/20250723205331_migration.ts +9 -6
  29. package/migrations/20250724191345_migration.ts +11 -8
  30. package/migrations/20250730180932_migration.ts +13 -14
  31. package/migrations/20250730213625_migration.ts +11 -8
  32. package/migrations/20250804124509_migration.ts +21 -26
  33. package/migrations/20250804132053_migration.ts +8 -5
  34. package/migrations/20250804164518_migration.ts +7 -7
  35. package/migrations/20250823223016_migration.ts +21 -32
  36. package/migrations/20250910015452_migration.ts +6 -18
  37. package/migrations/20250911000000_migration.ts +4 -18
  38. package/migrations/20250917144153_migration.ts +7 -14
  39. package/migrations/20250930200521_migration.ts +4 -8
  40. package/migrations/20251010143500_migration.ts +6 -27
  41. package/migrations/20251020225758_migration.ts +15 -51
  42. package/migrations/20251112120000_migration.ts +2 -10
  43. package/migrations/20251112120200_migration.ts +7 -19
  44. package/migrations/20251112120300_migration.ts +2 -7
  45. package/package.json +1 -1
  46. package/src/constants/video.constants.ts +19 -0
  47. package/src/d.types.ts +14 -18
  48. package/src/dao/VideoMinuteResultDAO.ts +49 -72
  49. package/src/dao/auth/auth.dao.ts +55 -58
  50. package/src/dao/batch/batch.dao.ts +98 -101
  51. package/src/dao/camera/camera.dao.ts +121 -124
  52. package/src/dao/chat/chat.dao.ts +60 -42
  53. package/src/dao/folder/folder.dao.ts +56 -65
  54. package/src/dao/location/location.dao.ts +87 -109
  55. package/src/dao/message/message.dao.ts +32 -32
  56. package/src/dao/report-configuration/report-configuration.dao.ts +342 -370
  57. package/src/dao/study/study.dao.ts +63 -88
  58. package/src/dao/user/user.dao.ts +50 -52
  59. package/src/dao/user-push-notification-token/user-push-notification-token.dao.ts +49 -83
  60. package/src/dao/video/video.dao.ts +339 -357
  61. package/src/entities/BaseEntity.ts +1 -1
  62. package/src/index.ts +23 -27
  63. package/src/interfaces/auth/auth.interfaces.ts +10 -10
  64. package/src/interfaces/batch/batch.interfaces.ts +1 -1
  65. package/src/interfaces/camera/camera.interfaces.ts +9 -9
  66. package/src/interfaces/chat/chat.interfaces.ts +4 -4
  67. package/src/interfaces/folder/folder.interfaces.ts +2 -2
  68. package/src/interfaces/location/location.interfaces.ts +7 -7
  69. package/src/interfaces/message/message.interfaces.ts +3 -3
  70. package/src/interfaces/report-configuration/report-configuration.interfaces.ts +16 -16
  71. package/src/interfaces/study/study.interfaces.ts +3 -3
  72. package/src/interfaces/user/user.interfaces.ts +9 -9
  73. package/src/interfaces/user-push-notification-token/user-push-notification-token.interfaces.ts +9 -9
  74. package/src/interfaces/video/video.interfaces.ts +34 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trafficgroup/knex-rel",
3
- "version": "0.1.9",
3
+ "version": "0.1.11-rc0",
4
4
  "description": "Knex Module",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Video sorting constants
3
+ * Shared between DAO and API controller to ensure validation consistency
4
+ */
5
+
6
+ export const VALID_VIDEO_SORT_FIELDS = ['created_at', 'name', 'status'] as const;
7
+ export type VideoSortField = typeof VALID_VIDEO_SORT_FIELDS[number];
8
+
9
+ export const VALID_SORT_ORDERS = ['ASC', 'DESC'] as const;
10
+ export type SortOrder = typeof VALID_SORT_ORDERS[number];
11
+
12
+ /**
13
+ * Maps API sort field names to database column names with table aliases
14
+ */
15
+ export const VIDEO_SORT_COLUMN_MAP: Record<VideoSortField, string> = {
16
+ 'created_at': 'v.created_at',
17
+ 'name': 'v.name',
18
+ 'status': 'v.status'
19
+ };
package/src/d.types.ts CHANGED
@@ -1,22 +1,18 @@
1
1
  export interface IDataPaginator<T> {
2
- success: boolean;
3
- data: T[];
4
- page: number;
5
- limit: number;
6
- count: number;
7
- totalCount: number;
8
- totalPages: number;
2
+ success: boolean;
3
+ data: T[];
4
+ page: number;
5
+ limit: number;
6
+ count: number;
7
+ totalCount: number;
8
+ totalPages: number;
9
9
  }
10
10
 
11
11
  export interface IBaseDAO<T> {
12
- create(item: T): Promise<T>;
13
- getById(id: number): Promise<T | null>;
14
- getByUuid(uuid: string): Promise<T | null>;
15
- getAll(
16
- page: number,
17
- limit: number,
18
- entityId?: any | null,
19
- ): Promise<IDataPaginator<T>>;
20
- update(id: number, item: T): Promise<T | null>;
21
- delete(id: number): Promise<boolean>;
22
- }
12
+ create(item: T): Promise<T>;
13
+ getById(id: number): Promise<T | null>;
14
+ getByUuid(uuid: string): Promise<T | null>;
15
+ getAll(page: number, limit: number, entityId?: any | null): Promise<IDataPaginator<T>>;
16
+ update(id: number, item: T): Promise<T | null>;
17
+ delete(id: number): Promise<boolean>;
18
+ }
@@ -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,41 +423,37 @@ 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", [
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
- ),
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')
435
431
  )
436
- .where("video_id", video.id);
432
+ .where('video_id', video.id);
437
433
 
438
434
  if (startMinute !== undefined) {
439
- groupingQuery.where("minute_number", ">=", startMinute);
435
+ groupingQuery.where('minute_number', '>=', startMinute);
440
436
  }
441
437
 
442
438
  if (endMinute !== undefined) {
443
- groupingQuery.where("minute_number", "<=", endMinute);
439
+ groupingQuery.where('minute_number', '<=', endMinute);
444
440
  }
445
441
 
446
442
  const rows = await groupingQuery
447
- .groupBy("group_index")
448
- .orderBy("group_index");
443
+ .groupBy('group_index')
444
+ .orderBy('group_index');
449
445
 
450
446
  // Aggregate the results in TypeScript based on video type
451
447
  const aggregatedGroups: IGroupedResult[] = rows.map((row: any) => {
452
- if (!row || typeof row !== "object") {
453
- throw new Error("Invalid row data received from database query");
448
+ if (!row || typeof row !== 'object') {
449
+ throw new Error('Invalid row data received from database query');
454
450
  }
455
451
 
456
452
  const allResults = Array.isArray(row.all_results) ? row.all_results : [];
457
-
453
+
458
454
  // Determine video type based on multiple factors
459
- let studyType = video.videoType || "ATR"; // Default fallback to ATR
460
-
455
+ let studyType = video.videoType || 'ATR'; // Default fallback to ATR
456
+
461
457
  // Check if minute data has study_type field (ATR usually does)
462
458
  if (allResults.length > 0 && allResults[0].study_type) {
463
459
  studyType = allResults[0].study_type;
@@ -469,24 +465,20 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
469
465
  const vehicleKeys = Object.keys(sampleResult.vehicles);
470
466
  if (vehicleKeys.length > 0) {
471
467
  const firstVehicleType = sampleResult.vehicles[vehicleKeys[0]];
472
- if (firstVehicleType && typeof firstVehicleType === "object") {
468
+ if (firstVehicleType && typeof firstVehicleType === 'object') {
473
469
  const directions = Object.keys(firstVehicleType);
474
- if (
475
- directions.includes("NORTH") ||
476
- directions.includes("SOUTH") ||
477
- directions.includes("EAST") ||
478
- directions.includes("WEST")
479
- ) {
480
- studyType = "TMC";
470
+ if (directions.includes('NORTH') || directions.includes('SOUTH') ||
471
+ directions.includes('EAST') || directions.includes('WEST')) {
472
+ studyType = 'TMC';
481
473
  }
482
474
  }
483
475
  }
484
476
  }
485
477
  }
486
-
478
+
487
479
  // Aggregate based on determined video type
488
480
  let aggregatedResult;
489
- if (studyType === "TMC") {
481
+ if (studyType === 'TMC') {
490
482
  aggregatedResult = this.aggregateTMCResults(allResults);
491
483
  } else {
492
484
  aggregatedResult = this.aggregateATRResults(allResults);
@@ -577,8 +569,8 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
577
569
  "v.uuid as videoUuid",
578
570
  "v.recordingStartedAt",
579
571
  this.knex.raw(
580
- '"v"."recordingStartedAt" + (vmr.minute_number || \' minutes\')::INTERVAL as "absoluteTime"',
581
- ),
572
+ '"v"."recordingStartedAt" + (vmr.minute_number || \' minutes\')::INTERVAL as "absoluteTime"'
573
+ )
582
574
  )
583
575
  .whereIn("v.id", videoIds)
584
576
  .orderBy("absoluteTime", "asc");
@@ -616,7 +608,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
616
608
  for (const minute of minuteResults) {
617
609
  const absoluteTime = new Date(minute.absoluteTime);
618
610
  const minutesSinceEarliest = Math.floor(
619
- (absoluteTime.getTime() - earliestTime.getTime()) / (1000 * 60),
611
+ (absoluteTime.getTime() - earliestTime.getTime()) / (1000 * 60)
620
612
  );
621
613
  const bucketIndex = Math.floor(minutesSinceEarliest / groupingMinutes);
622
614
 
@@ -624,7 +616,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
624
616
  // Calculate bucket start time
625
617
  const bucketStartMinutes = bucketIndex * groupingMinutes;
626
618
  const bucketStartTime = new Date(
627
- earliestTime.getTime() + bucketStartMinutes * 60 * 1000,
619
+ earliestTime.getTime() + bucketStartMinutes * 60 * 1000
628
620
  );
629
621
 
630
622
  buckets.set(bucketIndex, {
@@ -641,14 +633,12 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
641
633
  }
642
634
 
643
635
  // Step 5: Aggregate using existing methods based on study type
644
- const aggregatedGroups: IStudyTimeGroupResult[] = Array.from(
645
- buckets.values(),
646
- )
636
+ const aggregatedGroups: IStudyTimeGroupResult[] = Array.from(buckets.values())
647
637
  .sort((a, b) => a.groupIndex - b.groupIndex)
648
638
  .map((bucket) => {
649
639
  let aggregatedResult: ITMCResult | IATRResult;
650
640
 
651
- if (study.type === "TMC") {
641
+ if (study.type === 'TMC') {
652
642
  aggregatedResult = this.aggregateTMCResults(bucket.results);
653
643
  } else {
654
644
  aggregatedResult = this.aggregateATRResults(bucket.results);
@@ -659,10 +649,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
659
649
  groupIndex: bucket.groupIndex,
660
650
  startMinute: bucket.groupIndex * groupingMinutes,
661
651
  endMinute: (bucket.groupIndex + 1) * groupingMinutes - 1,
662
- label: this.formatStudyTimeLabel(
663
- bucket.absoluteTime,
664
- groupingMinutes,
665
- ),
652
+ label: this.formatStudyTimeLabel(bucket.absoluteTime, groupingMinutes),
666
653
  results: aggregatedResult,
667
654
  minuteCount: bucket.results.length,
668
655
  videoCount: bucket.videoUuids.size,
@@ -725,27 +712,25 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
725
712
  vehicles: {},
726
713
  counts: {
727
714
  total_vehicles: 0,
728
- entry_vehicles: 0,
715
+ entry_vehicles: 0
729
716
  },
730
717
  total: 0,
731
718
  totalcount: 0,
732
719
  detected_classes: {},
733
- study_type: "TMC",
720
+ study_type: "TMC"
734
721
  };
735
722
 
736
723
  for (const minute of minutes) {
737
724
  const results = minute; // minute is already the results object from array_agg
738
725
 
739
726
  // Aggregate vehicle movements by class and direction
740
- if (results.vehicles && typeof results.vehicles === "object") {
741
- for (const [vehicleClass, directions] of Object.entries(
742
- results.vehicles,
743
- )) {
727
+ if (results.vehicles && typeof results.vehicles === 'object') {
728
+ for (const [vehicleClass, directions] of Object.entries(results.vehicles)) {
744
729
  // Skip the 'total' pseudo vehicle class - validate it's actually aggregate data
745
- if (vehicleClass === "total" && typeof directions === "number") {
730
+ if (vehicleClass === 'total' && typeof directions === 'number') {
746
731
  continue; // This is aggregate total data, not a vehicle class
747
732
  }
748
-
733
+
749
734
  if (!aggregated.vehicles[vehicleClass]) {
750
735
  aggregated.vehicles[vehicleClass] = {};
751
736
  }
@@ -768,12 +753,11 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
768
753
  for (const [turnType, count] of Object.entries(turns)) {
769
754
  const turnCount = (count as number) || 0;
770
755
  aggregated.vehicles[vehicleClass][direction][turnType] =
771
- (aggregated.vehicles[vehicleClass][direction][turnType] ||
772
- 0) + turnCount;
773
-
756
+ (aggregated.vehicles[vehicleClass][direction][turnType] || 0) + turnCount;
757
+
774
758
  // Add to detected_classes count for this vehicle type
775
759
  aggregated.detected_classes[vehicleClass] += turnCount;
776
-
760
+
777
761
  // Add to total counts
778
762
  aggregated.total += turnCount;
779
763
  aggregated.totalcount += turnCount;
@@ -781,12 +765,10 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
781
765
  }
782
766
  }
783
767
  }
784
-
768
+
785
769
  // Also process the 'total' entry for validation but don't count it as a vehicle class
786
770
  if (results.vehicles.total) {
787
- for (const [direction, turns] of Object.entries(
788
- results.vehicles.total as any,
789
- )) {
771
+ for (const [direction, turns] of Object.entries(results.vehicles.total as any)) {
790
772
  if (typeof turns === "object" && turns !== null) {
791
773
  for (const [turnType, count] of Object.entries(turns)) {
792
774
  const turnCount = (count as number) || 0;
@@ -799,8 +781,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
799
781
  }
800
782
 
801
783
  // Set entry_vehicles same as total_vehicles for TMC
802
- aggregated.counts.entry_vehicles =
803
- aggregated.counts.total_vehicles || aggregated.total;
784
+ aggregated.counts.entry_vehicles = aggregated.counts.total_vehicles || aggregated.total;
804
785
 
805
786
  return aggregated;
806
787
  }
@@ -824,7 +805,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
824
805
  if (results.vehicles) {
825
806
  for (const [vehicleClass, lanes] of Object.entries(results.vehicles)) {
826
807
  // Skip 'total' pseudo-class if present
827
- if (vehicleClass === "total") {
808
+ if (vehicleClass === 'total') {
828
809
  continue;
829
810
  }
830
811
 
@@ -833,8 +814,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
833
814
  }
834
815
 
835
816
  for (const [laneId, count] of Object.entries(lanes as any)) {
836
- const numericCount =
837
- typeof count === "number" ? count : parseInt(String(count)) || 0;
817
+ const numericCount = typeof count === 'number' ? count : (parseInt(String(count)) || 0);
838
818
  aggregated.vehicles[vehicleClass][laneId] =
839
819
  (aggregated.vehicles[vehicleClass][laneId] || 0) + numericCount;
840
820
  }
@@ -941,10 +921,7 @@ export class VideoMinuteResultDAO implements IBaseDAO<IVideoMinuteResult> {
941
921
  * Used when results are already grouped by date in the UI
942
922
  * Uses UTC time for consistency with absoluteTime field
943
923
  */
944
- private formatStudyTimeLabel(
945
- startTime: Date,
946
- durationMinutes: number,
947
- ): string {
924
+ private formatStudyTimeLabel(startTime: Date, durationMinutes: number): string {
948
925
  const endTime = new Date(startTime.getTime() + durationMinutes * 60 * 1000);
949
926
 
950
927
  const formatTime = (date: Date): string => {
@@ -4,61 +4,58 @@ 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")
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
- }
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
+ }