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