Add stats route
This commit is contained in:
parent
22b59e8099
commit
09cababd79
|
@ -13,7 +13,7 @@ import {
|
|||
TRANSCODING_THREADS
|
||||
} from '@app/shared/forms/form-validators/custom-config'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
|
||||
import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
|
||||
|
||||
@Component({
|
||||
selector: 'my-edit-custom-config',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { HttpClient } from '@angular/common/http'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
|
||||
import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
|
||||
import { environment } from '../../../../environments/environment'
|
||||
import { RestExtractor, RestService } from '../../../shared'
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'rxjs/add/operator/map'
|
|||
import { Observable } from 'rxjs/Observable'
|
||||
import { ResultList } from '../../../../../../shared'
|
||||
import { JobState } from '../../../../../../shared/models'
|
||||
import { Job } from '../../../../../../shared/models/job.model'
|
||||
import { Job } from '../../../../../../shared/models/server/job.model'
|
||||
import { environment } from '../../../../environments/environment'
|
||||
import { RestExtractor, RestPagination, RestService } from '../../../shared'
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
|
|||
import 'rxjs/add/operator/do'
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject'
|
||||
import { ServerConfig } from '../../../../../shared'
|
||||
import { About } from '../../../../../shared/models/config/about.model'
|
||||
import { About } from '../../../../../shared/models/server/about.model'
|
||||
import { environment } from '../../../environments/environment'
|
||||
|
||||
@Injectable()
|
||||
|
|
|
@ -56,6 +56,8 @@ async function inboxController (req: express.Request, res: express.Response, nex
|
|||
specificActor = res.locals.videoChannel
|
||||
}
|
||||
|
||||
logger.info('Receiving inbox requests for %d activities by %s.', activities.length, res.locals.signature.actor)
|
||||
|
||||
await processActivities(activities, res.locals.signature.actor, specificActor)
|
||||
|
||||
res.status(204).end()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as express from 'express'
|
||||
import { omit } from 'lodash'
|
||||
import { ServerConfig, UserRight } from '../../../shared'
|
||||
import { About } from '../../../shared/models/config/about.model'
|
||||
import { CustomConfig } from '../../../shared/models/config/custom-config.model'
|
||||
import { About } from '../../../shared/models/server/about.model'
|
||||
import { CustomConfig } from '../../../shared/models/server/custom-config.model'
|
||||
import { unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
|
||||
import { isSignupAllowed } from '../../helpers/utils'
|
||||
import { CONFIG, CONSTRAINTS_FIELDS, reloadConfig } from '../../initializers'
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import * as express from 'express'
|
||||
import { serverFollowsRouter } from './follows'
|
||||
import { statsRouter } from './stats'
|
||||
|
||||
const serverRouter = express.Router()
|
||||
|
||||
serverRouter.use('/', serverFollowsRouter)
|
||||
serverRouter.use('/', statsRouter)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import * as express from 'express'
|
||||
import { ServerStats } from '../../../../shared/models/server/server-stats.model'
|
||||
import { asyncMiddleware } from '../../../middlewares'
|
||||
import { UserModel } from '../../../models/account/user'
|
||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||
import { VideoModel } from '../../../models/video/video'
|
||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||
|
||||
const statsRouter = express.Router()
|
||||
|
||||
statsRouter.get('/stats',
|
||||
asyncMiddleware(getStats)
|
||||
)
|
||||
|
||||
async function getStats (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const { totalLocalVideos, totalLocalVideoViews, totalVideos } = await VideoModel.getStats()
|
||||
const { totalLocalVideoComments, totalVideoComments } = await VideoCommentModel.getStats()
|
||||
const { totalUsers } = await UserModel.getStats()
|
||||
const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats()
|
||||
|
||||
const data: ServerStats = {
|
||||
totalLocalVideos,
|
||||
totalLocalVideoViews,
|
||||
totalVideos,
|
||||
totalLocalVideoComments,
|
||||
totalVideoComments,
|
||||
totalUsers,
|
||||
totalInstanceFollowers,
|
||||
totalInstanceFollowing
|
||||
}
|
||||
|
||||
return res.json(data).end()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
statsRouter
|
||||
}
|
|
@ -13,6 +13,7 @@ import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
|
|||
import { OAuthTokenModel } from '../oauth/oauth-token'
|
||||
import { getSort, throwIfNotValid } from '../utils'
|
||||
import { VideoChannelModel } from '../video/video-channel'
|
||||
import { VideoCommentModel } from '../video/video-comment'
|
||||
import { AccountModel } from './account'
|
||||
|
||||
@DefaultScope({
|
||||
|
@ -226,6 +227,14 @@ export class UserModel extends Model<UserModel> {
|
|||
})
|
||||
}
|
||||
|
||||
static async getStats () {
|
||||
const totalUsers = await UserModel.count()
|
||||
|
||||
return {
|
||||
totalUsers
|
||||
}
|
||||
}
|
||||
|
||||
hasRight (right: UserRight) {
|
||||
return hasUserRight(this.role, right)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
import { FollowState } from '../../../shared/models/actors'
|
||||
import { AccountFollow } from '../../../shared/models/actors/follow.model'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { getServerActor } from '../../helpers/utils'
|
||||
import { ACTOR_FOLLOW_SCORE } from '../../initializers'
|
||||
import { FOLLOW_STATES } from '../../initializers/constants'
|
||||
import { ServerModel } from '../server/server'
|
||||
|
@ -182,34 +183,6 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
|
|||
return ActorFollowModel.findOne(query)
|
||||
}
|
||||
|
||||
static loadByFollowerInbox (url: string, t?: Sequelize.Transaction) {
|
||||
const query = {
|
||||
where: {
|
||||
state: 'accepted'
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ActorModel,
|
||||
required: true,
|
||||
as: 'ActorFollower',
|
||||
where: {
|
||||
[Sequelize.Op.or]: [
|
||||
{
|
||||
inboxUrl: url
|
||||
},
|
||||
{
|
||||
sharedInboxUrl: url
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
transaction: t
|
||||
} as any // FIXME: typings does not work
|
||||
|
||||
return ActorFollowModel.findOne(query)
|
||||
}
|
||||
|
||||
static listFollowingForApi (id: number, start: number, count: number, sort: string) {
|
||||
const query = {
|
||||
distinct: true,
|
||||
|
@ -296,6 +269,27 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
|
|||
return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count)
|
||||
}
|
||||
|
||||
static async getStats () {
|
||||
const serverActor = await getServerActor()
|
||||
|
||||
const totalInstanceFollowing = await ActorFollowModel.count({
|
||||
where: {
|
||||
actorId: serverActor.id
|
||||
}
|
||||
})
|
||||
|
||||
const totalInstanceFollowers = await ActorFollowModel.count({
|
||||
where: {
|
||||
targetActorId: serverActor.id
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
totalInstanceFollowing,
|
||||
totalInstanceFollowers
|
||||
}
|
||||
}
|
||||
|
||||
private static async createListAcceptedFollowForApiQuery (
|
||||
type: 'followers' | 'following',
|
||||
actorIds: number[],
|
||||
|
|
|
@ -326,6 +326,32 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
.findAll(query)
|
||||
}
|
||||
|
||||
static async getStats () {
|
||||
const totalLocalVideoComments = await VideoCommentModel.count({
|
||||
include: [
|
||||
{
|
||||
model: AccountModel,
|
||||
required: true,
|
||||
include: [
|
||||
{
|
||||
model: ActorModel,
|
||||
required: true,
|
||||
where: {
|
||||
serverId: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
const totalVideoComments = await VideoCommentModel.count()
|
||||
|
||||
return {
|
||||
totalLocalVideoComments,
|
||||
totalVideoComments
|
||||
}
|
||||
}
|
||||
|
||||
getThreadId (): number {
|
||||
return this.originCommentId || this.id
|
||||
}
|
||||
|
|
|
@ -761,6 +761,29 @@ export class VideoModel extends Model<VideoModel> {
|
|||
.findOne(options)
|
||||
}
|
||||
|
||||
static async getStats () {
|
||||
const totalLocalVideos = await VideoModel.count({
|
||||
where: {
|
||||
remote: false
|
||||
}
|
||||
})
|
||||
const totalVideos = await VideoModel.count()
|
||||
|
||||
let totalLocalVideoViews = await VideoModel.sum('views', {
|
||||
where: {
|
||||
remote: false
|
||||
}
|
||||
})
|
||||
// Sequelize could return null...
|
||||
if (!totalLocalVideoViews) totalLocalVideoViews = 0
|
||||
|
||||
return {
|
||||
totalLocalVideos,
|
||||
totalLocalVideoViews,
|
||||
totalVideos
|
||||
}
|
||||
}
|
||||
|
||||
getOriginalFile () {
|
||||
if (Array.isArray(this.VideoFiles) === false) return undefined
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { omit } from 'lodash'
|
||||
import 'mocha'
|
||||
import { CustomConfig } from '../../../../shared/models/config/custom-config.model'
|
||||
import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
|
||||
|
||||
import {
|
||||
createUser, flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePutBodyRequest, runServer, ServerInfo,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Order of the tests we want to execute
|
||||
import './server/config'
|
||||
import './server/stats'
|
||||
import './check-params'
|
||||
import './users/users'
|
||||
import './videos/single-server'
|
||||
|
@ -10,3 +10,4 @@ import './videos/video-description'
|
|||
import './videos/video-privacy'
|
||||
import './videos/services'
|
||||
import './server/email'
|
||||
import './server/config'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// Order of the tests we want to execute
|
||||
// import './multiple-servers'
|
||||
import './videos/video-transcoder'
|
||||
import './videos/multiple-servers'
|
||||
import './server/follows'
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
import 'mocha'
|
||||
import * as chai from 'chai'
|
||||
import { About } from '../../../../shared/models/config/about.model'
|
||||
import { CustomConfig } from '../../../../shared/models/config/custom-config.model'
|
||||
import { About } from '../../../../shared/models/server/about.model'
|
||||
import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
|
||||
import { deleteCustomConfig, getAbout, killallServers, reRunServer } from '../../utils'
|
||||
const expect = chai.expect
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import { ServerStats } from '../../../../shared/models/server/server-stats.model'
|
||||
import {
|
||||
createUser,
|
||||
doubleFollow,
|
||||
flushAndRunMultipleServers,
|
||||
follow,
|
||||
killallServers,
|
||||
ServerInfo,
|
||||
uploadVideo,
|
||||
viewVideo,
|
||||
wait
|
||||
} from '../../utils'
|
||||
import { flushTests, setAccessTokensToServers } from '../../utils/index'
|
||||
import { getStats } from '../../utils/server/stats'
|
||||
import { addVideoCommentThread } from '../../utils/videos/video-comments'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
describe('Test stats', function () {
|
||||
let servers: ServerInfo[] = []
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
await flushTests()
|
||||
servers = await flushAndRunMultipleServers(3)
|
||||
await setAccessTokensToServers(servers)
|
||||
|
||||
await doubleFollow(servers[0], servers[1])
|
||||
|
||||
const user = {
|
||||
username: 'user1',
|
||||
password: 'super_password'
|
||||
}
|
||||
await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
|
||||
|
||||
const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, {})
|
||||
const videoUUID = resVideo.body.video.uuid
|
||||
|
||||
await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment')
|
||||
|
||||
await viewVideo(servers[0].url, videoUUID)
|
||||
|
||||
await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken)
|
||||
await wait(5000)
|
||||
})
|
||||
|
||||
it('Should have the correct stats on instance 1', async function () {
|
||||
const res = await getStats(servers[0].url)
|
||||
const data: ServerStats = res.body
|
||||
|
||||
expect(data.totalLocalVideoComments).to.equal(1)
|
||||
expect(data.totalLocalVideos).to.equal(1)
|
||||
expect(data.totalLocalVideoViews).to.equal(1)
|
||||
expect(data.totalUsers).to.equal(2)
|
||||
expect(data.totalVideoComments).to.equal(1)
|
||||
expect(data.totalVideos).to.equal(1)
|
||||
expect(data.totalInstanceFollowers).to.equal(2)
|
||||
expect(data.totalInstanceFollowing).to.equal(1)
|
||||
})
|
||||
|
||||
it('Should have the correct stats on instance 2', async function () {
|
||||
const res = await getStats(servers[1].url)
|
||||
const data: ServerStats = res.body
|
||||
|
||||
expect(data.totalLocalVideoComments).to.equal(0)
|
||||
expect(data.totalLocalVideos).to.equal(0)
|
||||
expect(data.totalLocalVideoViews).to.equal(0)
|
||||
expect(data.totalUsers).to.equal(1)
|
||||
expect(data.totalVideoComments).to.equal(1)
|
||||
expect(data.totalVideos).to.equal(1)
|
||||
expect(data.totalInstanceFollowers).to.equal(1)
|
||||
expect(data.totalInstanceFollowing).to.equal(1)
|
||||
})
|
||||
|
||||
it('Should have the correct stats on instance 3', async function () {
|
||||
const res = await getStats(servers[2].url)
|
||||
const data: ServerStats = res.body
|
||||
|
||||
expect(data.totalLocalVideoComments).to.equal(0)
|
||||
expect(data.totalLocalVideos).to.equal(0)
|
||||
expect(data.totalLocalVideoViews).to.equal(0)
|
||||
expect(data.totalUsers).to.equal(1)
|
||||
expect(data.totalVideoComments).to.equal(1)
|
||||
expect(data.totalVideos).to.equal(1)
|
||||
expect(data.totalInstanceFollowing).to.equal(1)
|
||||
expect(data.totalInstanceFollowers).to.equal(0)
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers(servers)
|
||||
|
||||
// Keep the logs if the test failed
|
||||
if (this['ok']) {
|
||||
await flushTests()
|
||||
}
|
||||
})
|
||||
})
|
|
@ -1,5 +1,5 @@
|
|||
import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../'
|
||||
import { CustomConfig } from '../../../../shared/models/config/custom-config.model'
|
||||
import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
|
||||
|
||||
function getConfig (url: string) {
|
||||
const path = '/api/v1/config'
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { makeGetRequest } from '../'
|
||||
|
||||
function getStats (url: string) {
|
||||
const path = '/api/v1/server/stats'
|
||||
|
||||
return makeGetRequest({
|
||||
url,
|
||||
path,
|
||||
statusCodeExpected: 200
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getStats
|
||||
}
|
|
@ -2,7 +2,7 @@ export * from './actors'
|
|||
export * from './activitypub'
|
||||
export * from './users'
|
||||
export * from './videos'
|
||||
export * from './job.model'
|
||||
export * from './server/job.model'
|
||||
export * from './oauth-client-local.model'
|
||||
export * from './result-list.model'
|
||||
export * from './config/server-config.model'
|
||||
export * from './server/server-config.model'
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export interface ServerStats {
|
||||
totalUsers: number
|
||||
totalLocalVideos: number
|
||||
totalLocalVideoViews: number
|
||||
totalLocalVideoComments: number
|
||||
|
||||
totalVideos: number
|
||||
totalVideoComments: number
|
||||
|
||||
totalInstanceFollowers: number
|
||||
totalInstanceFollowing: number
|
||||
}
|
Loading…
Reference in New Issue