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