1 import cors from 'cors'
2 import express from 'express'
3 import { CONFIG, isEmailEnabled } from '@server/initializers/config'
4 import { serveIndexHTML } from '@server/lib/client-html'
5 import { ServerConfigManager } from '@server/lib/server-config-manager'
6 import { HttpStatusCode } from '@shared/models'
7 import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
8 import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION, ROUTE_CACHE_LIFETIME } from '../initializers/constants'
9 import { getThemeOrDefault } from '../lib/plugins/theme-utils'
10 import { asyncMiddleware } from '../middlewares'
11 import { cacheRoute } from '../middlewares/cache/cache'
12 import { UserModel } from '../models/user/user'
13 import { VideoModel } from '../models/video/video'
14 import { VideoCommentModel } from '../models/video/video-comment'
16 const miscRouter = express.Router()
18 miscRouter.use(cors())
20 miscRouter.use('/nodeinfo/:version.json',
21 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
22 asyncMiddleware(generateNodeinfo)
26 miscRouter.get('/robots.txt',
27 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS),
28 (_, res: express.Response) => {
29 res.type('text/plain')
31 return res.send(CONFIG.INSTANCE.ROBOTS)
35 miscRouter.all('/teapot',
37 asyncMiddleware(serveIndexHTML)
40 // security.txt service
41 miscRouter.get('/security.txt',
42 (_, res: express.Response) => {
43 return res.redirect(HttpStatusCode.MOVED_PERMANENTLY_301, '/.well-known/security.txt')
47 // ---------------------------------------------------------------------------
53 // ---------------------------------------------------------------------------
55 async function generateNodeinfo (req: express.Request, res: express.Response) {
56 const { totalVideos } = await VideoModel.getStats()
57 const { totalLocalVideoComments } = await VideoCommentModel.getStats()
58 const { totalUsers, totalMonthlyActiveUsers, totalHalfYearActiveUsers } = await UserModel.getStats()
60 if (!req.params.version || req.params.version !== '2.0') {
62 status: HttpStatusCode.NOT_FOUND_404,
63 message: 'Nodeinfo schema version not handled'
71 version: PEERTUBE_VERSION
83 openRegistrations: CONFIG.SIGNUP.ENABLED,
87 activeMonth: totalMonthlyActiveUsers,
88 activeHalfyear: totalHalfYearActiveUsers
90 localPosts: totalVideos,
91 localComments: totalLocalVideoComments
97 nodeName: CONFIG.INSTANCE.NAME,
98 nodeDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
102 users: CONFIG.SEARCH.REMOTE_URI.USERS,
103 anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS
107 registered: ServerConfigManager.Instance.getRegisteredPlugins()
110 registered: ServerConfigManager.Instance.getRegisteredThemes(),
111 default: getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
114 enabled: isEmailEnabled()
117 enabled: CONFIG.CONTACT_FORM.ENABLED
121 enabled: CONFIG.TRANSCODING.HLS.ENABLED
124 enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED
126 enabledResolutions: ServerConfigManager.Instance.getEnabledResolutions('vod')
129 enabled: CONFIG.LIVE.ENABLED,
131 enabled: CONFIG.LIVE.TRANSCODING.ENABLED,
132 enabledResolutions: ServerConfigManager.Instance.getEnabledResolutions('live')
138 enabled: CONFIG.IMPORT.VIDEOS.HTTP.ENABLED
141 enabled: CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED
148 enabled: CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED
155 max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max
157 extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME
162 extensions: CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME,
164 max: CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max
168 extensions: CONSTRAINTS_FIELDS.VIDEOS.EXTNAME
174 max: CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.FILE_SIZE.max
176 extensions: CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.EXTNAME
180 videoQuota: CONFIG.USER.VIDEO_QUOTA,
181 videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY
185 intervalDays: CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
189 enabled: CONFIG.TRACKER.ENABLED
193 } as HttpNodeinfoDiasporaSoftwareNsSchema20
195 res.contentType('application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"')
200 function getCup (req: express.Request, res: express.Response, next: express.NextFunction) {
201 res.status(HttpStatusCode.I_AM_A_TEAPOT_418)
202 res.setHeader('Accept-Additions', 'Non-Dairy;1,Sugar;1')
203 res.setHeader('Safe', 'if-sepia-awake')