]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/initializers/database.ts
Feature/Add replay privacy (#5692)
[github/Chocobozzz/PeerTube.git] / server / initializers / database.ts
... / ...
CommitLineData
1import { QueryTypes, Transaction } from 'sequelize'
2import { Sequelize as SequelizeTypescript } from 'sequelize-typescript'
3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
4import { TrackerModel } from '@server/models/server/tracker'
5import { VideoTrackerModel } from '@server/models/server/video-tracker'
6import { UserModel } from '@server/models/user/user'
7import { UserNotificationModel } from '@server/models/user/user-notification'
8import { UserRegistrationModel } from '@server/models/user/user-registration'
9import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
10import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync'
11import { VideoJobInfoModel } from '@server/models/video/video-job-info'
12import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
13import { VideoSourceModel } from '@server/models/video/video-source'
14import { LocalVideoViewerModel } from '@server/models/view/local-video-viewer'
15import { LocalVideoViewerWatchSectionModel } from '@server/models/view/local-video-viewer-watch-section'
16import { isTestOrDevInstance } from '../helpers/core-utils'
17import { logger } from '../helpers/logger'
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'
22import { AccountModel } from '../models/account/account'
23import { AccountBlocklistModel } from '../models/account/account-blocklist'
24import { AccountVideoRateModel } from '../models/account/account-video-rate'
25import { ActorModel } from '../models/actor/actor'
26import { ActorFollowModel } from '../models/actor/actor-follow'
27import { ActorImageModel } from '../models/actor/actor-image'
28import { ApplicationModel } from '../models/application/application'
29import { OAuthClientModel } from '../models/oauth/oauth-client'
30import { OAuthTokenModel } from '../models/oauth/oauth-token'
31import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
32import { PluginModel } from '../models/server/plugin'
33import { ServerModel } from '../models/server/server'
34import { ServerBlocklistModel } from '../models/server/server-blocklist'
35import { UserNotificationSettingModel } from '../models/user/user-notification-setting'
36import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update'
37import { TagModel } from '../models/video/tag'
38import { ThumbnailModel } from '../models/video/thumbnail'
39import { VideoModel } from '../models/video/video'
40import { VideoBlacklistModel } from '../models/video/video-blacklist'
41import { VideoCaptionModel } from '../models/video/video-caption'
42import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership'
43import { VideoChannelModel } from '../models/video/video-channel'
44import { VideoCommentModel } from '../models/video/video-comment'
45import { VideoFileModel } from '../models/video/video-file'
46import { VideoImportModel } from '../models/video/video-import'
47import { VideoLiveModel } from '../models/video/video-live'
48import { VideoPlaylistModel } from '../models/video/video-playlist'
49import { VideoPlaylistElementModel } from '../models/video/video-playlist-element'
50import { VideoShareModel } from '../models/video/video-share'
51import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
52import { VideoTagModel } from '../models/video/video-tag'
53import { VideoViewModel } from '../models/view/video-view'
54import { CONFIG } from './config'
55import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting'
56
57require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
58
59const dbname = CONFIG.DATABASE.DBNAME
60const username = CONFIG.DATABASE.USERNAME
61const password = CONFIG.DATABASE.PASSWORD
62const host = CONFIG.DATABASE.HOSTNAME
63const port = CONFIG.DATABASE.PORT
64const poolMax = CONFIG.DATABASE.POOL.MAX
65
66let dialectOptions: any = {}
67
68if (CONFIG.DATABASE.SSL) {
69 dialectOptions = {
70 ssl: {
71 rejectUnauthorized: false
72 }
73 }
74}
75
76const sequelizeTypescript = new SequelizeTypescript({
77 database: dbname,
78 dialect: 'postgres',
79 dialectOptions,
80 host,
81 port,
82 username,
83 password,
84 pool: {
85 max: poolMax
86 },
87 benchmark: isTestOrDevInstance(),
88 isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE,
89 logging: (message: string, benchmark: number) => {
90 if (process.env.NODE_DB_LOG === 'false') return
91
92 let newMessage = 'Executed SQL request'
93 if (isTestOrDevInstance() === true && benchmark !== undefined) {
94 newMessage += ' in ' + benchmark + 'ms'
95 }
96
97 logger.debug(newMessage, { sql: message, tags: [ 'sql' ] })
98 }
99})
100
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}
110
111async function initDatabaseModels (silent: boolean) {
112 sequelizeTypescript.addModels([
113 ApplicationModel,
114 ActorModel,
115 ActorFollowModel,
116 ActorImageModel,
117 AccountModel,
118 OAuthClientModel,
119 OAuthTokenModel,
120 ServerModel,
121 TagModel,
122 AccountVideoRateModel,
123 UserModel,
124 AbuseMessageModel,
125 AbuseModel,
126 VideoCommentAbuseModel,
127 VideoAbuseModel,
128 VideoModel,
129 VideoChangeOwnershipModel,
130 VideoChannelModel,
131 VideoShareModel,
132 VideoFileModel,
133 VideoSourceModel,
134 VideoCaptionModel,
135 VideoBlacklistModel,
136 VideoTagModel,
137 VideoCommentModel,
138 ScheduleVideoUpdateModel,
139 VideoImportModel,
140 VideoViewModel,
141 VideoRedundancyModel,
142 UserVideoHistoryModel,
143 VideoLiveModel,
144 VideoLiveSessionModel,
145 VideoLiveReplaySettingModel,
146 AccountBlocklistModel,
147 ServerBlocklistModel,
148 UserNotificationModel,
149 UserNotificationSettingModel,
150 VideoStreamingPlaylistModel,
151 VideoPlaylistModel,
152 VideoPlaylistElementModel,
153 LocalVideoViewerModel,
154 LocalVideoViewerWatchSectionModel,
155 ThumbnailModel,
156 TrackerModel,
157 VideoTrackerModel,
158 PluginModel,
159 ActorCustomPageModel,
160 VideoJobInfoModel,
161 VideoChannelSyncModel,
162 UserRegistrationModel
163 ])
164
165 // Check extensions exist in the database
166 await checkPostgresExtensions()
167
168 // Create custom PostgreSQL functions
169 await createFunctions()
170
171 if (!silent) logger.info('Database %s is ready.', dbname)
172}
173
174// ---------------------------------------------------------------------------
175
176export {
177 initDatabaseModels,
178 checkDatabaseConnectionOrDie,
179 sequelizeTypescript
180}
181
182// ---------------------------------------------------------------------------
183
184async function checkPostgresExtensions () {
185 const promises = [
186 checkPostgresExtension('pg_trgm'),
187 checkPostgresExtension('unaccent')
188 ]
189
190 return Promise.all(promises)
191}
192
193async function checkPostgresExtension (extension: string) {
194 const query = `SELECT 1 FROM pg_available_extensions WHERE name = '${extension}' AND installed_version IS NOT NULL;`
195 const options = {
196 type: QueryTypes.SELECT as QueryTypes.SELECT,
197 raw: true
198 }
199
200 const res = await sequelizeTypescript.query<object>(query, options)
201
202 if (!res || res.length === 0) {
203 // Try to create the extension ourselves
204 try {
205 await sequelizeTypescript.query(`CREATE EXTENSION ${extension};`, { raw: true })
206
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)
211 }
212 }
213}
214
215function createFunctions () {
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;`
221
222 return sequelizeTypescript.query(query, { raw: true })
223}