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',
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'
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'
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()
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()
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'
import * as express from 'express'
import { serverFollowsRouter } from './follows'
+import { statsRouter } from './stats'
const serverRouter = express.Router()
serverRouter.use('/', serverFollowsRouter)
+serverRouter.use('/', statsRouter)
// ---------------------------------------------------------------------------
--- /dev/null
+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
+}
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({
})
}
+ static async getStats () {
+ const totalUsers = await UserModel.count()
+
+ return {
+ totalUsers
+ }
+ }
+
hasRight (right: UserRight) {
return hasUserRight(this.role, right)
}
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'
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,
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[],
.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
}
.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
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,
// Order of the tests we want to execute
-import './server/config'
+import './server/stats'
import './check-params'
import './users/users'
import './videos/single-server'
import './videos/video-privacy'
import './videos/services'
import './server/email'
+import './server/config'
// Order of the tests we want to execute
-// import './multiple-servers'
import './videos/video-transcoder'
import './videos/multiple-servers'
import './server/follows'
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
--- /dev/null
+/* 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()
+ }
+ })
+})
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'
--- /dev/null
+import { makeGetRequest } from '../'
+
+function getStats (url: string) {
+ const path = '/api/v1/server/stats'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getStats
+}
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'
--- /dev/null
+export interface ServerStats {
+ totalUsers: number
+ totalLocalVideos: number
+ totalLocalVideoViews: number
+ totalLocalVideoComments: number
+
+ totalVideos: number
+ totalVideoComments: number
+
+ totalInstanceFollowers: number
+ totalInstanceFollowing: number
+}