03c1cec7b77dfc0141f4a0613dba02c672b97dd3
[oweals/peertube.git] / server / controllers / api / config.ts
1 import * as express from 'express'
2 import { omit } from 'lodash'
3 import { ServerConfig, UserRight } from '../../../shared'
4 import { About } from '../../../shared/models/server/about.model'
5 import { CustomConfig } from '../../../shared/models/server/custom-config.model'
6 import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup'
7 import { CONFIG, CONSTRAINTS_FIELDS, reloadConfig } from '../../initializers'
8 import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
9 import { customConfigUpdateValidator } from '../../middlewares/validators/config'
10 import { ClientHtml } from '../../lib/client-html'
11 import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger'
12 import { remove, writeJSON } from 'fs-extra'
13 import { getVersion } from '../../helpers/utils'
14
15 const packageJSON = require('../../../../package.json')
16 const configRouter = express.Router()
17
18 const auditLogger = auditLoggerFactory('config')
19
20 configRouter.get('/about', getAbout)
21 configRouter.get('/',
22   asyncMiddleware(getConfig)
23 )
24
25 configRouter.get('/custom',
26   authenticate,
27   ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
28   asyncMiddleware(getCustomConfig)
29 )
30 configRouter.put('/custom',
31   authenticate,
32   ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
33   asyncMiddleware(customConfigUpdateValidator),
34   asyncMiddleware(updateCustomConfig)
35 )
36 configRouter.delete('/custom',
37   authenticate,
38   ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
39   asyncMiddleware(deleteCustomConfig)
40 )
41
42 let serverCommit: string
43 async function getConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
44   const allowed = await isSignupAllowed()
45   const allowedForCurrentIP = isSignupAllowedForCurrentIP(req.ip)
46   serverCommit = (serverCommit) ? serverCommit : await getVersion()
47   if (serverCommit === packageJSON.version) serverCommit = ''
48
49   const enabledResolutions = Object.keys(CONFIG.TRANSCODING.RESOLUTIONS)
50    .filter(key => CONFIG.TRANSCODING.ENABLED === CONFIG.TRANSCODING.RESOLUTIONS[key] === true)
51    .map(r => parseInt(r, 10))
52
53   const json: ServerConfig = {
54     instance: {
55       name: CONFIG.INSTANCE.NAME,
56       shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
57       defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
58       defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
59       customizations: {
60         javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT,
61         css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS
62       }
63     },
64     serverVersion: packageJSON.version,
65     serverCommit,
66     signup: {
67       allowed,
68       allowedForCurrentIP,
69       requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
70     },
71     transcoding: {
72       enabledResolutions
73     },
74     import: {
75       videos: {
76         http: {
77           enabled: CONFIG.IMPORT.VIDEOS.HTTP.ENABLED
78         },
79         torrent: {
80           enabled: CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED
81         }
82       }
83     },
84     avatar: {
85       file: {
86         size: {
87           max: CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max
88         },
89         extensions: CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME
90       }
91     },
92     video: {
93       image: {
94         extensions: CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME,
95         size: {
96           max: CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max
97         }
98       },
99       file: {
100         extensions: CONSTRAINTS_FIELDS.VIDEOS.EXTNAME
101       }
102     },
103     videoCaption: {
104       file: {
105         size: {
106           max: CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.FILE_SIZE.max
107         },
108         extensions: CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.EXTNAME
109       }
110     },
111     user: {
112       videoQuota: CONFIG.USER.VIDEO_QUOTA,
113       videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY
114     }
115   }
116
117   return res.json(json)
118 }
119
120 function getAbout (req: express.Request, res: express.Response, next: express.NextFunction) {
121   const about: About = {
122     instance: {
123       name: CONFIG.INSTANCE.NAME,
124       shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
125       description: CONFIG.INSTANCE.DESCRIPTION,
126       terms: CONFIG.INSTANCE.TERMS
127     }
128   }
129
130   return res.json(about).end()
131 }
132
133 async function getCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
134   const data = customConfig()
135
136   return res.json(data).end()
137 }
138
139 async function deleteCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
140   await remove(CONFIG.CUSTOM_FILE)
141
142   auditLogger.delete(getAuditIdFromRes(res), new CustomConfigAuditView(customConfig()))
143
144   reloadConfig()
145   ClientHtml.invalidCache()
146
147   const data = customConfig()
148
149   return res.json(data).end()
150 }
151
152 async function updateCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
153   const toUpdate: CustomConfig = req.body
154   const oldCustomConfigAuditKeys = new CustomConfigAuditView(customConfig())
155
156   // Force number conversion
157   toUpdate.cache.previews.size = parseInt('' + toUpdate.cache.previews.size, 10)
158   toUpdate.cache.captions.size = parseInt('' + toUpdate.cache.captions.size, 10)
159   toUpdate.signup.limit = parseInt('' + toUpdate.signup.limit, 10)
160   toUpdate.user.videoQuota = parseInt('' + toUpdate.user.videoQuota, 10)
161   toUpdate.user.videoQuotaDaily = parseInt('' + toUpdate.user.videoQuotaDaily, 10)
162   toUpdate.transcoding.threads = parseInt('' + toUpdate.transcoding.threads, 10)
163
164   // camelCase to snake_case key
165   const toUpdateJSON = omit(
166     toUpdate,
167     'user.videoQuota',
168     'instance.defaultClientRoute',
169     'instance.shortDescription',
170     'cache.videoCaptions',
171     'signup.requiresEmailVerification'
172   )
173   toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota
174   toUpdateJSON.user['video_quota_daily'] = toUpdate.user.videoQuotaDaily
175   toUpdateJSON.instance['default_client_route'] = toUpdate.instance.defaultClientRoute
176   toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription
177   toUpdateJSON.instance['default_nsfw_policy'] = toUpdate.instance.defaultNSFWPolicy
178   toUpdateJSON.signup['requires_email_verification'] = toUpdate.signup.requiresEmailVerification
179
180   await writeJSON(CONFIG.CUSTOM_FILE, toUpdateJSON, { spaces: 2 })
181
182   reloadConfig()
183   ClientHtml.invalidCache()
184
185   const data = customConfig()
186
187   auditLogger.update(
188     getAuditIdFromRes(res),
189     new CustomConfigAuditView(data),
190     oldCustomConfigAuditKeys
191   )
192
193   return res.json(data).end()
194 }
195
196 // ---------------------------------------------------------------------------
197
198 export {
199   configRouter
200 }
201
202 // ---------------------------------------------------------------------------
203
204 function customConfig (): CustomConfig {
205   return {
206     instance: {
207       name: CONFIG.INSTANCE.NAME,
208       shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
209       description: CONFIG.INSTANCE.DESCRIPTION,
210       terms: CONFIG.INSTANCE.TERMS,
211       defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
212       defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
213       customizations: {
214         css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS,
215         javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT
216       }
217     },
218     services: {
219       twitter: {
220         username: CONFIG.SERVICES.TWITTER.USERNAME,
221         whitelisted: CONFIG.SERVICES.TWITTER.WHITELISTED
222       }
223     },
224     cache: {
225       previews: {
226         size: CONFIG.CACHE.PREVIEWS.SIZE
227       },
228       captions: {
229         size: CONFIG.CACHE.VIDEO_CAPTIONS.SIZE
230       }
231     },
232     signup: {
233       enabled: CONFIG.SIGNUP.ENABLED,
234       limit: CONFIG.SIGNUP.LIMIT,
235       requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
236     },
237     admin: {
238       email: CONFIG.ADMIN.EMAIL
239     },
240     user: {
241       videoQuota: CONFIG.USER.VIDEO_QUOTA,
242       videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY
243     },
244     transcoding: {
245       enabled: CONFIG.TRANSCODING.ENABLED,
246       threads: CONFIG.TRANSCODING.THREADS,
247       resolutions: {
248         '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ],
249         '360p': CONFIG.TRANSCODING.RESOLUTIONS[ '360p' ],
250         '480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ],
251         '720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ],
252         '1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ]
253       }
254     },
255     import: {
256       videos: {
257         http: {
258           enabled: CONFIG.IMPORT.VIDEOS.HTTP.ENABLED
259         },
260         torrent: {
261           enabled: CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED
262         }
263       }
264     }
265   }
266 }