diff --git a/server/entity/Blacklist.ts b/server/entity/Blacklist.ts index a6bac9d9e..64c44e50d 100644 --- a/server/entity/Blacklist.ts +++ b/server/entity/Blacklist.ts @@ -3,10 +3,10 @@ import dataSource from '@server/datasource'; import Media from '@server/entity/Media'; import { User } from '@server/entity/User'; import type { BlacklistItem } from '@server/interfaces/api/blacklistInterfaces'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; import type { EntityManager } from 'typeorm'; import { Column, - CreateDateColumn, Entity, Index, JoinColumn, @@ -47,7 +47,7 @@ export class Blacklist implements BlacklistItem { @Column({ nullable: true, type: 'varchar' }) public blacklistedTags?: string; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; constructor(init?: Partial) { diff --git a/server/entity/DiscoverSlider.ts b/server/entity/DiscoverSlider.ts index 261419a07..0de0aef81 100644 --- a/server/entity/DiscoverSlider.ts +++ b/server/entity/DiscoverSlider.ts @@ -2,13 +2,8 @@ import type { DiscoverSliderType } from '@server/constants/discover'; import { defaultSliders } from '@server/constants/discover'; import { getRepository } from '@server/datasource'; import logger from '@server/logger'; -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() class DiscoverSlider { @@ -55,10 +50,14 @@ class DiscoverSlider { @Column({ nullable: true }) public data?: string; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; constructor(init?: Partial) { diff --git a/server/entity/Issue.ts b/server/entity/Issue.ts index fae96967d..8ff88e3b7 100644 --- a/server/entity/Issue.ts +++ b/server/entity/Issue.ts @@ -1,13 +1,13 @@ import type { IssueType } from '@server/constants/issue'; import { IssueStatus } from '@server/constants/issue'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; import { + AfterLoad, Column, - CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, - UpdateDateColumn, } from 'typeorm'; import IssueComment from './IssueComment'; import Media from './Media'; @@ -55,12 +55,21 @@ class Issue { }) public comments: IssueComment[]; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; + @AfterLoad() + sortComments() { + this.comments?.sort((a, b) => a.id - b.id); + } + constructor(init?: Partial) { Object.assign(this, init); } diff --git a/server/entity/IssueComment.ts b/server/entity/IssueComment.ts index e45216392..c36b3746c 100644 --- a/server/entity/IssueComment.ts +++ b/server/entity/IssueComment.ts @@ -1,11 +1,5 @@ -import { - Column, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import Issue from './Issue'; import { User } from './User'; @@ -28,10 +22,14 @@ class IssueComment { @Column({ type: 'text' }) public message: string; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; constructor(init?: Partial) { diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 33155a1ff..38726f701 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -15,13 +15,11 @@ import { getHostname } from '@server/utils/getHostname'; import { AfterLoad, Column, - CreateDateColumn, Entity, Index, OneToMany, OneToOne, PrimaryGeneratedColumn, - UpdateDateColumn, } from 'typeorm'; import Issue from './Issue'; import { MediaRequest } from './MediaRequest'; @@ -128,10 +126,14 @@ class Media { @OneToOne(() => Blacklist, (blacklist) => blacklist.media) public blacklist: Promise; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; /** diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index cd103ef4f..e5dabc241 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -13,18 +13,17 @@ import notificationManager, { Notification } from '@server/lib/notifications'; import { Permission } from '@server/lib/permissions'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; import { truncate } from 'lodash'; import { AfterInsert, AfterUpdate, Column, - CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationCount, - UpdateDateColumn, } from 'typeorm'; import Media from './Media'; import SeasonRequest from './SeasonRequest'; @@ -535,10 +534,14 @@ export class MediaRequest { }) public modifiedBy?: User; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; @Column({ type: 'varchar' }) diff --git a/server/entity/OverrideRule.ts b/server/entity/OverrideRule.ts index bf1373438..aab72006f 100644 --- a/server/entity/OverrideRule.ts +++ b/server/entity/OverrideRule.ts @@ -1,10 +1,5 @@ -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() class OverrideRule { @@ -38,10 +33,14 @@ class OverrideRule { @Column({ nullable: true }) public tags?: string; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; constructor(init?: Partial) { diff --git a/server/entity/Season.ts b/server/entity/Season.ts index d488a5c19..7d38581ed 100644 --- a/server/entity/Season.ts +++ b/server/entity/Season.ts @@ -1,12 +1,6 @@ import { MediaStatus } from '@server/constants/media'; -import { - Column, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import Media from './Media'; @Entity() @@ -28,10 +22,14 @@ class Season { }) public media: Promise; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; constructor(init?: Partial) { diff --git a/server/entity/SeasonRequest.ts b/server/entity/SeasonRequest.ts index f9eeef501..9262877b8 100644 --- a/server/entity/SeasonRequest.ts +++ b/server/entity/SeasonRequest.ts @@ -1,12 +1,6 @@ import { MediaRequestStatus } from '@server/constants/media'; -import { - Column, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { MediaRequest } from './MediaRequest'; @Entity() @@ -25,10 +19,14 @@ class SeasonRequest { }) public request: MediaRequest; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; constructor(init?: Partial) { diff --git a/server/entity/User.ts b/server/entity/User.ts index 5f51af710..8a96f396e 100644 --- a/server/entity/User.ts +++ b/server/entity/User.ts @@ -9,6 +9,7 @@ import { hasPermission, Permission } from '@server/lib/permissions'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { AfterDate } from '@server/utils/dateHelpers'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; import bcrypt from 'bcrypt'; import { randomUUID } from 'crypto'; import path from 'path'; @@ -16,14 +17,12 @@ import { default as generatePassword } from 'secure-random-password'; import { AfterLoad, Column, - CreateDateColumn, Entity, Not, OneToMany, OneToOne, PrimaryGeneratedColumn, RelationCount, - UpdateDateColumn, } from 'typeorm'; import Issue from './Issue'; import { MediaRequest } from './MediaRequest'; @@ -138,10 +137,14 @@ export class User { @OneToMany(() => Issue, (issue) => issue.createdBy, { cascade: true }) public createdIssues: Issue[]; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; public warnings: string[] = []; diff --git a/server/entity/UserPushSubscription.ts b/server/entity/UserPushSubscription.ts index f05dd0f2b..b3c4d5d2b 100644 --- a/server/entity/UserPushSubscription.ts +++ b/server/entity/UserPushSubscription.ts @@ -1,10 +1,5 @@ -import { - Column, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, -} from 'typeorm'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { User } from './User'; @Entity() @@ -30,7 +25,11 @@ export class UserPushSubscription { @Column({ nullable: true }) public userAgent: string; - @CreateDateColumn({ nullable: true }) + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + nullable: true, + }) public createdAt: Date; constructor(init?: Partial) { diff --git a/server/entity/Watchlist.ts b/server/entity/Watchlist.ts index df820120e..10c26246a 100644 --- a/server/entity/Watchlist.ts +++ b/server/entity/Watchlist.ts @@ -5,15 +5,14 @@ import Media from '@server/entity/Media'; import { User } from '@server/entity/User'; import type { WatchlistItem } from '@server/interfaces/api/discoverInterfaces'; import logger from '@server/logger'; +import { DbAwareColumn } from '@server/utils/DbColumnHelper'; import { Column, - CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, Unique, - UpdateDateColumn, } from 'typeorm'; import type { ZodNumber, ZodOptional, ZodString } from 'zod'; @@ -56,10 +55,14 @@ export class Watchlist implements WatchlistItem { }) public media: Media; - @CreateDateColumn() + @DbAwareColumn({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) public createdAt: Date; - @UpdateDateColumn() + @DbAwareColumn({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP', + }) public updatedAt: Date; constructor(init?: Partial) { diff --git a/server/migration/postgres/1746811308203-FixIssueTimestamps.ts b/server/migration/postgres/1746811308203-FixIssueTimestamps.ts new file mode 100644 index 000000000..ac0bae3a8 --- /dev/null +++ b/server/migration/postgres/1746811308203-FixIssueTimestamps.ts @@ -0,0 +1,231 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixIssueTimestamps1746811308203 implements MigrationInterface { + name = 'FixIssueTimestamps1746811308203'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "watchlist" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "watchlist" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "override_rule" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "override_rule" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season_request" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season_request" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media_request" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media_request" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "user_push_subscription" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "user" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "user" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "blacklist" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue_comment" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue_comment" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "discover_slider" + ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "discover_slider" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE + USING "updatedAt" AT TIME ZONE 'UTC' + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "discover_slider" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "discover_slider" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue_comment" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue_comment" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "issue" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "blacklist" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "user" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "user" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "user_push_subscription" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media_request" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "media_request" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season_request" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "season_request" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "override_rule" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "override_rule" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "watchlist" + ALTER COLUMN "updatedAt" TYPE TIMESTAMP + USING "updatedAt" AT TIME ZONE 'UTC' + `); + await queryRunner.query(` + ALTER TABLE "watchlist" + ALTER COLUMN "createdAt" TYPE TIMESTAMP + USING "createdAt" AT TIME ZONE 'UTC' + `); + } +}