]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/initializers/database.ts
Feature/Add replay privacy (#5692)
[github/Chocobozzz/PeerTube.git] / server / initializers / database.ts
CommitLineData
d95d1559 1import { QueryTypes, Transaction } from 'sequelize'
3fd3ab2d 2import { Sequelize as SequelizeTypescript } from 'sequelize-typescript'
b2111066 3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
f4796856
C
4import { TrackerModel } from '@server/models/server/tracker'
5import { VideoTrackerModel } from '@server/models/server/video-tracker'
7d9ba5c0
C
6import { UserModel } from '@server/models/user/user'
7import { UserNotificationModel } from '@server/models/user/user-notification'
e364e31e 8import { UserRegistrationModel } from '@server/models/user/user-registration'
7d9ba5c0 9import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
e364e31e 10import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync'
b2111066 11import { VideoJobInfoModel } from '@server/models/video/video-job-info'
26e3e98f 12import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
9452d4fd 13import { VideoSourceModel } from '@server/models/video/video-source'
b2111066
C
14import { LocalVideoViewerModel } from '@server/models/view/local-video-viewer'
15import { LocalVideoViewerWatchSectionModel } from '@server/models/view/local-video-viewer-watch-section'
9452d4fd 16import { isTestOrDevInstance } from '../helpers/core-utils'
3fd3ab2d 17import { logger } from '../helpers/logger'
c6c0fa6c
C
18import { AbuseModel } from '../models/abuse/abuse'
19import { AbuseMessageModel } from '../models/abuse/abuse-message'
20import { VideoAbuseModel } from '../models/abuse/video-abuse'
21import { VideoCommentAbuseModel } from '../models/abuse/video-comment-abuse'
3fd3ab2d 22import { AccountModel } from '../models/account/account'
d95d1559 23import { AccountBlocklistModel } from '../models/account/account-blocklist'
3fd3ab2d 24import { AccountVideoRateModel } from '../models/account/account-video-rate'
7d9ba5c0
C
25import { ActorModel } from '../models/actor/actor'
26import { ActorFollowModel } from '../models/actor/actor-follow'
27import { ActorImageModel } from '../models/actor/actor-image'
3fd3ab2d 28import { ApplicationModel } from '../models/application/application'
3fd3ab2d
C
29import { OAuthClientModel } from '../models/oauth/oauth-client'
30import { OAuthTokenModel } from '../models/oauth/oauth-token'
d95d1559
C
31import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
32import { PluginModel } from '../models/server/plugin'
3fd3ab2d 33import { ServerModel } from '../models/server/server'
d95d1559 34import { ServerBlocklistModel } from '../models/server/server-blocklist'
7d9ba5c0 35import { UserNotificationSettingModel } from '../models/user/user-notification-setting'
d95d1559 36import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update'
3fd3ab2d 37import { TagModel } from '../models/video/tag'
d95d1559 38import { ThumbnailModel } from '../models/video/thumbnail'
3fd3ab2d 39import { VideoModel } from '../models/video/video'
3fd3ab2d 40import { VideoBlacklistModel } from '../models/video/video-blacklist'
d95d1559
C
41import { VideoCaptionModel } from '../models/video/video-caption'
42import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership'
3fd3ab2d 43import { VideoChannelModel } from '../models/video/video-channel'
6d852470 44import { VideoCommentModel } from '../models/video/video-comment'
3fd3ab2d 45import { VideoFileModel } from '../models/video/video-file'
fbad87b0 46import { VideoImportModel } from '../models/video/video-import'
c6c0fa6c 47import { VideoLiveModel } from '../models/video/video-live'
418d092a
C
48import { VideoPlaylistModel } from '../models/video/video-playlist'
49import { VideoPlaylistElementModel } from '../models/video/video-playlist-element'
d95d1559
C
50import { VideoShareModel } from '../models/video/video-share'
51import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
52import { VideoTagModel } from '../models/video/video-tag'
b2111066 53import { VideoViewModel } from '../models/view/video-view'
d95d1559 54import { CONFIG } from './config'
05a60d85 55import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting'
fdbda9e3 56
3fd3ab2d 57require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
8c308c2b 58
65fcc311
C
59const dbname = CONFIG.DATABASE.DBNAME
60const username = CONFIG.DATABASE.USERNAME
61const password = CONFIG.DATABASE.PASSWORD
228077ef
C
62const host = CONFIG.DATABASE.HOSTNAME
63const port = CONFIG.DATABASE.PORT
1c3386e8 64const poolMax = CONFIG.DATABASE.POOL.MAX
8c308c2b 65
6bc672da
C
66let dialectOptions: any = {}
67
68if (CONFIG.DATABASE.SSL) {
69 dialectOptions = {
70 ssl: {
71 rejectUnauthorized: false
72 }
73 }
74}
75
3fd3ab2d
C
76const sequelizeTypescript = new SequelizeTypescript({
77 database: dbname,
feb4bdfd 78 dialect: 'postgres',
6bc672da 79 dialectOptions,
228077ef
C
80 host,
81 port,
3fd3ab2d
C
82 username,
83 password,
1c3386e8
RK
84 pool: {
85 max: poolMax
86 },
9452d4fd 87 benchmark: isTestOrDevInstance(),
1735c825 88 isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE,
075f16ca 89 logging: (message: string, benchmark: number) => {
165cdc75
C
90 if (process.env.NODE_DB_LOG === 'false') return
91
2a6cf69c 92 let newMessage = 'Executed SQL request'
9452d4fd 93 if (isTestOrDevInstance() === true && benchmark !== undefined) {
2a6cf69c 94 newMessage += ' in ' + benchmark + 'ms'
7920c273
C
95 }
96
452b3bea 97 logger.debug(newMessage, { sql: message, tags: [ 'sql' ] })
7920c273 98 }
feb4bdfd
C
99})
100
74055dc8
C
101function checkDatabaseConnectionOrDie () {
102 sequelizeTypescript.authenticate()
103 .then(() => logger.debug('Connection to PostgreSQL has been established successfully.'))
104 .catch(err => {
105
106 logger.error('Unable to connect to PostgreSQL database.', { err })
107 process.exit(-1)
108 })
109}
4f24f16e 110
91fea9fc 111async function initDatabaseModels (silent: boolean) {
3fd3ab2d
C
112 sequelizeTypescript.addModels([
113 ApplicationModel,
50d6de9c
C
114 ActorModel,
115 ActorFollowModel,
f4796856 116 ActorImageModel,
3fd3ab2d 117 AccountModel,
3fd3ab2d
C
118 OAuthClientModel,
119 OAuthTokenModel,
120 ServerModel,
121 TagModel,
122 AccountVideoRateModel,
3fd3ab2d 123 UserModel,
edbc9325 124 AbuseMessageModel,
d95d1559
C
125 AbuseModel,
126 VideoCommentAbuseModel,
3fd3ab2d 127 VideoAbuseModel,
b876eaf1 128 VideoModel,
74d63469 129 VideoChangeOwnershipModel,
3fd3ab2d 130 VideoChannelModel,
3fd3ab2d
C
131 VideoShareModel,
132 VideoFileModel,
2e401e85 133 VideoSourceModel,
40e87e9e 134 VideoCaptionModel,
3fd3ab2d
C
135 VideoBlacklistModel,
136 VideoTagModel,
2baea0c7 137 VideoCommentModel,
fbad87b0 138 ScheduleVideoUpdateModel,
6b616860 139 VideoImportModel,
c48e82b5 140 VideoViewModel,
6e46de09 141 VideoRedundancyModel,
7ad9b984 142 UserVideoHistoryModel,
c6c0fa6c 143 VideoLiveModel,
26e3e98f 144 VideoLiveSessionModel,
05a60d85 145 VideoLiveReplaySettingModel,
7ad9b984 146 AccountBlocklistModel,
cef534ed
C
147 ServerBlocklistModel,
148 UserNotificationModel,
09209296 149 UserNotificationSettingModel,
418d092a
C
150 VideoStreamingPlaylistModel,
151 VideoPlaylistModel,
e8bafea3 152 VideoPlaylistElementModel,
b2111066
C
153 LocalVideoViewerModel,
154 LocalVideoViewerWatchSectionModel,
f023a19c 155 ThumbnailModel,
d9a2a031
C
156 TrackerModel,
157 VideoTrackerModel,
2539932e 158 PluginModel,
0305db28 159 ActorCustomPageModel,
2a491182 160 VideoJobInfoModel,
e364e31e
C
161 VideoChannelSyncModel,
162 UserRegistrationModel
3fd3ab2d 163 ])
b769007f 164
57c36b27
C
165 // Check extensions exist in the database
166 await checkPostgresExtensions()
167
168 // Create custom PostgreSQL functions
169 await createFunctions()
170
f5028693 171 if (!silent) logger.info('Database %s is ready.', dbname)
b769007f 172}
65fcc311
C
173
174// ---------------------------------------------------------------------------
175
e02643f3 176export {
91fea9fc 177 initDatabaseModels,
74055dc8 178 checkDatabaseConnectionOrDie,
3fd3ab2d 179 sequelizeTypescript
74889a71 180}
57c36b27
C
181
182// ---------------------------------------------------------------------------
183
184async function checkPostgresExtensions () {
0b2f03d3
C
185 const promises = [
186 checkPostgresExtension('pg_trgm'),
187 checkPostgresExtension('unaccent')
57c36b27
C
188 ]
189
0b2f03d3
C
190 return Promise.all(promises)
191}
192
193async function checkPostgresExtension (extension: string) {
3acc5084 194 const query = `SELECT 1 FROM pg_available_extensions WHERE name = '${extension}' AND installed_version IS NOT NULL;`
1735c825
C
195 const options = {
196 type: QueryTypes.SELECT as QueryTypes.SELECT,
197 raw: true
198 }
199
3acc5084 200 const res = await sequelizeTypescript.query<object>(query, options)
57c36b27 201
3acc5084 202 if (!res || res.length === 0) {
1735c825 203 // Try to create the extension ourselves
0b2f03d3
C
204 try {
205 await sequelizeTypescript.query(`CREATE EXTENSION ${extension};`, { raw: true })
57c36b27 206
0b2f03d3
C
207 } catch {
208 const errorMessage = `You need to enable ${extension} extension in PostgreSQL. ` +
209 `You can do so by running 'CREATE EXTENSION ${extension};' as a PostgreSQL super user in ${CONFIG.DATABASE.DBNAME} database.`
210 throw new Error(errorMessage)
57c36b27
C
211 }
212 }
213}
214
f0ad4710 215function createFunctions () {
2cebd797
C
216 const query = `CREATE OR REPLACE FUNCTION immutable_unaccent(text)
217 RETURNS text AS
218$func$
219SELECT public.unaccent('public.unaccent', $1::text)
220$func$ LANGUAGE sql IMMUTABLE;`
57c36b27
C
221
222 return sequelizeTypescript.query(query, { raw: true })
223}