mirror of https://github.com/Chocobozzz/PeerTube
adding initial support for nodeinfo
parent
4278710d5b
commit
3f6d68d967
|
@ -16,7 +16,7 @@ import { VideoModel } from '../../models/video/video'
|
|||
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||
import { VideoShareModel } from '../../models/video/video-share'
|
||||
import { cacheRoute } from '../../middlewares/cache'
|
||||
import { cache } from '../../middlewares/cache'
|
||||
import { activityPubResponse } from './utils'
|
||||
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
|
||||
import {
|
||||
|
@ -25,7 +25,6 @@ import {
|
|||
getVideoLikesActivityPubUrl,
|
||||
getVideoSharesActivityPubUrl
|
||||
} from '../../lib/activitypub'
|
||||
import { VideoCaption } from '../../../shared/models/videos/video-caption.model'
|
||||
import { VideoCaptionModel } from '../../models/video/video-caption'
|
||||
|
||||
const activityPubClientRouter = express.Router()
|
||||
|
@ -44,7 +43,7 @@ activityPubClientRouter.get('/accounts?/:name/following',
|
|||
)
|
||||
|
||||
activityPubClientRouter.get('/videos/watch/:id',
|
||||
executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))),
|
||||
executeIfActivityPub(asyncMiddleware(cache(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))),
|
||||
executeIfActivityPub(asyncMiddleware(videosGetValidator)),
|
||||
executeIfActivityPub(asyncMiddleware(videoController))
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import { asyncMiddleware, setDefaultSort, videoCommentsFeedsValidator, videoFeed
|
|||
import { VideoModel } from '../models/video/video'
|
||||
import * as Feed from 'pfeed'
|
||||
import { AccountModel } from '../models/account/account'
|
||||
import { cacheRoute } from '../middlewares/cache'
|
||||
import { cache } from '../middlewares/cache'
|
||||
import { VideoChannelModel } from '../models/video/video-channel'
|
||||
import { VideoCommentModel } from '../models/video/video-comment'
|
||||
import { buildNSFWFilter } from '../helpers/express-utils'
|
||||
|
@ -13,7 +13,7 @@ import { buildNSFWFilter } from '../helpers/express-utils'
|
|||
const feedsRouter = express.Router()
|
||||
|
||||
feedsRouter.get('/feeds/video-comments.:format',
|
||||
asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS)),
|
||||
asyncMiddleware(cache(ROUTE_CACHE_LIFETIME.FEEDS)),
|
||||
asyncMiddleware(videoCommentsFeedsValidator),
|
||||
asyncMiddleware(generateVideoCommentsFeed)
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ feedsRouter.get('/feeds/video-comments.:format',
|
|||
feedsRouter.get('/feeds/videos.:format',
|
||||
videosSortValidator,
|
||||
setDefaultSort,
|
||||
asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS)),
|
||||
asyncMiddleware(cache(ROUTE_CACHE_LIFETIME.FEEDS)),
|
||||
asyncMiddleware(videoFeedsValidator),
|
||||
asyncMiddleware(generateVideoFeed)
|
||||
)
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import * as cors from 'cors'
|
||||
import * as express from 'express'
|
||||
import { CONFIG, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers'
|
||||
import { CONFIG, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS, ROUTE_CACHE_LIFETIME } from '../initializers'
|
||||
import { VideosPreviewCache } from '../lib/cache'
|
||||
import { cache } from '../middlewares/cache'
|
||||
import { asyncMiddleware, videosGetValidator } from '../middlewares'
|
||||
import { VideoModel } from '../models/video/video'
|
||||
import { VideosCaptionCache } from '../lib/cache/videos-caption-cache'
|
||||
import { UserModel } from '../models/account/user'
|
||||
import { VideoCommentModel } from '../models/video/video-comment'
|
||||
import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../models/nodeinfo'
|
||||
|
||||
const packageJSON = require('../../../package.json')
|
||||
const staticRouter = express.Router()
|
||||
|
||||
staticRouter.use(cors())
|
||||
|
@ -65,10 +70,32 @@ staticRouter.use(
|
|||
)
|
||||
|
||||
// robots.txt service
|
||||
staticRouter.get('/robots.txt', (req: express.Request, res: express.Response) => {
|
||||
res.type('text/plain')
|
||||
return res.send(CONFIG.INSTANCE.ROBOTS)
|
||||
})
|
||||
staticRouter.get('/robots.txt',
|
||||
asyncMiddleware(cache(ROUTE_CACHE_LIFETIME.ROBOTS)),
|
||||
(_, res: express.Response) => {
|
||||
res.type('text/plain')
|
||||
return res.send(CONFIG.INSTANCE.ROBOTS)
|
||||
}
|
||||
)
|
||||
|
||||
// nodeinfo service
|
||||
staticRouter.use('/.well-known/nodeinfo',
|
||||
asyncMiddleware(cache(ROUTE_CACHE_LIFETIME.NODEINFO)),
|
||||
(_, res: express.Response) => {
|
||||
return res.json({
|
||||
links: [
|
||||
{
|
||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||
href: CONFIG.WEBSERVER.URL + '/nodeinfo/2.0.json'
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
)
|
||||
staticRouter.use('/nodeinfo/:version.json',
|
||||
asyncMiddleware(cache(ROUTE_CACHE_LIFETIME.NODEINFO)),
|
||||
asyncMiddleware(generateNodeinfo)
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -95,6 +122,54 @@ async function getVideoCaption (req: express.Request, res: express.Response) {
|
|||
return res.sendFile(path, { maxAge: STATIC_MAX_AGE })
|
||||
}
|
||||
|
||||
async function generateNodeinfo (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const { totalVideos } = await VideoModel.getStats()
|
||||
const { totalLocalVideoComments } = await VideoCommentModel.getStats()
|
||||
const { totalUsers } = await UserModel.getStats()
|
||||
let json = {}
|
||||
|
||||
if (req.params.version && (req.params.version === '2.0')) {
|
||||
json = {
|
||||
version: '2.0',
|
||||
software: {
|
||||
name: 'peertube',
|
||||
version: packageJSON.version
|
||||
},
|
||||
protocols: [
|
||||
'activitypub'
|
||||
],
|
||||
services: {
|
||||
inbound: [],
|
||||
outbound: [
|
||||
'atom1.0',
|
||||
'rss2.0'
|
||||
]
|
||||
},
|
||||
openRegistrations: CONFIG.SIGNUP.ENABLED,
|
||||
usage: {
|
||||
users: {
|
||||
total: totalUsers
|
||||
},
|
||||
localPosts: totalVideos,
|
||||
localComments: totalLocalVideoComments
|
||||
},
|
||||
metadata: {
|
||||
taxonomy: {
|
||||
postsName: 'Videos'
|
||||
},
|
||||
nodeName: CONFIG.INSTANCE.NAME,
|
||||
nodeDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION
|
||||
}
|
||||
} as HttpNodeinfoDiasporaSoftwareNsSchema20
|
||||
res.set('Content-Type', 'application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8')
|
||||
} else {
|
||||
json = { error: 'Nodeinfo schema version not handled' }
|
||||
res.status(404)
|
||||
}
|
||||
|
||||
return res.end(JSON.stringify(json))
|
||||
}
|
||||
|
||||
async function downloadTorrent (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const { video, videoFile } = getVideoAndFile(req, res)
|
||||
if (!videoFile) return res.status(404).end()
|
||||
|
|
|
@ -104,6 +104,36 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
|
|||
return resolutionsEnabled
|
||||
}
|
||||
|
||||
const timeTable = {
|
||||
ms: 1,
|
||||
second: 1000,
|
||||
minute: 60000,
|
||||
hour: 3600000,
|
||||
day: 3600000 * 24,
|
||||
week: 3600000 * 24 * 7,
|
||||
month: 3600000 * 24 * 30
|
||||
}
|
||||
export function parseDuration (duration: number | string, defaultDuration: number): number {
|
||||
if (typeof duration === 'number') return duration
|
||||
|
||||
if (typeof duration === 'string') {
|
||||
const split = duration.match(/^([\d\.,]+)\s?(\w+)$/)
|
||||
|
||||
if (split.length === 3) {
|
||||
const len = parseFloat(split[1])
|
||||
let unit = split[2].replace(/s$/i,'').toLowerCase()
|
||||
if (unit === 'm') {
|
||||
unit = 'ms'
|
||||
}
|
||||
|
||||
return (len || 1) * (timeTable[unit] || 0)
|
||||
}
|
||||
}
|
||||
|
||||
logger.error('Duration could not be properly parsed, defaulting to ' + defaultDuration)
|
||||
return defaultDuration
|
||||
}
|
||||
|
||||
function resetSequelizeInstance (instance: Model<any>, savedFields: object) {
|
||||
Object.keys(savedFields).forEach(key => {
|
||||
const value = savedFields[key]
|
||||
|
|
|
@ -46,9 +46,11 @@ const OAUTH_LIFETIME = {
|
|||
}
|
||||
|
||||
const ROUTE_CACHE_LIFETIME = {
|
||||
FEEDS: 1000 * 60 * 15, // 15 minutes
|
||||
FEEDS: '15 minutes',
|
||||
ROBOTS: '2 hours',
|
||||
NODEINFO: '10 minutes',
|
||||
ACTIVITY_PUB: {
|
||||
VIDEOS: 1000 // 1 second, cache concurrent requests after a broadcast for example
|
||||
VIDEOS: '1 second' // 1 second, cache concurrent requests after a broadcast for example
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as express from 'express'
|
||||
import * as AsyncLock from 'async-lock'
|
||||
import { parseDuration } from '../helpers/utils'
|
||||
import { Redis } from '../lib/redis'
|
||||
import { logger } from '../helpers/logger'
|
||||
|
||||
|
@ -20,7 +21,7 @@ function cacheRoute (lifetime: number) {
|
|||
|
||||
res.send = (body) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 400) {
|
||||
const contentType = res.getHeader('content-type').toString()
|
||||
const contentType = res.get('content-type')
|
||||
Redis.Instance.setCachedRoute(req, body, lifetime, contentType, res.statusCode)
|
||||
.then(() => done())
|
||||
.catch(err => {
|
||||
|
@ -35,7 +36,7 @@ function cacheRoute (lifetime: number) {
|
|||
return next()
|
||||
}
|
||||
|
||||
if (cached.contentType) res.contentType(cached.contentType)
|
||||
if (cached.contentType) res.set('content-type', cached.contentType)
|
||||
|
||||
if (cached.statusCode) {
|
||||
const statusCode = parseInt(cached.statusCode, 10)
|
||||
|
@ -50,8 +51,14 @@ function cacheRoute (lifetime: number) {
|
|||
}
|
||||
}
|
||||
|
||||
const cache = (duration: number | string) => {
|
||||
const _lifetime = parseDuration(duration, 3600000)
|
||||
return cacheRoute(_lifetime)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
cacheRoute
|
||||
cacheRoute,
|
||||
cache
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* NodeInfo schema version 2.0.
|
||||
*/
|
||||
export interface HttpNodeinfoDiasporaSoftwareNsSchema20 {
|
||||
/**
|
||||
* The schema version, must be 2.0.
|
||||
*/
|
||||
version: '2.0'
|
||||
/**
|
||||
* Metadata about server software in use.
|
||||
*/
|
||||
software: {
|
||||
/**
|
||||
* The canonical name of this server software.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The version of this server software.
|
||||
*/
|
||||
version: string
|
||||
}
|
||||
/**
|
||||
* The protocols supported on this server.
|
||||
*/
|
||||
protocols: (
|
||||
| 'activitypub'
|
||||
| 'buddycloud'
|
||||
| 'dfrn'
|
||||
| 'diaspora'
|
||||
| 'libertree'
|
||||
| 'ostatus'
|
||||
| 'pumpio'
|
||||
| 'tent'
|
||||
| 'xmpp'
|
||||
| 'zot')[]
|
||||
/**
|
||||
* The third party sites this server can connect to via their application API.
|
||||
*/
|
||||
services: {
|
||||
/**
|
||||
* The third party sites this server can retrieve messages from for combined display with regular traffic.
|
||||
*/
|
||||
inbound: ('atom1.0' | 'gnusocial' | 'imap' | 'pnut' | 'pop3' | 'pumpio' | 'rss2.0' | 'twitter')[]
|
||||
/**
|
||||
* The third party sites this server can publish messages to on the behalf of a user.
|
||||
*/
|
||||
outbound: (
|
||||
| 'atom1.0'
|
||||
| 'blogger'
|
||||
| 'buddycloud'
|
||||
| 'diaspora'
|
||||
| 'dreamwidth'
|
||||
| 'drupal'
|
||||
| 'facebook'
|
||||
| 'friendica'
|
||||
| 'gnusocial'
|
||||
| 'google'
|
||||
| 'insanejournal'
|
||||
| 'libertree'
|
||||
| 'linkedin'
|
||||
| 'livejournal'
|
||||
| 'mediagoblin'
|
||||
| 'myspace'
|
||||
| 'pinterest'
|
||||
| 'pnut'
|
||||
| 'posterous'
|
||||
| 'pumpio'
|
||||
| 'redmatrix'
|
||||
| 'rss2.0'
|
||||
| 'smtp'
|
||||
| 'tent'
|
||||
| 'tumblr'
|
||||
| 'twitter'
|
||||
| 'wordpress'
|
||||
| 'xmpp')[]
|
||||
}
|
||||
/**
|
||||
* Whether this server allows open self-registration.
|
||||
*/
|
||||
openRegistrations: boolean
|
||||
/**
|
||||
* Usage statistics for this server.
|
||||
*/
|
||||
usage: {
|
||||
/**
|
||||
* statistics about the users of this server.
|
||||
*/
|
||||
users: {
|
||||
/**
|
||||
* The total amount of on this server registered users.
|
||||
*/
|
||||
total?: number
|
||||
/**
|
||||
* The amount of users that signed in at least once in the last 180 days.
|
||||
*/
|
||||
activeHalfyear?: number
|
||||
/**
|
||||
* The amount of users that signed in at least once in the last 30 days.
|
||||
*/
|
||||
activeMonth?: number
|
||||
};
|
||||
/**
|
||||
* The amount of posts that were made by users that are registered on this server.
|
||||
*/
|
||||
localPosts?: number
|
||||
/**
|
||||
* The amount of comments that were made by users that are registered on this server.
|
||||
*/
|
||||
localComments?: number
|
||||
}
|
||||
/**
|
||||
* Free form key value pairs for software specific values. Clients should not rely on any specific key present.
|
||||
*/
|
||||
metadata: {
|
||||
[k: string]: any
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue