diff --git a/.eslintrc.json b/.eslintrc.json index 9545fe142..17afd1b83 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -118,6 +118,8 @@ "@typescript-eslint/consistent-type-exports": "off", "@typescript-eslint/key-spacing": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/ban-types": [ "error", { diff --git a/apps/peertube-runner/src/shared/http.ts b/apps/peertube-runner/src/shared/http.ts index 4d86c2ef7..5c029ab21 100644 --- a/apps/peertube-runner/src/shared/http.ts +++ b/apps/peertube-runner/src/shared/http.ts @@ -61,7 +61,7 @@ export function downloadFile (options: { // --------------------------------------------------------------------------- function getRequest (url: string) { - if (url.startsWith('https://')) return https.request + if (url.startsWith('https://')) return https.request.bind(https) - return http.request + return http.request.bind(http) } diff --git a/client/.eslintrc.json b/client/.eslintrc.json index e292617dd..271363cb7 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -160,7 +160,11 @@ "error", "consistent-as-needed" ], - "no-constant-binary-expression": "error" + "no-constant-binary-expression": "error", + "@typescript-eslint/unbound-method": [ + "error", + { "ignoreStatic": true } + ] } }, { diff --git a/client/.stylelintrc.json b/client/.stylelintrc.json index 9f8afa29c..fc5a5307b 100644 --- a/client/.stylelintrc.json +++ b/client/.stylelintrc.json @@ -2,7 +2,6 @@ "extends": "stylelint-config-sass-guidelines", "rules": { "scss/at-import-no-partial-leading-underscore": null, - "color-hex-case": null, "color-hex-length": null, "selector-pseudo-element-no-unknown": [ true, @@ -19,10 +18,10 @@ "selector-max-compound-selectors": 9, "selector-no-qualifying-type": null, "scss/at-extend-no-missing-placeholder": null, - "number-leading-zero": null, "rule-empty-line-before": null, "selector-max-id": null, "scss/at-function-pattern": null, + "scss/load-no-partial-leading-underscore": null, "property-no-vendor-prefix": [ true, { diff --git a/client/e2e/src/suites-local/videos-list.e2e-spec.ts b/client/e2e/src/suites-local/videos-list.e2e-spec.ts index 8264308f2..9f0623af5 100644 --- a/client/e2e/src/suites-local/videos-list.e2e-spec.ts +++ b/client/e2e/src/suites-local/videos-list.e2e-spec.ts @@ -51,15 +51,15 @@ describe('Videos list', () => { async function checkCommonVideoListPages (policy: NSFWPolicy) { const promisesWithFilters = [ - videoListPage.goOnRootAccount, - videoListPage.goOnLocal, - videoListPage.goOnRecentlyAdded, - videoListPage.goOnTrending, - videoListPage.goOnRootChannel + videoListPage.goOnRootAccount.bind(videoListPage), + videoListPage.goOnLocal.bind(videoListPage), + videoListPage.goOnRecentlyAdded.bind(videoListPage), + videoListPage.goOnTrending.bind(videoListPage), + videoListPage.goOnRootChannel.bind(videoListPage) ] for (const p of promisesWithFilters) { - await p.call(videoListPage) + await p() const filter = await videoListPage.getNSFWFilter() const filterText = await filter.getText() @@ -69,11 +69,11 @@ describe('Videos list', () => { } const promisesWithoutFilters = [ - videoListPage.goOnRootAccountChannels, - videoListPage.goOnHomepage + videoListPage.goOnRootAccountChannels.bind(videoListPage), + videoListPage.goOnHomepage.bind(videoListPage) ] for (const p of promisesWithoutFilters) { - await p.call(videoListPage) + await p() await checkNormalVideo() await checkNSFWVideo(policy) diff --git a/client/package.json b/client/package.json index 517020690..68c604e82 100644 --- a/client/package.json +++ b/client/package.json @@ -109,6 +109,7 @@ "linkifyjs": "^4.0.2", "lodash-es": "^4.17.4", "markdown-it": "14.0.0", + "markdown-it-emoji": "^3.0.0", "mini-css-extract-plugin": "^2.2.0", "ngx-uploadx": "^6.1.0", "path-browserify": "^1.0.0", diff --git a/client/src/app/+admin/system/jobs/job.service.ts b/client/src/app/+admin/system/jobs/job.service.ts index eae1dea7d..740adf742 100644 --- a/client/src/app/+admin/system/jobs/job.service.ts +++ b/client/src/app/+admin/system/jobs/job.service.ts @@ -35,8 +35,8 @@ export class JobService { return this.authHttp.get>(JobService.BASE_JOB_URL + `/${jobState || ''}`, { params }) .pipe( map(res => this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ], 'precise')), - map(res => this.restExtractor.applyToResultListData(res, this.prettyPrintData)), - map(res => this.restExtractor.applyToResultListData(res, this.buildUniqId)), + map(res => this.restExtractor.applyToResultListData(res, this.prettyPrintData.bind(this))), + map(res => this.restExtractor.applyToResultListData(res, this.buildUniqId.bind(this))), catchError(err => this.restExtractor.handleError(err)) ) } diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts index b8165e760..186649e60 100644 --- a/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts @@ -104,22 +104,22 @@ export class VideoRateComponent implements OnInit, OnChanges, OnDestroy { private setRating (nextRating: UserVideoRateType) { const ratingMethods: { [id in UserVideoRateType]: (id: string, videoPassword: string) => Observable } = { - like: this.videoService.setVideoLike, - dislike: this.videoService.setVideoDislike, - none: this.videoService.unsetVideoLike + like: this.videoService.setVideoLike.bind(this.videoService), + dislike: this.videoService.setVideoDislike.bind(this.videoService), + none: this.videoService.unsetVideoLike.bind(this.videoService) } - ratingMethods[nextRating].call(this.videoService, this.video.uuid, this.videoPassword) - .subscribe({ - next: () => { - // Update the video like attribute - this.updateVideoRating(this.userRating, nextRating) - this.userRating = nextRating - this.rateUpdated.emit(this.userRating) - }, + ratingMethods[nextRating](this.video.uuid, this.videoPassword) + .subscribe({ + next: () => { + // Update the video like attribute + this.updateVideoRating(this.userRating, nextRating) + this.userRating = nextRating + this.rateUpdated.emit(this.userRating) + }, - error: err => this.notifier.error(err.message) - }) + error: err => this.notifier.error(err.message) + }) } private updateVideoRating (oldRating: UserVideoRateType, newRating: UserVideoRateType) { diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts index a01bd31fe..64e3f65dd 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts @@ -85,7 +85,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges, getEmojiMarkupList () { if (this.emojiMarkupList) return this.emojiMarkupList - const emojiMarkupObjectList = require('markdown-it-emoji/lib/data/light.json') + const emojiMarkupObjectList = require('markdown-it-emoji/lib/data/light.mjs').default this.emojiMarkupList = [] for (const name of Object.keys(emojiMarkupObjectList)) { diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 360524d69..6c285fe30 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -200,7 +200,7 @@ routes.push({ @NgModule({ imports: [ RouterModule.forRoot(routes, { - useHash: Boolean(history.pushState) === false, + useHash: false, // Redefined in app component scrollPositionRestoration: 'disabled', preloadingStrategy: PreloadSelectedModulesList, diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts index bcc50c0f4..f7105e07f 100644 --- a/client/src/app/core/rest/rest-extractor.service.ts +++ b/client/src/app/core/rest/rest-extractor.service.ts @@ -32,7 +32,7 @@ export class RestExtractor { fieldsToConvert: string[] = [ 'createdAt' ], format?: DateFormat ): ResultList { - return this.applyToResultListData(result, this.convertDateToHuman, [ fieldsToConvert, format ]) + return this.applyToResultListData(result, this.convertDateToHuman.bind(this), [ fieldsToConvert, format ]) } convertDateToHuman (target: any, fieldsToConvert: string[], format?: DateFormat) { diff --git a/client/src/app/core/routing/menu-guard.service.ts b/client/src/app/core/routing/menu-guard.service.ts index 765862b59..3b3723b5a 100644 --- a/client/src/app/core/routing/menu-guard.service.ts +++ b/client/src/app/core/routing/menu-guard.service.ts @@ -3,7 +3,7 @@ import { MenuService } from '../menu' import { ScreenService } from '../wrappers' abstract class MenuGuard { - canDeactivate = this.canActivate + canDeactivate = this.canActivate.bind(this) constructor (protected menu: MenuService, protected screen: ScreenService, protected display: boolean) { diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts index c896da116..b0fe0193c 100644 --- a/client/src/app/menu/menu.component.ts +++ b/client/src/app/menu/menu.component.ts @@ -244,8 +244,10 @@ export class MenuComponent implements OnInit, OnDestroy { if (opened) { window.addEventListener('scroll', onWindowScroll) document.querySelector('nav').scrollTo(0, 0) // Reset menu scroll to easy lock + // eslint-disable-next-line @typescript-eslint/unbound-method document.querySelector('nav').addEventListener('scroll', this.onMenuScrollEvent) } else { + // eslint-disable-next-line @typescript-eslint/unbound-method document.querySelector('nav').removeEventListener('scroll', this.onMenuScrollEvent) } } diff --git a/client/src/app/menu/notification.component.ts b/client/src/app/menu/notification.component.ts index 2f1557120..4017ac58d 100644 --- a/client/src/app/menu/notification.component.ts +++ b/client/src/app/menu/notification.component.ts @@ -67,6 +67,7 @@ export class NotificationComponent implements OnInit, OnDestroy { this.opened = true document.querySelector('nav').scrollTo(0, 0) // Reset menu scroll to easy lock + // eslint-disable-next-line @typescript-eslint/unbound-method document.querySelector('nav').addEventListener('scroll', this.onMenuScrollEvent) } @@ -74,6 +75,7 @@ export class NotificationComponent implements OnInit, OnDestroy { this.loaded = false this.opened = false + // eslint-disable-next-line @typescript-eslint/unbound-method document.querySelector('nav').removeEventListener('scroll', this.onMenuScrollEvent) } diff --git a/client/src/app/shared/shared-main/video/video-ownership.service.ts b/client/src/app/shared/shared-main/video/video-ownership.service.ts index 03e8fc946..03bdea14c 100644 --- a/client/src/app/shared/shared-main/video/video-ownership.service.ts +++ b/client/src/app/shared/shared-main/video/video-ownership.service.ts @@ -41,12 +41,12 @@ export class VideoOwnershipService { acceptOwnership (id: number, input: VideoChangeOwnershipAccept) { const url = VideoOwnershipService.BASE_VIDEO_CHANGE_OWNERSHIP_URL + 'ownership/' + id + '/accept' return this.authHttp.post(url, input) - .pipe(catchError(this.restExtractor.handleError)) + .pipe(catchError(err => this.restExtractor.handleError(err))) } refuseOwnership (id: number) { const url = VideoOwnershipService.BASE_VIDEO_CHANGE_OWNERSHIP_URL + 'ownership/' + id + '/refuse' return this.authHttp.post(url, {}) - .pipe(catchError(this.restExtractor.handleError)) + .pipe(catchError(err => this.restExtractor.handleError(err))) } } diff --git a/client/src/assets/player/shared/settings/settings-menu-button.ts b/client/src/assets/player/shared/settings/settings-menu-button.ts index 9b28c94db..2ce175655 100644 --- a/client/src/assets/player/shared/settings/settings-menu-button.ts +++ b/client/src/assets/player/shared/settings/settings-menu-button.ts @@ -244,6 +244,7 @@ class SettingsButton extends Button { // Hide children to avoid sub menus stacking on top of each other // or having multiple menus open + // eslint-disable-next-line @typescript-eslint/unbound-method settingsMenuItem.on('click', videojs.bind(this, this.hideChildren)) // Whether to add or remove selected class on the settings sub menu element diff --git a/client/src/typings.d.ts b/client/src/typings.d.ts index ad21fdedf..9145ebc3f 100644 --- a/client/src/typings.d.ts +++ b/client/src/typings.d.ts @@ -6,4 +6,4 @@ interface NodeModule { id: string } -declare module 'markdown-it-emoji/light' +declare module 'markdown-it-emoji/lib/light.mjs' diff --git a/client/yarn.lock b/client/yarn.lock index 8b38cc444..0a1709fcd 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -7555,6 +7555,11 @@ map-stream@~0.1.0: resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== +markdown-it-emoji@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz#8475a04d671d7c93f931b76fb90c582768b7f0b5" + integrity sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg== + markdown-it@14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.0.0.tgz#b4b2ddeb0f925e88d981f84c183b59bac9e3741b" diff --git a/packages/server-commands/src/server/jobs.ts b/packages/server-commands/src/server/jobs.ts index 1f3b1f745..32b09755d 100644 --- a/packages/server-commands/src/server/jobs.ts +++ b/packages/server-commands/src/server/jobs.ts @@ -18,8 +18,8 @@ async function waitJobs ( let servers: PeerTubeServer[] - if (Array.isArray(serversArg) === false) servers = [ serversArg as PeerTubeServer ] - else servers = serversArg as PeerTubeServer[] + if (Array.isArray(serversArg) === false) servers = [ serversArg ] + else servers = serversArg const states: JobState[] = [ 'waiting', 'active' ] if (!skipDelayed) states.push('delayed') diff --git a/packages/server-commands/src/server/servers.ts b/packages/server-commands/src/server/servers.ts index dc478929e..113a12942 100644 --- a/packages/server-commands/src/server/servers.ts +++ b/packages/server-commands/src/server/servers.ts @@ -35,6 +35,7 @@ async function cleanupTests (servers: PeerTubeServer[]) { for (const server of servers) { if (!server) continue + // eslint-disable-next-line @typescript-eslint/no-floating-promises p = p.concat(server.servers.cleanupTests()) } diff --git a/packages/tests/src/api/check-params/my-user.ts b/packages/tests/src/api/check-params/my-user.ts index 2ef2e242a..f22bf2205 100644 --- a/packages/tests/src/api/check-params/my-user.ts +++ b/packages/tests/src/api/check-params/my-user.ts @@ -149,7 +149,7 @@ describe('Test my user API validators', function () { await makePutBodyRequest({ url: server.url, path: path + 'me', - token: 'super token', + token: 'supertoken', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) diff --git a/packages/tests/src/api/check-params/users-admin.ts b/packages/tests/src/api/check-params/users-admin.ts index 1ad222ddc..8114a570b 100644 --- a/packages/tests/src/api/check-params/users-admin.ts +++ b/packages/tests/src/api/check-params/users-admin.ts @@ -187,7 +187,7 @@ describe('Test users admin API validators', function () { await makePostBodyRequest({ url: server.url, path, - token: 'super token', + token: 'supertoken', fields: baseCorrectParams, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) @@ -309,7 +309,7 @@ describe('Test users admin API validators', function () { await makeGetRequest({ url: server.url, path: path + userId, - token: 'super token', + token: 'supertoken', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) }) @@ -383,7 +383,7 @@ describe('Test users admin API validators', function () { await makePutBodyRequest({ url: server.url, path: path + userId, - token: 'super token', + token: 'supertoken', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) diff --git a/packages/tests/src/api/check-params/video-blacklist.ts b/packages/tests/src/api/check-params/video-blacklist.ts index c0506c385..e5006574e 100644 --- a/packages/tests/src/api/check-params/video-blacklist.ts +++ b/packages/tests/src/api/check-params/video-blacklist.ts @@ -226,7 +226,7 @@ describe('Test video blacklist API validators', function () { it('Should fail with a non authenticated user', async function () { await command.remove({ - token: 'fake token', + token: 'faketoken', videoId: servers[0].store.videoCreated.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) @@ -258,7 +258,7 @@ describe('Test video blacklist API validators', function () { const basePath = '/api/v1/videos/blacklist/' it('Should fail with a non authenticated user', async function () { - await servers[0].blacklist.list({ token: 'fake token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) + await servers[0].blacklist.list({ token: 'faketoken', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) }) it('Should fail with a non admin user', async function () { diff --git a/packages/tests/src/api/videos/video-nsfw.ts b/packages/tests/src/api/videos/video-nsfw.ts index fc5225dd2..162d6e2bf 100644 --- a/packages/tests/src/api/videos/video-nsfw.ts +++ b/packages/tests/src/api/videos/video-nsfw.ts @@ -24,6 +24,7 @@ describe('Test video NSFW policy', function () { let promises: Promise>[] if (token) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises promises = [ server.search.advancedVideoSearch({ token, search: { search: 'n', sort: '-publishedAt', ...query } }), server.videos.listWithToken({ token, ...query }), @@ -34,13 +35,14 @@ describe('Test video NSFW policy', function () { // Overviews do not support video filters if (!hasQuery) { const p = server.overviews.getVideos({ page: 1, token }) - .then(res => createOverviewRes(res)) + .then(res => createOverviewRes(res)) promises.push(p) } return Promise.all(promises) } + // eslint-disable-next-line @typescript-eslint/no-floating-promises promises = [ server.search.searchVideos({ search: 'n', sort: '-publishedAt' }), server.videos.list(), @@ -51,7 +53,8 @@ describe('Test video NSFW policy', function () { // Overviews do not support video filters if (!hasQuery) { const p = server.overviews.getVideos({ page: 1 }) - .then(res => createOverviewRes(res)) + .then(res => createOverviewRes(res)) + promises.push(p) } diff --git a/server/core/controllers/api/accounts.ts b/server/core/controllers/api/accounts.ts index b5fd4d133..7b4a11ddd 100644 --- a/server/core/controllers/api/accounts.ts +++ b/server/core/controllers/api/accounts.ts @@ -226,7 +226,7 @@ async function listAccountVideos (req: express.Request, res: express.Response) { }, 'filter:api.accounts.videos.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoModel.listForApi, + VideoModel.listForApi.bind(VideoModel), apiOptions, 'filter:api.accounts.videos.list.result' ) diff --git a/server/core/controllers/api/overviews.ts b/server/core/controllers/api/overviews.ts index a34669662..f5ff54a9f 100644 --- a/server/core/controllers/api/overviews.ts +++ b/server/core/controllers/api/overviews.ts @@ -130,7 +130,7 @@ async function getVideos ( }, 'filter:api.overviews.videos.list.params') const { data } = await Hooks.wrapPromiseFun( - VideoModel.listForApi, + VideoModel.listForApi.bind(VideoModel), query, 'filter:api.overviews.videos.list.result' ) diff --git a/server/core/controllers/api/search/search-video-channels.ts b/server/core/controllers/api/search/search-video-channels.ts index cccc3b3dc..a278a93dc 100644 --- a/server/core/controllers/api/search/search-video-channels.ts +++ b/server/core/controllers/api/search/search-video-channels.ts @@ -102,7 +102,7 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQueryAfterSaniti }, 'filter:api.search.video-channels.local.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoChannelModel.searchForApi, + VideoChannelModel.searchForApi.bind(VideoChannelModel), apiOptions, 'filter:api.search.video-channels.local.list.result' ) diff --git a/server/core/controllers/api/search/search-video-playlists.ts b/server/core/controllers/api/search/search-video-playlists.ts index 4c0f98cf2..ca8c90956 100644 --- a/server/core/controllers/api/search/search-video-playlists.ts +++ b/server/core/controllers/api/search/search-video-playlists.ts @@ -93,7 +93,7 @@ async function searchVideoPlaylistsDB (query: VideoPlaylistsSearchQueryAfterSani }, 'filter:api.search.video-playlists.local.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoPlaylistModel.searchForApi, + VideoPlaylistModel.searchForApi.bind(VideoPlaylistModel), apiOptions, 'filter:api.search.video-playlists.local.list.result' ) diff --git a/server/core/controllers/api/search/search-videos.ts b/server/core/controllers/api/search/search-videos.ts index b8f521c1a..ad497748c 100644 --- a/server/core/controllers/api/search/search-videos.ts +++ b/server/core/controllers/api/search/search-videos.ts @@ -121,7 +121,7 @@ async function searchVideosDB (query: VideosSearchQueryAfterSanitize, req: expre }, 'filter:api.search.videos.local.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoModel.searchAndPopulateAccountAndServer, + VideoModel.searchAndPopulateAccountAndServer.bind(VideoModel), apiOptions, 'filter:api.search.videos.local.list.result' ) diff --git a/server/core/controllers/api/users/me.ts b/server/core/controllers/api/users/me.ts index 461867876..fd8e9441c 100644 --- a/server/core/controllers/api/users/me.ts +++ b/server/core/controllers/api/users/me.ts @@ -131,7 +131,7 @@ async function getUserVideos (req: express.Request, res: express.Response) { }, 'filter:api.user.me.videos.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoModel.listUserVideosForApi, + VideoModel.listUserVideosForApi.bind(VideoModel), apiOptions, 'filter:api.user.me.videos.list.result' ) diff --git a/server/core/controllers/api/users/my-subscriptions.ts b/server/core/controllers/api/users/my-subscriptions.ts index f2010950e..3c0e48121 100644 --- a/server/core/controllers/api/users/my-subscriptions.ts +++ b/server/core/controllers/api/users/my-subscriptions.ts @@ -184,7 +184,7 @@ async function getUserSubscriptionVideos (req: express.Request, res: express.Res }, 'filter:api.user.me.subscription-videos.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoModel.listForApi, + VideoModel.listForApi.bind(VideoModel), apiOptions, 'filter:api.user.me.subscription-videos.list.result' ) diff --git a/server/core/controllers/api/video-channel.ts b/server/core/controllers/api/video-channel.ts index 68b6963f8..b9c9e5f60 100644 --- a/server/core/controllers/api/video-channel.ts +++ b/server/core/controllers/api/video-channel.ts @@ -200,7 +200,7 @@ async function listVideoChannels (req: express.Request, res: express.Response) { }, 'filter:api.video-channels.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoChannelModel.listForApi, + VideoChannelModel.listForApi.bind(VideoChannelModel), apiOptions, 'filter:api.video-channels.list.result' ) @@ -409,7 +409,7 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon }, 'filter:api.video-channels.videos.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoModel.listForApi, + VideoModel.listForApi.bind(VideoModel), apiOptions, 'filter:api.video-channels.videos.list.result' ) diff --git a/server/core/controllers/api/video-playlist.ts b/server/core/controllers/api/video-playlist.ts index 1e75727eb..aef073ceb 100644 --- a/server/core/controllers/api/video-playlist.ts +++ b/server/core/controllers/api/video-playlist.ts @@ -472,7 +472,7 @@ async function getVideoPlaylistVideos (req: express.Request, res: express.Respon }, 'filter:api.video-playlist.videos.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoPlaylistElementModel.listForApi, + VideoPlaylistElementModel.listForApi.bind(VideoPlaylistElementModel), apiOptions, 'filter:api.video-playlist.videos.list.result' ) diff --git a/server/core/controllers/api/videos/comment.ts b/server/core/controllers/api/videos/comment.ts index ae8994382..e7fe6ca43 100644 --- a/server/core/controllers/api/videos/comment.ts +++ b/server/core/controllers/api/videos/comment.ts @@ -128,7 +128,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) { }, 'filter:api.video-threads.list.params') resultList = await Hooks.wrapPromiseFun( - VideoCommentModel.listThreadsForApi, + VideoCommentModel.listThreadsForApi.bind(VideoCommentModel), apiOptions, 'filter:api.video-threads.list.result' ) @@ -160,7 +160,7 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo }, 'filter:api.video-thread-comments.list.params') resultList = await Hooks.wrapPromiseFun( - VideoCommentModel.listThreadCommentsForApi, + VideoCommentModel.listThreadCommentsForApi.bind(VideoCommentModel), apiOptions, 'filter:api.video-thread-comments.list.result' ) diff --git a/server/core/controllers/api/videos/index.ts b/server/core/controllers/api/videos/index.ts index 430555ac4..ec6a50f08 100644 --- a/server/core/controllers/api/videos/index.ts +++ b/server/core/controllers/api/videos/index.ts @@ -194,7 +194,7 @@ async function listVideos (req: express.Request, res: express.Response) { }, 'filter:api.videos.list.params') const resultList = await Hooks.wrapPromiseFun( - VideoModel.listForApi, + VideoModel.listForApi.bind(VideoModel), apiOptions, 'filter:api.videos.list.result' ) diff --git a/server/core/controllers/download.ts b/server/core/controllers/download.ts index f653014b9..26c9ce0d4 100644 --- a/server/core/controllers/download.ts +++ b/server/core/controllers/download.ts @@ -133,7 +133,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response) { async function downloadHLSVideoFile (req: express.Request, res: express.Response) { const video = res.locals.videoAll const streamingPlaylist = getHLSPlaylist(video) - if (!streamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).end + if (!streamingPlaylist) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) const videoFile = getVideoFile(req, streamingPlaylist.VideoFiles) if (!videoFile) { diff --git a/server/core/helpers/logger.ts b/server/core/helpers/logger.ts index 60a6c16f7..464f6388d 100644 --- a/server/core/helpers/logger.ts +++ b/server/core/helpers/logger.ts @@ -121,10 +121,10 @@ const bunyanLogger = { // --------------------------------------------------------------------------- -type LoggerTags = { tags: string[] } -type LoggerTagsFn = (...tags: string[]) => LoggerTags -function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn { - return (...tags: string[]) => { +type LoggerTags = { tags: (string | number)[] } +type LoggerTagsFn = (...tags: (string | number)[]) => LoggerTags +function loggerTagsFactory (...defaultTags: (string | number)[]): LoggerTagsFn { + return (...tags: (string | number)[]) => { return { tags: defaultTags.concat(tags) } } } diff --git a/server/core/lib/files-cache/shared/abstract-simple-file-cache.ts b/server/core/lib/files-cache/shared/abstract-simple-file-cache.ts index 1dd45d221..6e7d6867b 100644 --- a/server/core/lib/files-cache/shared/abstract-simple-file-cache.ts +++ b/server/core/lib/files-cache/shared/abstract-simple-file-cache.ts @@ -14,7 +14,7 @@ export abstract class AbstractSimpleFileCache { protected abstract loadRemoteFile (key: string): Promise init (max: number, maxAge: number) { - this.getFilePath = memoizee(this.getFilePathImpl, { + this.getFilePath = memoizee(this.getFilePathImpl.bind(this), { maxAge, max, promise: true, diff --git a/server/core/lib/job-queue/handlers/shared/move-video.ts b/server/core/lib/job-queue/handlers/shared/move-video.ts index 9aeb55353..93c49b207 100644 --- a/server/core/lib/job-queue/handlers/shared/move-video.ts +++ b/server/core/lib/job-queue/handlers/shared/move-video.ts @@ -7,7 +7,7 @@ import { MVideoWithAllFiles } from '@server/types/models/index.js' export async function moveToJob (options: { jobId: string videoUUID: string - loggerTags: string[] + loggerTags: (number | string)[] moveWebVideoFiles: (video: MVideoWithAllFiles) => Promise moveHLSFiles: (video: MVideoWithAllFiles) => Promise diff --git a/server/core/lib/job-queue/handlers/video-studio-edition.ts b/server/core/lib/job-queue/handlers/video-studio-edition.ts index c0a1c60a6..a21491a36 100644 --- a/server/core/lib/job-queue/handlers/video-studio-edition.ts +++ b/server/core/lib/job-queue/handlers/video-studio-edition.ts @@ -95,7 +95,7 @@ type TaskProcessorOptions Promise } = { diff --git a/server/core/lib/live/live-manager.ts b/server/core/lib/live/live-manager.ts index bfe13066d..3ef1661b8 100644 --- a/server/core/lib/live/live-manager.ts +++ b/server/core/lib/live/live-manager.ts @@ -86,7 +86,7 @@ class LiveManager { .catch(err => logger.error('Cannot handle sessions.', { err, ...lTags(sessionId) })) }) - events.on('donePublish', sessionId => { + events.on('donePublish', (sessionId: string) => { logger.info('Live session ended.', { sessionId, ...lTags(sessionId) }) // Force session aborting, so we kill ffmpeg even if it still has data to process (slow CPU) @@ -405,7 +405,7 @@ class LiveManager { }) } - private async publishAndFederateLive (live: MVideoLiveVideo, localLTags: { tags: string[] }) { + private async publishAndFederateLive (live: MVideoLiveVideo, localLTags: { tags: (string | number)[] }) { const videoId = live.videoId try { diff --git a/server/core/lib/object-storage/shared/object-storage-helpers.ts b/server/core/lib/object-storage/shared/object-storage-helpers.ts index 4c1e512a5..88fee8eb1 100644 --- a/server/core/lib/object-storage/shared/object-storage-helpers.ts +++ b/server/core/lib/object-storage/shared/object-storage-helpers.ts @@ -11,7 +11,7 @@ import { getInternalUrl } from '../urls.js' import { getClient } from './client.js' import { lTags } from './logger.js' -import type { _Object, CompleteMultipartUploadCommandOutput, ObjectCannedACL, PutObjectCommandInput, S3Client } from '@aws-sdk/client-s3' +import type { _Object, ObjectCannedACL, PutObjectCommandInput, S3Client } from '@aws-sdk/client-s3' type BucketInfo = { BUCKET_NAME: string @@ -317,7 +317,7 @@ async function uploadToStorage (options: { params: input }) - const response = (await parallelUploads3.done()) as CompleteMultipartUploadCommandOutput + const response = await parallelUploads3.done() // Check is needed even if the HTTP status code is 200 OK // For more information, see https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html if (!response.Bucket) { diff --git a/server/core/lib/peertube-socket.ts b/server/core/lib/peertube-socket.ts index 4bb82189e..17168f4ad 100644 --- a/server/core/lib/peertube-socket.ts +++ b/server/core/lib/peertube-socket.ts @@ -42,14 +42,16 @@ class PeerTubeSocket { this.liveVideosNamespace = io.of('/live-videos') .on('connection', socket => { - socket.on('subscribe', ({ videoId }) => { + socket.on('subscribe', params => { + const videoId = params.videoId + '' if (!isIdValid(videoId)) return /* eslint-disable @typescript-eslint/no-floating-promises */ socket.join(videoId) }) - socket.on('unsubscribe', ({ videoId }) => { + socket.on('unsubscribe', params => { + const videoId = params.videoId + '' if (!isIdValid(videoId)) return /* eslint-disable @typescript-eslint/no-floating-promises */ @@ -93,7 +95,7 @@ class PeerTubeSocket { logger.debug('Sending video live new state notification of %s.', video.url, { state: video.state }) this.liveVideosNamespace - .in(video.id) + .in(video.id + '') .emit(type, data) } @@ -104,7 +106,7 @@ class PeerTubeSocket { logger.debug('Sending video live views update notification of %s.', video.url, { viewers: numViewers }) this.liveVideosNamespace - .in(video.id) + .in(video.id + '') .emit(type, data) } diff --git a/server/core/middlewares/async.ts b/server/core/middlewares/async.ts index e1483ed09..d4dfa9b46 100644 --- a/server/core/middlewares/async.ts +++ b/server/core/middlewares/async.ts @@ -15,7 +15,7 @@ function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) } try { - for (const f of (fun as RequestPromiseHandler[])) { + for (const f of fun) { await new Promise((resolve, reject) => { return asyncMiddleware(f)(req, res, err => { if (err) return reject(err) diff --git a/server/core/middlewares/cache/shared/api-cache.ts b/server/core/middlewares/cache/shared/api-cache.ts index 514fc2f99..4cf38e011 100644 --- a/server/core/middlewares/cache/shared/api-cache.ts +++ b/server/core/middlewares/cache/shared/api-cache.ts @@ -189,9 +189,9 @@ export class ApiCache { const self = this res.locals.apicache = { - write: res.write, - writeHead: res.writeHead, - end: res.end, + write: res.write.bind(res), + writeHead: res.writeHead.bind(res), + end: res.end.bind(res), cacheable: true, content: undefined, headers: undefined diff --git a/server/core/middlewares/validators/shared/utils.ts b/server/core/middlewares/validators/shared/utils.ts index c36525a6d..7e0619cdb 100644 --- a/server/core/middlewares/validators/shared/utils.ts +++ b/server/core/middlewares/validators/shared/utils.ts @@ -9,7 +9,7 @@ function areValidationErrors ( options: { omitLog?: boolean omitBodyLog?: boolean - tags?: string[] + tags?: (number | string)[] } = {}) { const { omitLog = false, omitBodyLog = false, tags = [] } = options diff --git a/server/core/models/abuse/abuse-message.ts b/server/core/models/abuse/abuse-message.ts index 9ba080e77..09e82094a 100644 --- a/server/core/models/abuse/abuse-message.ts +++ b/server/core/models/abuse/abuse-message.ts @@ -1,11 +1,10 @@ import { FindOptions } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Table, UpdatedAt } from 'sequelize-typescript' import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses.js' import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models/index.js' import { AbuseMessage } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account.js' -import { getSort, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js' import { AbuseModel } from './abuse.js' @Table({ @@ -19,7 +18,7 @@ import { AbuseModel } from './abuse.js' } ] }) -export class AbuseMessageModel extends Model>> { +export class AbuseMessageModel extends SequelizeModel { @AllowNull(false) @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) diff --git a/server/core/models/abuse/abuse.ts b/server/core/models/abuse/abuse.ts index fa33369b6..c3b03b18d 100644 --- a/server/core/models/abuse/abuse.ts +++ b/server/core/models/abuse/abuse.ts @@ -12,7 +12,6 @@ import { UserVideoAbuse, type AbuseStateType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses.js' import invert from 'lodash-es/invert.js' import { Op, QueryTypes, literal } from 'sequelize' @@ -25,9 +24,7 @@ import { Default, ForeignKey, HasOne, - Is, - Model, - Scopes, + Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -41,7 +38,7 @@ import { MUserAccountId } from '../../types/models/index.js' import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account.js' -import { getSort, parseAggregateResult, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, parseAggregateResult, throwIfNotValid } from '../shared/index.js' import { ThumbnailModel } from '../video/thumbnail.js' import { VideoBlacklistModel } from '../video/video-blacklist.js' import { SummaryOptions as ChannelSummaryOptions, VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../video/video-channel.js' @@ -195,7 +192,7 @@ export enum ScopeNames { } ] }) -export class AbuseModel extends Model>> { +export class AbuseModel extends SequelizeModel { @AllowNull(false) @Default(null) diff --git a/server/core/models/abuse/video-abuse.ts b/server/core/models/abuse/video-abuse.ts index 1f2f85f1c..2c681eef7 100644 --- a/server/core/models/abuse/video-abuse.ts +++ b/server/core/models/abuse/video-abuse.ts @@ -1,8 +1,8 @@ import { type VideoDetails } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { VideoModel } from '../video/video.js' import { AbuseModel } from './abuse.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'videoAbuse', @@ -15,7 +15,7 @@ import { AbuseModel } from './abuse.js' } ] }) -export class VideoAbuseModel extends Model>> { +export class VideoAbuseModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/abuse/video-comment-abuse.ts b/server/core/models/abuse/video-comment-abuse.ts index 6c5078aa8..8bc143c28 100644 --- a/server/core/models/abuse/video-comment-abuse.ts +++ b/server/core/models/abuse/video-comment-abuse.ts @@ -1,7 +1,7 @@ -import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { BelongsTo, Column, CreatedAt, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { VideoCommentModel } from '../video/video-comment.js' import { AbuseModel } from './abuse.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'commentAbuse', @@ -14,7 +14,7 @@ import { AbuseModel } from './abuse.js' } ] }) -export class VideoCommentAbuseModel extends Model>> { +export class VideoCommentAbuseModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/account/account-blocklist.ts b/server/core/models/account/account-blocklist.ts index bc088a4e5..018c907fd 100644 --- a/server/core/models/account/account-blocklist.ts +++ b/server/core/models/account/account-blocklist.ts @@ -1,12 +1,11 @@ import { FindOptions, Op, QueryTypes } from 'sequelize' -import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { BelongsTo, Column, CreatedAt, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { AccountBlock } from '@peertube/peertube-models' import { handlesToNameAndHost } from '@server/helpers/actors.js' import { MAccountBlocklist, MAccountBlocklistFormattable } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { ActorModel } from '../actor/actor.js' import { ServerModel } from '../server/server.js' -import { createSafeIn, getSort, searchAttribute } from '../shared/index.js' +import { SequelizeModel, createSafeIn, getSort, searchAttribute } from '../shared/index.js' import { AccountModel } from './account.js' import { WEBSERVER } from '@server/initializers/constants.js' @@ -22,7 +21,7 @@ import { WEBSERVER } from '@server/initializers/constants.js' } ] }) -export class AccountBlocklistModel extends Model>> { +export class AccountBlocklistModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/account/account-video-rate.ts b/server/core/models/account/account-video-rate.ts index 3f9c93808..cff82a4a6 100644 --- a/server/core/models/account/account-video-rate.ts +++ b/server/core/models/account/account-video-rate.ts @@ -1,5 +1,4 @@ import { AccountVideoRate, type VideoRateType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { MAccountVideoRate, MAccountVideoRateAccountUrl, @@ -8,11 +7,11 @@ import { MAccountVideoRateVideoUrl } from '@server/types/models/index.js' import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Table, UpdatedAt } from 'sequelize-typescript' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants.js' import { ActorModel } from '../actor/actor.js' -import { getSort, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js' import { SummaryOptions, VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../video/video-channel.js' import { VideoModel } from '../video/video.js' import { AccountModel } from './account.js' @@ -42,7 +41,7 @@ import { AccountModel } from './account.js' } ] }) -export class AccountVideoRateModel extends Model>> { +export class AccountVideoRateModel extends SequelizeModel { @AllowNull(false) @Column(DataType.ENUM(...Object.values(VIDEO_RATE_TYPES))) diff --git a/server/core/models/account/account.ts b/server/core/models/account/account.ts index daede0abb..99b151b44 100644 --- a/server/core/models/account/account.ts +++ b/server/core/models/account/account.ts @@ -10,15 +10,12 @@ import { DefaultScope, ForeignKey, HasMany, - Is, - Model, - Scopes, + Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { Account, AccountSummary } from '@peertube/peertube-models' import { ModelCache } from '@server/models/shared/model-cache.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts.js' import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants.js' import { sendDeleteActor } from '../../lib/activitypub/send/send-delete.js' @@ -36,7 +33,7 @@ import { ActorModel } from '../actor/actor.js' import { ApplicationModel } from '../application/application.js' import { ServerBlocklistModel } from '../server/server-blocklist.js' import { ServerModel } from '../server/server.js' -import { buildSQLAttributes, getSort, throwIfNotValid } from '../shared/index.js' +import { buildSQLAttributes, getSort, SequelizeModel, throwIfNotValid } from '../shared/index.js' import { UserModel } from '../user/user.js' import { VideoChannelModel } from '../video/video-channel.js' import { VideoCommentModel } from '../video/video-comment.js' @@ -144,7 +141,7 @@ export type SummaryOptions = { } ] }) -export class AccountModel extends Model>> { +export class AccountModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/account/actor-custom-page.ts b/server/core/models/account/actor-custom-page.ts index 8a9b09706..3c239f270 100644 --- a/server/core/models/account/actor-custom-page.ts +++ b/server/core/models/account/actor-custom-page.ts @@ -1,7 +1,8 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { CustomPage } from '@peertube/peertube-models' import { ActorModel } from '../actor/actor.js' import { getServerActor } from '../application/application.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'actorCustomPage', @@ -12,7 +13,7 @@ import { getServerActor } from '../application/application.js' } ] }) -export class ActorCustomPageModel extends Model { +export class ActorCustomPageModel extends SequelizeModel { @AllowNull(true) @Column(DataType.TEXT) diff --git a/server/core/models/actor/actor-follow.ts b/server/core/models/actor/actor-follow.ts index 38ae4b764..f7a1cf582 100644 --- a/server/core/models/actor/actor-follow.ts +++ b/server/core/models/actor/actor-follow.ts @@ -1,5 +1,4 @@ import { ActorFollow, type FollowState } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc.js' import { afterCommitIfTransaction } from '@server/helpers/database-utils.js' import { getServerActor } from '@server/models/application/application.js' @@ -27,16 +26,14 @@ import { ForeignKey, Is, IsInt, - Max, - Model, - Table, + Max, Table, UpdatedAt } from 'sequelize-typescript' import { logger } from '../../helpers/logger.js' import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME, SORTABLE_COLUMNS } from '../../initializers/constants.js' import { AccountModel } from '../account/account.js' import { ServerModel } from '../server/server.js' -import { buildSQLAttributes, createSafeIn, getSort, searchAttribute, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, buildSQLAttributes, createSafeIn, getSort, searchAttribute, throwIfNotValid } from '../shared/index.js' import { doesExist } from '../shared/query.js' import { VideoChannelModel } from '../video/video-channel.js' import { ActorModel, unusedActorAttributesForAPI } from './actor.js' @@ -65,7 +62,7 @@ import { InstanceListFollowingQueryBuilder, ListFollowingOptions } from './sql/i } ] }) -export class ActorFollowModel extends Model>> { +export class ActorFollowModel extends SequelizeModel { @AllowNull(false) @Column(DataType.ENUM(...Object.values(FOLLOW_STATES))) diff --git a/server/core/models/actor/actor-image.ts b/server/core/models/actor/actor-image.ts index 96a9b9bdc..f54baadc1 100644 --- a/server/core/models/actor/actor-image.ts +++ b/server/core/models/actor/actor-image.ts @@ -1,6 +1,5 @@ import { ActivityIconObject, ActorImage, ActorImageType, type ActorImageType_Type } from '@peertube/peertube-models' import { getLowercaseExtension } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { MActorId, MActorImage, MActorImageFormattable } from '@server/types/models/index.js' import { remove } from 'fs-extra/esm' import { join } from 'path' @@ -12,16 +11,14 @@ import { CreatedAt, Default, ForeignKey, - Is, - Model, - Table, + Is, Table, UpdatedAt } from 'sequelize-typescript' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' import { logger } from '../../helpers/logger.js' import { CONFIG } from '../../initializers/config.js' import { LAZY_STATIC_PATHS, MIMETYPES, WEBSERVER } from '../../initializers/constants.js' -import { buildSQLAttributes, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, buildSQLAttributes, throwIfNotValid } from '../shared/index.js' import { ActorModel } from './actor.js' @Table({ @@ -37,7 +34,7 @@ import { ActorModel } from './actor.js' } ] }) -export class ActorImageModel extends Model>> { +export class ActorImageModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/actor/actor.ts b/server/core/models/actor/actor.ts index 64d8cf5c6..6ca820b82 100644 --- a/server/core/models/actor/actor.ts +++ b/server/core/models/actor/actor.ts @@ -17,9 +17,7 @@ import { ForeignKey, HasMany, HasOne, - Is, - Model, - Scopes, + Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -58,7 +56,7 @@ import { import { AccountModel } from '../account/account.js' import { getServerActor } from '../application/application.js' import { ServerModel } from '../server/server.js' -import { buildSQLAttributes, isOutdated, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, buildSQLAttributes, isOutdated, throwIfNotValid } from '../shared/index.js' import { VideoChannelModel } from '../video/video-channel.js' import { VideoModel } from '../video/video.js' import { ActorFollowModel } from './actor-follow.js' @@ -165,7 +163,7 @@ export const unusedActorAttributesForAPI: (keyof AttributesOnly)[] = } ] }) -export class ActorModel extends Model>> { +export class ActorModel extends SequelizeModel { @AllowNull(false) @Column(DataType.ENUM(...Object.values(ACTIVITY_PUB_ACTOR_TYPES))) diff --git a/server/core/models/application/application.ts b/server/core/models/application/application.ts index 1b1ff15b1..3911c057b 100644 --- a/server/core/models/application/application.ts +++ b/server/core/models/application/application.ts @@ -1,8 +1,8 @@ import memoizee from 'memoizee' -import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' +import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Table } from 'sequelize-typescript' import { getNodeABIVersion } from '@server/helpers/version.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { AccountModel } from '../account/account.js' +import { SequelizeModel } from '../shared/index.js' export const getServerActor = memoizee(async function () { const application = await ApplicationModel.load() @@ -26,7 +26,7 @@ export const getServerActor = memoizee(async function () { tableName: 'application', timestamps: false }) -export class ApplicationModel extends Model>> { +export class ApplicationModel extends SequelizeModel { @AllowNull(false) @Default(0) diff --git a/server/core/models/oauth/oauth-client.ts b/server/core/models/oauth/oauth-client.ts index 3d17f2431..dc022c891 100644 --- a/server/core/models/oauth/oauth-client.ts +++ b/server/core/models/oauth/oauth-client.ts @@ -1,6 +1,6 @@ -import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { AllowNull, Column, CreatedAt, DataType, HasMany, Table, UpdatedAt } from 'sequelize-typescript' import { OAuthTokenModel } from './oauth-token.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'oAuthClient', @@ -15,7 +15,7 @@ import { OAuthTokenModel } from './oauth-token.js' } ] }) -export class OAuthClientModel extends Model>> { +export class OAuthClientModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/oauth/oauth-token.ts b/server/core/models/oauth/oauth-token.ts index 7101e199a..29772c614 100644 --- a/server/core/models/oauth/oauth-token.ts +++ b/server/core/models/oauth/oauth-token.ts @@ -6,21 +6,19 @@ import { BelongsTo, Column, CreatedAt, - ForeignKey, - Model, - Scopes, + ForeignKey, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { TokensCache } from '@server/lib/auth/tokens-cache.js' import { MUserAccountId } from '@server/types/models/index.js' import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { logger } from '../../helpers/logger.js' import { AccountModel } from '../account/account.js' import { ActorModel } from '../actor/actor.js' import { UserModel } from '../user/user.js' import { OAuthClientModel } from './oauth-client.js' +import { SequelizeModel } from '../shared/index.js' export type OAuthTokenInfo = { refreshToken: string @@ -80,7 +78,7 @@ enum ScopeNames { } ] }) -export class OAuthTokenModel extends Model>> { +export class OAuthTokenModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/redundancy/video-redundancy.ts b/server/core/models/redundancy/video-redundancy.ts index 3a5e7bf33..3e44caf5f 100644 --- a/server/core/models/redundancy/video-redundancy.ts +++ b/server/core/models/redundancy/video-redundancy.ts @@ -8,9 +8,7 @@ import { CreatedAt, DataType, ForeignKey, - Is, - Model, - Scopes, + Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -26,7 +24,6 @@ import { VideoRedundancyStrategyWithManual } from '@peertube/peertube-models' import { isTestInstance } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { getServerActor } from '@server/models/application/application.js' import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models/index.js' import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' @@ -35,7 +32,7 @@ import { CONFIG } from '../../initializers/config.js' import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js' import { ActorModel } from '../actor/actor.js' import { ServerModel } from '../server/server.js' -import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../shared/index.js' +import { getSort, getVideoSort, parseAggregateResult, SequelizeModel, throwIfNotValid } from '../shared/index.js' import { ScheduleVideoUpdateModel } from '../video/schedule-video-update.js' import { VideoChannelModel } from '../video/video-channel.js' import { VideoFileModel } from '../video/video-file.js' @@ -92,7 +89,7 @@ export enum ScopeNames { } ] }) -export class VideoRedundancyModel extends Model>> { +export class VideoRedundancyModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/runner/runner-job.ts b/server/core/models/runner/runner-job.ts index 1ebcdae1d..5c466518d 100644 --- a/server/core/models/runner/runner-job.ts +++ b/server/core/models/runner/runner-job.ts @@ -7,7 +7,6 @@ import { type RunnerJobStateType, type RunnerJobType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isArray, isUUIDValid } from '@server/helpers/custom-validators/misc.js' import { CONSTRAINTS_FIELDS, RUNNER_JOB_STATES } from '@server/initializers/constants.js' import { MRunnerJob, MRunnerJobRunner, MRunnerJobRunnerParent } from '@server/types/models/runners/index.js' @@ -20,13 +19,11 @@ import { DataType, Default, ForeignKey, - IsUUID, - Model, - Scopes, + IsUUID, Scopes, Table, UpdatedAt } from 'sequelize-typescript' -import { getSort, searchAttribute } from '../shared/index.js' +import { SequelizeModel, getSort, searchAttribute } from '../shared/index.js' import { RunnerModel } from './runner.js' enum ScopeNames { @@ -68,7 +65,7 @@ enum ScopeNames { } ] }) -export class RunnerJobModel extends Model>> { +export class RunnerJobModel extends SequelizeModel { @AllowNull(false) @IsUUID(4) diff --git a/server/core/models/runner/runner-registration-token.ts b/server/core/models/runner/runner-registration-token.ts index 1bde519ca..5d055f4a0 100644 --- a/server/core/models/runner/runner-registration-token.ts +++ b/server/core/models/runner/runner-registration-token.ts @@ -1,9 +1,8 @@ import { FindOptions, literal } from 'sequelize' -import { AllowNull, Column, CreatedAt, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, Column, CreatedAt, HasMany, Table, UpdatedAt } from 'sequelize-typescript' import { MRunnerRegistrationToken } from '@server/types/models/runners/index.js' import { RunnerRegistrationToken } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' -import { getSort } from '../shared/index.js' +import { SequelizeModel, getSort } from '../shared/index.js' import { RunnerModel } from './runner.js' /** @@ -21,7 +20,7 @@ import { RunnerModel } from './runner.js' } ] }) -export class RunnerRegistrationTokenModel extends Model>> { +export class RunnerRegistrationTokenModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/runner/runner.ts b/server/core/models/runner/runner.ts index 5c968b1c2..0d46e6cd4 100644 --- a/server/core/models/runner/runner.ts +++ b/server/core/models/runner/runner.ts @@ -1,9 +1,8 @@ import { FindOptions } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { MRunner } from '@server/types/models/runners/index.js' import { Runner } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' -import { getSort } from '../shared/index.js' +import { SequelizeModel, getSort } from '../shared/index.js' import { RunnerRegistrationTokenModel } from './runner-registration-token.js' import { CONSTRAINTS_FIELDS } from '@server/initializers/constants.js' @@ -23,7 +22,7 @@ import { CONSTRAINTS_FIELDS } from '@server/initializers/constants.js' } ] }) -export class RunnerModel extends Model>> { +export class RunnerModel extends SequelizeModel { // Used to identify the appropriate runner when it uses the runner REST API @AllowNull(false) diff --git a/server/core/models/server/plugin.ts b/server/core/models/server/plugin.ts index ff8350b6a..500e59e33 100644 --- a/server/core/models/server/plugin.ts +++ b/server/core/models/server/plugin.ts @@ -6,10 +6,9 @@ import { SettingValue, type PluginType_Type } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { MPlugin, MPluginFormattable } from '@server/types/models/index.js' import { FindAndCountOptions, json, QueryTypes } from 'sequelize' -import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Table, UpdatedAt } from 'sequelize-typescript' import { isPluginDescriptionValid, isPluginHomepage, @@ -18,7 +17,7 @@ import { isPluginStableVersionValid, isPluginTypeValid } from '../../helpers/custom-validators/plugins.js' -import { getSort, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js' @DefaultScope(() => ({ attributes: { @@ -35,7 +34,7 @@ import { getSort, throwIfNotValid } from '../shared/index.js' } ] }) -export class PluginModel extends Model>> { +export class PluginModel extends SequelizeModel { @AllowNull(false) @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) diff --git a/server/core/models/server/server-blocklist.ts b/server/core/models/server/server-blocklist.ts index 7817d122e..4d3d9e057 100644 --- a/server/core/models/server/server-blocklist.ts +++ b/server/core/models/server/server-blocklist.ts @@ -1,10 +1,9 @@ import { Op, QueryTypes } from 'sequelize' -import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' +import { BelongsTo, Column, CreatedAt, ForeignKey, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models/index.js' import { ServerBlock } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { AccountModel } from '../account/account.js' -import { createSafeIn, getSort, searchAttribute } from '../shared/index.js' +import { SequelizeModel, createSafeIn, getSort, searchAttribute } from '../shared/index.js' import { ServerModel } from './server.js' enum ScopeNames { @@ -43,7 +42,7 @@ enum ScopeNames { } ] }) -export class ServerBlocklistModel extends Model>> { +export class ServerBlocklistModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/server/server.ts b/server/core/models/server/server.ts index a50cc25d8..d1e45e7ff 100644 --- a/server/core/models/server/server.ts +++ b/server/core/models/server/server.ts @@ -1,10 +1,9 @@ import { Transaction } from 'sequelize' -import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Table, UpdatedAt } from 'sequelize-typescript' import { MServer, MServerFormattable } from '@server/types/models/server/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isHostValid } from '../../helpers/custom-validators/servers.js' import { ActorModel } from '../actor/actor.js' -import { buildSQLAttributes, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, buildSQLAttributes, throwIfNotValid } from '../shared/index.js' import { ServerBlocklistModel } from './server-blocklist.js' @Table({ @@ -16,7 +15,7 @@ import { ServerBlocklistModel } from './server-blocklist.js' } ] }) -export class ServerModel extends Model>> { +export class ServerModel extends SequelizeModel { @AllowNull(false) @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) diff --git a/server/core/models/server/tracker.ts b/server/core/models/server/tracker.ts index 46955768d..dfb41e859 100644 --- a/server/core/models/server/tracker.ts +++ b/server/core/models/server/tracker.ts @@ -1,9 +1,9 @@ -import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsToMany, Column, CreatedAt, Table, UpdatedAt } from 'sequelize-typescript' import { Transaction } from 'sequelize' import { MTracker } from '@server/types/models/server/tracker.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { VideoModel } from '../video/video.js' import { VideoTrackerModel } from './video-tracker.js' +import { SequelizeModel } from '../shared/sequelize-type.js' @Table({ tableName: 'tracker', @@ -14,7 +14,7 @@ import { VideoTrackerModel } from './video-tracker.js' } ] }) -export class TrackerModel extends Model>> { +export class TrackerModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/server/video-tracker.ts b/server/core/models/server/video-tracker.ts index a925dc5c1..468abb405 100644 --- a/server/core/models/server/video-tracker.ts +++ b/server/core/models/server/video-tracker.ts @@ -1,7 +1,7 @@ -import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { Column, CreatedAt, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { VideoModel } from '../video/video.js' import { TrackerModel } from './tracker.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'videoTracker', @@ -14,7 +14,7 @@ import { TrackerModel } from './tracker.js' } ] }) -export class VideoTrackerModel extends Model>> { +export class VideoTrackerModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/shared/index.ts b/server/core/models/shared/index.ts index 53cf65d5f..77e9373bb 100644 --- a/server/core/models/shared/index.ts +++ b/server/core/models/shared/index.ts @@ -3,6 +3,7 @@ export * from './model-builder.js' export * from './model-cache.js' export * from './query.js' export * from './sequelize-helpers.js' +export * from './sequelize-type.js' export * from './sort.js' export * from './sql.js' export * from './update.js' diff --git a/server/core/models/shared/sequelize-type.ts b/server/core/models/shared/sequelize-type.ts new file mode 100644 index 000000000..00b06752b --- /dev/null +++ b/server/core/models/shared/sequelize-type.ts @@ -0,0 +1,6 @@ +import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { Model } from 'sequelize-typescript' + +export abstract class SequelizeModel extends Model>> { + id: number +} diff --git a/server/core/models/user/user-export.ts b/server/core/models/user/user-export.ts index 68bc3cd38..8a67c81a5 100644 --- a/server/core/models/user/user-export.ts +++ b/server/core/models/user/user-export.ts @@ -1,7 +1,6 @@ import { FindOptions, Op } from 'sequelize' -import { AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { MUserAccountId, MUserExport } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { UserModel } from './user.js' import { getSort } from '../shared/sort.js' import { UserExportState, type UserExport, type UserExportStateType, type FileStorageType, FileStorage } from '@peertube/peertube-models' @@ -18,6 +17,7 @@ import { join } from 'path' import jwt from 'jsonwebtoken' import { CONFIG } from '@server/initializers/config.js' import { removeUserExportObjectStorage } from '@server/lib/object-storage/user-export.js' +import { SequelizeModel } from '../shared/sequelize-type.js' @Table({ tableName: 'userExport', @@ -31,7 +31,7 @@ import { removeUserExportObjectStorage } from '@server/lib/object-storage/user-e } ] }) -export class UserExportModel extends Model>> { +export class UserExportModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/user/user-import.ts b/server/core/models/user/user-import.ts index 55c0bc86e..f491f5e71 100644 --- a/server/core/models/user/user-import.ts +++ b/server/core/models/user/user-import.ts @@ -1,6 +1,6 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { MUserImport } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { SequelizeModel } from '../shared/index.js' import { UserModel } from './user.js' import type { UserImportResultSummary, UserImportStateType } from '@peertube/peertube-models' import { getSort } from '../shared/sort.js' @@ -18,7 +18,7 @@ import { USER_IMPORT_STATES } from '@server/initializers/constants.js' } ] }) -export class UserImportModel extends Model>> { +export class UserImportModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/user/user-notification-setting.ts b/server/core/models/user/user-notification-setting.ts index 8d51a1880..2ad3ddfa9 100644 --- a/server/core/models/user/user-notification-setting.ts +++ b/server/core/models/user/user-notification-setting.ts @@ -1,5 +1,4 @@ import { type UserNotificationSetting, type UserNotificationSettingValueType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { TokensCache } from '@server/lib/auth/tokens-cache.js' import { MNotificationSettingFormattable } from '@server/types/models/index.js' import { @@ -11,13 +10,11 @@ import { CreatedAt, Default, ForeignKey, - Is, - Model, - Table, + Is, Table, UpdatedAt } from 'sequelize-typescript' import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications.js' -import { throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, throwIfNotValid } from '../shared/index.js' import { UserModel } from './user.js' @Table({ @@ -29,7 +26,7 @@ import { UserModel } from './user.js' } ] }) -export class UserNotificationSettingModel extends Model>> { +export class UserNotificationSettingModel extends SequelizeModel { @AllowNull(false) @Default(null) diff --git a/server/core/models/user/user-notification.ts b/server/core/models/user/user-notification.ts index a96cb666c..3f0b77711 100644 --- a/server/core/models/user/user-notification.ts +++ b/server/core/models/user/user-notification.ts @@ -1,11 +1,10 @@ import { forceNumber } from '@peertube/peertube-core-utils' import { UserNotification, type UserNotificationType_Type } from '@peertube/peertube-models' import { uuidToShort } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { getBiggestActorImage } from '@server/lib/actor-image.js' import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user/index.js' import { ModelIndexesOptions, Op, WhereOptions } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Table, UpdatedAt } from 'sequelize-typescript' import { isBooleanValid } from '../../helpers/custom-validators/misc.js' import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications.js' import { AbuseModel } from '../abuse/abuse.js' @@ -13,7 +12,7 @@ import { AccountModel } from '../account/account.js' import { ActorFollowModel } from '../actor/actor-follow.js' import { ApplicationModel } from '../application/application.js' import { PluginModel } from '../server/plugin.js' -import { throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, throwIfNotValid } from '../shared/index.js' import { VideoBlacklistModel } from '../video/video-blacklist.js' import { VideoCommentModel } from '../video/video-comment.js' import { VideoImportModel } from '../video/video-import.js' @@ -110,7 +109,7 @@ import { UserModel } from './user.js' } ] as (ModelIndexesOptions & { where?: WhereOptions })[] }) -export class UserNotificationModel extends Model>> { +export class UserNotificationModel extends SequelizeModel { @AllowNull(false) @Default(null) diff --git a/server/core/models/user/user-registration.ts b/server/core/models/user/user-registration.ts index b9c19fa8e..54245b009 100644 --- a/server/core/models/user/user-registration.ts +++ b/server/core/models/user/user-registration.ts @@ -1,5 +1,4 @@ import { UserRegistration, type UserRegistrationStateType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isRegistrationModerationResponseValid, isRegistrationReasonValid, @@ -19,13 +18,11 @@ import { DataType, ForeignKey, Is, - IsEmail, - Model, - Table, + IsEmail, Table, UpdatedAt } from 'sequelize-typescript' import { isUserDisplayNameValid, isUserEmailVerifiedValid, isUserPasswordValid } from '../../helpers/custom-validators/users.js' -import { getSort, parseAggregateResult, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, parseAggregateResult, throwIfNotValid } from '../shared/index.js' import { UserModel } from './user.js' import { forceNumber } from '@peertube/peertube-core-utils' @@ -50,7 +47,7 @@ import { forceNumber } from '@peertube/peertube-core-utils' } ] }) -export class UserRegistrationModel extends Model>> { +export class UserRegistrationModel extends SequelizeModel { @AllowNull(false) @Is('RegistrationState', value => throwIfNotValid(value, isRegistrationStateValid, 'state')) diff --git a/server/core/models/user/user-video-history.ts b/server/core/models/user/user-video-history.ts index 4764f58eb..31de226f8 100644 --- a/server/core/models/user/user-video-history.ts +++ b/server/core/models/user/user-video-history.ts @@ -1,10 +1,10 @@ import { DestroyOptions, Op, Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Table, UpdatedAt } from 'sequelize-typescript' import { ResultList } from '@peertube/peertube-models' import { MUserAccountId, MUserId } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { VideoModel } from '../video/video.js' import { UserModel } from './user.js' +import { SequelizeModel } from '../shared/sequelize-type.js' @Table({ tableName: 'userVideoHistory', @@ -21,7 +21,7 @@ import { UserModel } from './user.js' } ] }) -export class UserVideoHistoryModel extends Model>> { +export class UserVideoHistoryModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/user/user.ts b/server/core/models/user/user.ts index 8a1e201c4..87cd31e0b 100644 --- a/server/core/models/user/user.ts +++ b/server/core/models/user/user.ts @@ -10,7 +10,6 @@ import { type UserAdminFlagType, type UserRoleType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { TokensCache } from '@server/lib/auth/tokens-cache.js' import { LiveQuotaStore } from '@server/lib/live/index.js' import { @@ -37,9 +36,7 @@ import { HasOne, Is, IsEmail, - IsUUID, - Model, - Scopes, + IsUUID, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -70,7 +67,7 @@ import { ActorFollowModel } from '../actor/actor-follow.js' import { ActorImageModel } from '../actor/actor-image.js' import { ActorModel } from '../actor/actor.js' import { OAuthTokenModel } from '../oauth/oauth-token.js' -import { getAdminUsersSort, throwIfNotValid } from '../shared/index.js' +import { getAdminUsersSort, SequelizeModel, throwIfNotValid } from '../shared/index.js' import { VideoChannelModel } from '../video/video-channel.js' import { VideoImportModel } from '../video/video-import.js' import { VideoLiveModel } from '../video/video-live.js' @@ -278,7 +275,7 @@ enum ScopeNames { } ] }) -export class UserModel extends Model>> { +export class UserModel extends SequelizeModel { @AllowNull(true) @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) diff --git a/server/core/models/video/schedule-video-update.ts b/server/core/models/video/schedule-video-update.ts index 229610d87..a74f7c58a 100644 --- a/server/core/models/video/schedule-video-update.ts +++ b/server/core/models/video/schedule-video-update.ts @@ -1,9 +1,9 @@ import { Op, Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { VideoPrivacy } from '@peertube/peertube-models' import { MScheduleVideoUpdate, MScheduleVideoUpdateFormattable } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { VideoModel } from './video.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'scheduleVideoUpdate', @@ -17,7 +17,7 @@ import { VideoModel } from './video.js' } ] }) -export class ScheduleVideoUpdateModel extends Model>> { +export class ScheduleVideoUpdateModel extends SequelizeModel { @AllowNull(false) @Default(null) diff --git a/server/core/models/video/sql/comment/video-comment-list-query-builder.ts b/server/core/models/video/sql/comment/video-comment-list-query-builder.ts index 0a9388fda..5e6207056 100644 --- a/server/core/models/video/sql/comment/video-comment-list-query-builder.ts +++ b/server/core/models/video/sql/comment/video-comment-list-query-builder.ts @@ -23,7 +23,7 @@ export interface ListVideoCommentsOptions { isLocal?: boolean onLocalVideo?: boolean onPublicVideo?: boolean - videoAccountOwnerId?: boolean + videoAccountOwnerId?: number search?: string searchAccount?: string diff --git a/server/core/models/video/storyboard.ts b/server/core/models/video/storyboard.ts index 1d0a8e429..039b24b12 100644 --- a/server/core/models/video/storyboard.ts +++ b/server/core/models/video/storyboard.ts @@ -1,14 +1,14 @@ import { remove } from 'fs-extra/esm' import { join } from 'path' -import { AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { CONFIG } from '@server/initializers/config.js' import { MStoryboard, MStoryboardVideo, MVideo } from '@server/types/models/index.js' import { Storyboard } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { logger } from '../../helpers/logger.js' import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, WEBSERVER } from '../../initializers/constants.js' import { VideoModel } from './video.js' import { Transaction } from 'sequelize' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'storyboard', @@ -23,7 +23,7 @@ import { Transaction } from 'sequelize' } ] }) -export class StoryboardModel extends Model>> { +export class StoryboardModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/video/tag.ts b/server/core/models/video/tag.ts index dee954795..9d5e8b793 100644 --- a/server/core/models/video/tag.ts +++ b/server/core/models/video/tag.ts @@ -1,10 +1,9 @@ import { col, fn, QueryTypes, Transaction } from 'sequelize' -import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Table, UpdatedAt } from 'sequelize-typescript' import { VideoPrivacy, VideoState } from '@peertube/peertube-models' import { MTag } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isVideoTagValid } from '../../helpers/custom-validators/videos.js' -import { throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, throwIfNotValid } from '../shared/index.js' import { VideoTagModel } from './video-tag.js' import { VideoModel } from './video.js' @@ -22,7 +21,7 @@ import { VideoModel } from './video.js' } ] }) -export class TagModel extends Model>> { +export class TagModel extends SequelizeModel { @AllowNull(false) @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) diff --git a/server/core/models/video/thumbnail.ts b/server/core/models/video/thumbnail.ts index 8e06fed96..86faa63a9 100644 --- a/server/core/models/video/thumbnail.ts +++ b/server/core/models/video/thumbnail.ts @@ -1,5 +1,4 @@ import { ActivityIconObject, ThumbnailType, type ThumbnailType_Type } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { afterCommitIfTransaction } from '@server/helpers/database-utils.js' import { MThumbnail, MThumbnailVideo, MVideo, MVideoPlaylist } from '@server/types/models/index.js' import { remove } from 'fs-extra/esm' @@ -14,9 +13,7 @@ import { CreatedAt, DataType, Default, - ForeignKey, - Model, - Table, + ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { logger } from '../../helpers/logger.js' @@ -24,6 +21,7 @@ import { CONFIG } from '../../initializers/config.js' import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, WEBSERVER } from '../../initializers/constants.js' import { VideoPlaylistModel } from './video-playlist.js' import { VideoModel } from './video.js' +import { SequelizeModel } from '../shared/sequelize-type.js' @Table({ tableName: 'thumbnail', @@ -41,7 +39,7 @@ import { VideoModel } from './video.js' } ] }) -export class ThumbnailModel extends Model>> { +export class ThumbnailModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/video/video-blacklist.ts b/server/core/models/video/video-blacklist.ts index 0f6930034..ed143131d 100644 --- a/server/core/models/video/video-blacklist.ts +++ b/server/core/models/video/video-blacklist.ts @@ -1,11 +1,10 @@ import { VideoBlacklist, type VideoBlacklistType_Type } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models/index.js' import { FindOptions } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Table, UpdatedAt } from 'sequelize-typescript' import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist.js' import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js' -import { getBlacklistSort, searchAttribute, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getBlacklistSort, searchAttribute, throwIfNotValid } from '../shared/index.js' import { ThumbnailModel } from './thumbnail.js' import { SummaryOptions, VideoChannelModel, ScopeNames as VideoChannelScopeNames } from './video-channel.js' import { VideoModel } from './video.js' @@ -19,7 +18,7 @@ import { VideoModel } from './video.js' } ] }) -export class VideoBlacklistModel extends Model>> { +export class VideoBlacklistModel extends SequelizeModel { @AllowNull(true) @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) diff --git a/server/core/models/video/video-caption.ts b/server/core/models/video/video-caption.ts index 0d5da3e31..e707e868f 100644 --- a/server/core/models/video/video-caption.ts +++ b/server/core/models/video/video-caption.ts @@ -9,9 +9,7 @@ import { CreatedAt, DataType, ForeignKey, - Is, - Model, - Scopes, + Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -24,12 +22,11 @@ import { MVideoCaptionVideo } from '@server/types/models/index.js' import { buildUUID } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions.js' import { logger } from '../../helpers/logger.js' import { CONFIG } from '../../initializers/config.js' import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants.js' -import { buildWhereIdOrUUID, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, buildWhereIdOrUUID, throwIfNotValid } from '../shared/index.js' import { VideoModel } from './video.js' export enum ScopeNames { @@ -64,7 +61,7 @@ export enum ScopeNames { } ] }) -export class VideoCaptionModel extends Model>> { +export class VideoCaptionModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-change-ownership.ts b/server/core/models/video/video-change-ownership.ts index 152f85a22..7af19d8fc 100644 --- a/server/core/models/video/video-change-ownership.ts +++ b/server/core/models/video/video-change-ownership.ts @@ -1,9 +1,8 @@ import { VideoChangeOwnership, type VideoChangeOwnershipStatusType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership.js' -import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { AccountModel } from '../account/account.js' -import { getSort } from '../shared/index.js' +import { SequelizeModel, getSort } from '../shared/index.js' import { VideoModel, ScopeNames as VideoScopeNames } from './video.js' enum ScopeNames { @@ -54,7 +53,7 @@ enum ScopeNames { ] } })) -export class VideoChangeOwnershipModel extends Model>> { +export class VideoChangeOwnershipModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-channel-sync.ts b/server/core/models/video/video-channel-sync.ts index da2a99593..c33eb9146 100644 --- a/server/core/models/video/video-channel-sync.ts +++ b/server/core/models/video/video-channel-sync.ts @@ -1,5 +1,4 @@ import { VideoChannelSync, VideoChannelSyncState, type VideoChannelSyncStateType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc.js' import { isVideoChannelSyncStateValid } from '@server/helpers/custom-validators/video-channel-syncs.js' import { CONSTRAINTS_FIELDS, VIDEO_CHANNEL_SYNC_STATE } from '@server/initializers/constants.js' @@ -14,13 +13,11 @@ import { Default, DefaultScope, ForeignKey, - Is, - Model, - Table, + Is, Table, UpdatedAt } from 'sequelize-typescript' import { AccountModel } from '../account/account.js' -import { getChannelSyncSort, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getChannelSyncSort, throwIfNotValid } from '../shared/index.js' import { UserModel } from '../user/user.js' import { VideoChannelModel } from './video-channel.js' @@ -40,7 +37,7 @@ import { VideoChannelModel } from './video-channel.js' } ] }) -export class VideoChannelSyncModel extends Model>> { +export class VideoChannelSyncModel extends SequelizeModel { @AllowNull(false) @Default(null) diff --git a/server/core/models/video/video-channel.ts b/server/core/models/video/video-channel.ts index c3abb95f7..115e80b68 100644 --- a/server/core/models/video/video-channel.ts +++ b/server/core/models/video/video-channel.ts @@ -1,6 +1,5 @@ import { forceNumber, pick } from '@peertube/peertube-core-utils' import { ActivityPubActor, VideoChannel, VideoChannelSummary } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { CONFIG } from '@server/initializers/config.js' import { InternalEventEmitter } from '@server/lib/internal-event-emitter.js' import { MAccountHost } from '@server/types/models/index.js' @@ -19,9 +18,7 @@ import { DefaultScope, ForeignKey, HasMany, - Is, - Model, - Scopes, + Is, Scopes, Sequelize, Table, UpdatedAt @@ -47,6 +44,7 @@ import { ActorImageModel } from '../actor/actor-image.js' import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor.js' import { ServerModel } from '../server/server.js' import { + SequelizeModel, buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, @@ -352,7 +350,7 @@ export type SummaryOptions = { } ] }) -export class VideoChannelModel extends Model>> { +export class VideoChannelModel extends SequelizeModel { @AllowNull(false) @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelDisplayNameValid, 'name')) diff --git a/server/core/models/video/video-chapter.ts b/server/core/models/video/video-chapter.ts index 6e59abec9..e515cbc7b 100644 --- a/server/core/models/video/video-chapter.ts +++ b/server/core/models/video/video-chapter.ts @@ -1,10 +1,10 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { MVideo, MVideoChapter } from '@server/types/models/index.js' import { VideoChapter, VideoChapterObject } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { VideoModel } from './video.js' import { Transaction } from 'sequelize' import { getSort } from '../shared/sort.js' +import { SequelizeModel } from '../shared/sequelize-type.js' @Table({ tableName: 'videoChapter', @@ -15,7 +15,7 @@ import { getSort } from '../shared/sort.js' } ] }) -export class VideoChapterModel extends Model>> { +export class VideoChapterModel extends SequelizeModel { @AllowNull(false) @Column diff --git a/server/core/models/video/video-comment.ts b/server/core/models/video/video-comment.ts index 44307dcc6..6ad0d9ac1 100644 --- a/server/core/models/video/video-comment.ts +++ b/server/core/models/video/video-comment.ts @@ -1,4 +1,4 @@ -import { FindOptions, Op, Order, QueryTypes, Sequelize, Transaction } from 'sequelize' +import { Op, Order, QueryTypes, Sequelize, Transaction } from 'sequelize' import { AllowNull, BelongsTo, @@ -7,9 +7,7 @@ import { DataType, ForeignKey, HasMany, - Is, - Model, - Scopes, + Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -18,7 +16,6 @@ import { ActivityTagObject, ActivityTombstoneObject, VideoComment, VideoCommentA import { extractMentions } from '@server/helpers/mentions.js' import { getServerActor } from '@server/models/application/application.js' import { MAccount, MAccountId, MUserAccountId } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js' import { @@ -38,7 +35,7 @@ import { import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse.js' import { AccountModel } from '../account/account.js' import { ActorModel } from '../actor/actor.js' -import { buildLocalAccountIdsIn, buildSQLAttributes, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, buildLocalAccountIdsIn, buildSQLAttributes, throwIfNotValid } from '../shared/index.js' import { ListVideoCommentsOptions, VideoCommentListQueryBuilder } from './sql/comment/video-comment-list-query-builder.js' import { VideoChannelModel } from './video-channel.js' import { VideoModel } from './video.js' @@ -123,7 +120,7 @@ export enum ScopeNames { } ] }) -export class VideoCommentModel extends Model>> { +export class VideoCommentModel extends SequelizeModel { @CreatedAt createdAt: Date @@ -216,46 +213,43 @@ export class VideoCommentModel extends Model { - const query: FindOptions = { + static loadById (id: number, transaction?: Transaction): Promise { + const query = { where: { id - } + }, + transaction } - if (t !== undefined) query.transaction = t - return VideoCommentModel.findOne(query) } - static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction): Promise { - const query: FindOptions = { + static loadByIdAndPopulateVideoAndAccountAndReply (id: number, transaction?: Transaction): Promise { + const query = { where: { id - } + }, + transaction } - if (t !== undefined) query.transaction = t - return VideoCommentModel .scope([ ScopeNames.WITH_VIDEO, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_IN_REPLY_TO ]) .findOne(query) } - static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction): Promise { - const query: FindOptions = { + static loadByUrlAndPopulateAccountAndVideo (url: string, transaction?: Transaction): Promise { + const query = { where: { url - } + }, + transaction } - if (t !== undefined) query.transaction = t - return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query) } - static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction): Promise { - const query: FindOptions = { + static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, transaction?: Transaction): Promise { + const query = { where: { url }, @@ -264,11 +258,10 @@ export class VideoCommentModel extends Model>> { +export class VideoFileModel extends SequelizeModel { @CreatedAt createdAt: Date @@ -257,7 +254,7 @@ export class VideoFileModel extends Model }) RedundancyVideos: Awaited[] - static doesInfohashExistCached = memoizee(VideoFileModel.doesInfohashExist, { + static doesInfohashExistCached = memoizee(VideoFileModel.doesInfohashExist.bind(VideoFileModel), { promise: true, max: MEMOIZE_LENGTH.INFO_HASH_EXISTS, maxAge: MEMOIZE_TTL.INFO_HASH_EXISTS diff --git a/server/core/models/video/video-import.ts b/server/core/models/video/video-import.ts index 3be691c00..9d2f628eb 100644 --- a/server/core/models/video/video-import.ts +++ b/server/core/models/video/video-import.ts @@ -1,5 +1,4 @@ import { VideoImport, VideoImportState, type VideoImportStateType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { afterCommitIfTransaction } from '@server/helpers/database-utils.js' import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import.js' import { IncludeOptions, Op, WhereOptions } from 'sequelize' @@ -13,15 +12,13 @@ import { Default, DefaultScope, ForeignKey, - Is, - Model, - Table, + Is, Table, UpdatedAt } from 'sequelize-typescript' import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports.js' import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos.js' import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants.js' -import { getSort, searchAttribute, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, searchAttribute, throwIfNotValid } from '../shared/index.js' import { UserModel } from '../user/user.js' import { VideoChannelSyncModel } from './video-channel-sync.js' import { VideoModel, ScopeNames as VideoModelScopeNames } from './video.js' @@ -63,7 +60,7 @@ const defaultVideoScope = () => { } ] }) -export class VideoImportModel extends Model>> { +export class VideoImportModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-job-info.ts b/server/core/models/video/video-job-info.ts index 4fd956528..a1e55948b 100644 --- a/server/core/models/video/video-job-info.ts +++ b/server/core/models/video/video-job-info.ts @@ -1,8 +1,8 @@ import { Op, QueryTypes, Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, IsInt, Model, Table, Unique, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, IsInt, Table, Unique, UpdatedAt } from 'sequelize-typescript' import { forceNumber } from '@peertube/peertube-core-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { VideoModel } from './video.js' +import { SequelizeModel } from '../shared/sequelize-type.js' export type VideoJobInfoColumnType = 'pendingMove' | 'pendingTranscode' @@ -20,7 +20,7 @@ export type VideoJobInfoColumnType = 'pendingMove' | 'pendingTranscode' ] }) -export class VideoJobInfoModel extends Model>> { +export class VideoJobInfoModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-live-replay-setting.ts b/server/core/models/video/video-live-replay-setting.ts index 174d0c1f5..9999b7d03 100644 --- a/server/core/models/video/video-live-replay-setting.ts +++ b/server/core/models/video/video-live-replay-setting.ts @@ -2,13 +2,14 @@ import { type VideoPrivacyType } from '@peertube/peertube-models' import { isVideoPrivacyValid } from '@server/helpers/custom-validators/videos.js' import { MLiveReplaySetting } from '@server/types/models/video/video-live-replay-setting.js' import { Transaction } from 'sequelize' -import { AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, Column, CreatedAt, Is, Table, UpdatedAt } from 'sequelize-typescript' import { throwIfNotValid } from '../shared/sequelize-helpers.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'videoLiveReplaySetting' }) -export class VideoLiveReplaySettingModel extends Model { +export class VideoLiveReplaySettingModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-live-session.ts b/server/core/models/video/video-live-session.ts index 33bd40d89..1788de2d0 100644 --- a/server/core/models/video/video-live-session.ts +++ b/server/core/models/video/video-live-session.ts @@ -10,14 +10,13 @@ import { Column, CreatedAt, DataType, - ForeignKey, - Model, - Scopes, + ForeignKey, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { VideoLiveReplaySettingModel } from './video-live-replay-setting.js' import { VideoModel } from './video.js' +import { SequelizeModel } from '../shared/index.js' export enum ScopeNames { WITH_REPLAY = 'WITH_REPLAY' @@ -54,7 +53,7 @@ export enum ScopeNames { } ] }) -export class VideoLiveSessionModel extends Model>> { +export class VideoLiveSessionModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-live.ts b/server/core/models/video/video-live.ts index 10c0d8594..a18e4a6cc 100644 --- a/server/core/models/video/video-live.ts +++ b/server/core/models/video/video-live.ts @@ -1,5 +1,4 @@ import { LiveVideo, VideoState, type LiveVideoLatencyModeType } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { CONFIG } from '@server/initializers/config.js' import { WEBSERVER } from '@server/initializers/constants.js' import { MVideoLive, MVideoLiveVideoWithSetting, MVideoLiveWithSetting } from '@server/types/models/index.js' @@ -12,14 +11,13 @@ import { CreatedAt, DataType, DefaultScope, - ForeignKey, - Model, - Table, + ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { VideoBlacklistModel } from './video-blacklist.js' import { VideoLiveReplaySettingModel } from './video-live-replay-setting.js' import { VideoModel } from './video.js' +import { SequelizeModel } from '../shared/index.js' @DefaultScope(() => ({ include: [ @@ -52,7 +50,7 @@ import { VideoModel } from './video.js' } ] }) -export class VideoLiveModel extends Model>> { +export class VideoLiveModel extends SequelizeModel { @AllowNull(true) @Column(DataType.STRING) diff --git a/server/core/models/video/video-password.ts b/server/core/models/video/video-password.ts index f841f320e..7812701ea 100644 --- a/server/core/models/video/video-password.ts +++ b/server/core/models/video/video-password.ts @@ -1,9 +1,8 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, Is, Table, UpdatedAt } from 'sequelize-typescript' import { VideoModel } from './video.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { ResultList, VideoPassword } from '@peertube/peertube-models' -import { getSort, throwIfNotValid } from '../shared/index.js' -import { FindOptions, Transaction } from 'sequelize' +import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js' +import { Transaction } from 'sequelize' import { MVideoPassword } from '@server/types/models/index.js' import { isPasswordValid } from '@server/helpers/custom-validators/videos.js' import { pick } from '@peertube/peertube-core-utils' @@ -25,7 +24,7 @@ import { pick } from '@peertube/peertube-core-utils' } ] }) -export class VideoPasswordModel extends Model>> { +export class VideoPasswordModel extends SequelizeModel { @AllowNull(false) @Is('VideoPassword', value => throwIfNotValid(value, isPasswordValid, 'videoPassword')) @@ -51,7 +50,7 @@ export class VideoPasswordModel extends Model static async countByVideoId (videoId: number, t?: Transaction) { - const query: FindOptions = { + const query = { where: { videoId }, @@ -63,7 +62,7 @@ export class VideoPasswordModel extends Model { const { id, videoId, t } = options - const query: FindOptions = { + const query = { where: { id, videoId diff --git a/server/core/models/video/video-playlist-element.ts b/server/core/models/video/video-playlist-element.ts index 6190d0c18..ddb818eb5 100644 --- a/server/core/models/video/video-playlist-element.ts +++ b/server/core/models/video/video-playlist-element.ts @@ -9,9 +9,7 @@ import { ForeignKey, Is, IsInt, - Min, - Model, - Table, + Min, Table, UpdatedAt } from 'sequelize-typescript' import validator from 'validator' @@ -32,11 +30,10 @@ import { MVideoPlaylistElementVideoThumbnail, MVideoPlaylistElementVideoUrl } from '@server/types/models/video/video-playlist-element.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js' import { AccountModel } from '../account/account.js' -import { getSort, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js' import { VideoPlaylistModel } from './video-playlist.js' import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video.js' @@ -55,7 +52,7 @@ import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './vide } ] }) -export class VideoPlaylistElementModel extends Model>> { +export class VideoPlaylistElementModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-playlist.ts b/server/core/models/video/video-playlist.ts index bea5a10e2..a6b2d4e73 100644 --- a/server/core/models/video/video-playlist.ts +++ b/server/core/models/video/video-playlist.ts @@ -9,7 +9,6 @@ import { type VideoPlaylistType_Type } from '@peertube/peertube-models' import { buildUUID, uuidToShort } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { activityPubCollectionPagination } from '@server/lib/activitypub/collection.js' import { MAccountId, MChannelId } from '@server/types/models/index.js' import { join } from 'path' @@ -25,9 +24,7 @@ import { HasMany, HasOne, Is, - IsUUID, - Model, - Scopes, + IsUUID, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -58,6 +55,7 @@ import { import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account.js' import { ActorModel } from '../actor/actor.js' import { + SequelizeModel, buildServerIdsFollowedBy, buildTrigramSearchIndex, buildWhereIdOrUUID, @@ -287,7 +285,7 @@ function getVideoLengthSelect () { } ] }) -export class VideoPlaylistModel extends Model>> { +export class VideoPlaylistModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-share.ts b/server/core/models/video/video-share.ts index a3f571fed..be367595c 100644 --- a/server/core/models/video/video-share.ts +++ b/server/core/models/video/video-share.ts @@ -1,13 +1,12 @@ import { literal, Op, QueryTypes, Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { forceNumber } from '@peertube/peertube-core-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js' import { MActorDefault, MActorFollowersUrl, MActorId } from '../../types/models/index.js' import { MVideoShareActor, MVideoShareFull } from '../../types/models/video/index.js' import { ActorModel } from '../actor/actor.js' -import { buildLocalActorIdsIn, throwIfNotValid } from '../shared/index.js' +import { buildLocalActorIdsIn, SequelizeModel, throwIfNotValid } from '../shared/index.js' import { VideoModel } from './video.js' enum ScopeNames { @@ -52,7 +51,7 @@ enum ScopeNames { } ] }) -export class VideoShareModel extends Model>> { +export class VideoShareModel extends SequelizeModel { @AllowNull(false) @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) diff --git a/server/core/models/video/video-source.ts b/server/core/models/video/video-source.ts index 32743e796..a336b83d1 100644 --- a/server/core/models/video/video-source.ts +++ b/server/core/models/video/video-source.ts @@ -1,8 +1,7 @@ import { Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { VideoSource } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' -import { getSort } from '../shared/index.js' +import { SequelizeModel, getSort } from '../shared/index.js' import { VideoModel } from './video.js' @Table({ @@ -16,7 +15,7 @@ import { VideoModel } from './video.js' } ] }) -export class VideoSourceModel extends Model>> { +export class VideoSourceModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video-streaming-playlist.ts b/server/core/models/video/video-streaming-playlist.ts index c06708052..2f6fb2f1c 100644 --- a/server/core/models/video/video-streaming-playlist.ts +++ b/server/core/models/video/video-streaming-playlist.ts @@ -5,7 +5,6 @@ import { type VideoStreamingPlaylistType_Type } from '@peertube/peertube-models' import { sha1 } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { CONFIG } from '@server/initializers/config.js' import { getHLSPrivateFileUrl, getHLSPublicFileUrl } from '@server/lib/object-storage/index.js' import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '@server/lib/paths.js' @@ -24,9 +23,7 @@ import { Default, ForeignKey, HasMany, - Is, - Model, - Table, + Is, Table, UpdatedAt } from 'sequelize-typescript' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' @@ -41,7 +38,7 @@ import { WEBSERVER } from '../../initializers/constants.js' import { VideoRedundancyModel } from '../redundancy/video-redundancy.js' -import { doesExist, throwIfNotValid } from '../shared/index.js' +import { SequelizeModel, doesExist, throwIfNotValid } from '../shared/index.js' import { VideoModel } from './video.js' @Table({ @@ -60,7 +57,7 @@ import { VideoModel } from './video.js' } ] }) -export class VideoStreamingPlaylistModel extends Model>> { +export class VideoStreamingPlaylistModel extends SequelizeModel { @CreatedAt createdAt: Date @@ -132,7 +129,7 @@ export class VideoStreamingPlaylistModel extends Model[] - static doesInfohashExistCached = memoizee(VideoStreamingPlaylistModel.doesInfohashExist, { + static doesInfohashExistCached = memoizee(VideoStreamingPlaylistModel.doesInfohashExist.bind(VideoStreamingPlaylistModel), { promise: true, max: MEMOIZE_LENGTH.INFO_HASH_EXISTS, maxAge: MEMOIZE_TTL.INFO_HASH_EXISTS diff --git a/server/core/models/video/video-tag.ts b/server/core/models/video/video-tag.ts index 5c36a8de5..1b5540d2b 100644 --- a/server/core/models/video/video-tag.ts +++ b/server/core/models/video/video-tag.ts @@ -1,7 +1,7 @@ -import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { Column, CreatedAt, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { TagModel } from './tag.js' import { VideoModel } from './video.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'videoTag', @@ -14,7 +14,7 @@ import { VideoModel } from './video.js' } ] }) -export class VideoTagModel extends Model>> { +export class VideoTagModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/video/video.ts b/server/core/models/video/video.ts index f96ca5346..88af4f429 100644 --- a/server/core/models/video/video.ts +++ b/server/core/models/video/video.ts @@ -19,7 +19,6 @@ import { type VideoStateType } from '@peertube/peertube-models' import { uuidToShort } from '@peertube/peertube-node-utils' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video.js' import { InternalEventEmitter } from '@server/lib/internal-event-emitter.js' import { LiveManager } from '@server/lib/live/live-manager.js' @@ -58,9 +57,7 @@ import { Is, IsInt, IsUUID, - Min, - Model, - Scopes, + Min, Scopes, Table, UpdatedAt } from 'sequelize-typescript' @@ -118,7 +115,15 @@ import { VideoRedundancyModel } from '../redundancy/video-redundancy.js' import { ServerModel } from '../server/server.js' import { TrackerModel } from '../server/tracker.js' import { VideoTrackerModel } from '../server/video-tracker.js' -import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, setAsUpdated, throwIfNotValid } from '../shared/index.js' +import { + SequelizeModel, + buildTrigramSearchIndex, + buildWhereIdOrUUID, + getVideoSort, + isOutdated, + setAsUpdated, + throwIfNotValid +} from '../shared/index.js' import { UserVideoHistoryModel } from '../user/user-video-history.js' import { UserModel } from '../user/user.js' import { VideoViewModel } from '../view/video-view.js' @@ -452,7 +457,7 @@ export type ForAPIOptions = { } ] }) -export class VideoModel extends Model>> { +export class VideoModel extends SequelizeModel { @AllowNull(false) @Default(DataType.UUIDV4) diff --git a/server/core/models/view/local-video-viewer-watch-section.ts b/server/core/models/view/local-video-viewer-watch-section.ts index eeb7fafd3..ceccde043 100644 --- a/server/core/models/view/local-video-viewer-watch-section.ts +++ b/server/core/models/view/local-video-viewer-watch-section.ts @@ -1,8 +1,8 @@ import { Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Table } from 'sequelize-typescript' import { MLocalVideoViewerWatchSection } from '@server/types/models/index.js' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { LocalVideoViewerModel } from './local-video-viewer.js' +import { SequelizeModel } from '../shared/index.js' @Table({ tableName: 'localVideoViewerWatchSection', @@ -13,7 +13,7 @@ import { LocalVideoViewerModel } from './local-video-viewer.js' } ] }) -export class LocalVideoViewerWatchSectionModel extends Model>> { +export class LocalVideoViewerWatchSectionModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/view/local-video-viewer.ts b/server/core/models/view/local-video-viewer.ts index e55b7aa39..2c7052806 100644 --- a/server/core/models/view/local-video-viewer.ts +++ b/server/core/models/view/local-video-viewer.ts @@ -1,5 +1,5 @@ import { QueryTypes } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, IsUUID, Model, Table } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, IsUUID, Table } from 'sequelize-typescript' import { getActivityStreamDuration } from '@server/lib/activitypub/activity.js' import { buildGroupByAndBoundaries } from '@server/lib/timeserie.js' import { MLocalVideoViewer, MLocalVideoViewerWithWatchSections, MVideo } from '@server/types/models/index.js' @@ -10,9 +10,9 @@ import { VideoStatsTimeserieMetric, WatchActionObject } from '@peertube/peertube-models' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' import { VideoModel } from '../video/video.js' import { LocalVideoViewerWatchSectionModel } from './local-video-viewer-watch-section.js' +import { SequelizeModel } from '../shared/index.js' /** * @@ -34,7 +34,7 @@ import { LocalVideoViewerWatchSectionModel } from './local-video-viewer-watch-se } ] }) -export class LocalVideoViewerModel extends Model>> { +export class LocalVideoViewerModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/core/models/view/video-view.ts b/server/core/models/view/video-view.ts index a7aaccf26..f19b103d2 100644 --- a/server/core/models/view/video-view.ts +++ b/server/core/models/view/video-view.ts @@ -1,7 +1,7 @@ import { literal, Op } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table } from 'sequelize-typescript' -import { AttributesOnly } from '@peertube/peertube-typescript-utils' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table } from 'sequelize-typescript' import { VideoModel } from '../video/video.js' +import { SequelizeModel } from '../shared/index.js' /** * @@ -22,7 +22,7 @@ import { VideoModel } from '../video/video.js' } ] }) -export class VideoViewModel extends Model>> { +export class VideoViewModel extends SequelizeModel { @CreatedAt createdAt: Date diff --git a/server/scripts/parse-log.ts b/server/scripts/parse-log.ts index e80c0d927..1eef1260c 100755 --- a/server/scripts/parse-log.ts +++ b/server/scripts/parse-log.ts @@ -14,7 +14,7 @@ program .option('-l, --level [level]', 'Level log (debug/info/warn/error)') .option('-f, --files [file...]', 'Files to parse. If not provided, the script will parse the latest log file from config)') .option('-t, --tags [tags...]', 'Display only lines with these tags') - .option('-nt, --not-tags [tags...]', 'Donrt display lines containing these tags') + .option('-nt, --not-tags [tags...]', 'Do not display lines containing these tags') .parse(process.argv) const options = program.opts()