pluginAuth: string | null
+ lastLoginDate: Date | null
+
createdAt: Date
constructor (hash: Partial<UserServerModel>) {
this.createdAt = hash.createdAt
this.pluginAuth = hash.pluginAuth
+ this.lastLoginDate = hash.lastLoginDate
if (hash.account !== undefined) {
this.account = new Account(hash.account)
async function getStats (req: express.Request, res: express.Response) {
const { totalLocalVideos, totalLocalVideoViews, totalVideos } = await VideoModel.getStats()
const { totalLocalVideoComments, totalVideoComments } = await VideoCommentModel.getStats()
- const { totalUsers } = await UserModel.getStats()
+ const { totalUsers, totalDailyActiveUsers, totalWeeklyActiveUsers, totalMonthlyActiveUsers } = await UserModel.getStats()
const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats()
const { totalLocalVideoFilesSize } = await VideoFileModel.getStats()
totalLocalVideoComments,
totalVideos,
totalVideoComments,
+
totalUsers,
+ totalDailyActiveUsers,
+ totalWeeklyActiveUsers,
+ totalMonthlyActiveUsers,
+
totalInstanceFollowers,
totalInstanceFollowing,
+
videosRedundancy: videosRedundancyStats
}
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 500
+const LAST_MIGRATION_VERSION = 505
// ---------------------------------------------------------------------------
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction
+ queryInterface: Sequelize.QueryInterface
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
+
+ {
+ const field = {
+ type: Sequelize.DATE,
+ allowNull: true
+ }
+ await utils.queryInterface.addColumn('user', 'lastLoginDate', field)
+ }
+
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
}
const tokenCreated = await OAuthTokenModel.create(tokenToCreate)
+
+ user.lastLoginDate = new Date()
+ await user.save()
+
return Object.assign(tokenCreated, { client, user })
}
@Column
pluginAuth: string
+ @AllowNull(true)
+ @Default(null)
+ @Column
+ lastLoginDate: Date
+
@CreatedAt
createdAt: Date
}
static async getStats () {
+ function getActiveUsers (days: number) {
+ const query = {
+ where: {
+ [Op.and]: [
+ literal(`"lastLoginDate" > NOW() - INTERVAL '${days}d'`)
+ ]
+ }
+ }
+
+ return UserModel.count(query)
+ }
+
const totalUsers = await UserModel.count()
+ const totalDailyActiveUsers = await getActiveUsers(1)
+ const totalWeeklyActiveUsers = await getActiveUsers(7)
+ const totalMonthlyActiveUsers = await getActiveUsers(30)
return {
- totalUsers
+ totalUsers,
+ totalDailyActiveUsers,
+ totalWeeklyActiveUsers,
+ totalMonthlyActiveUsers
}
}
createdAt: this.createdAt,
- pluginAuth: this.pluginAuth
+ pluginAuth: this.pluginAuth,
+
+ lastLoginDate: this.lastLoginDate
}
if (parameters.withAdminFlags) {
ServerInfo, unfollow,
uploadVideo,
viewVideo,
- wait
+ wait,
+ userLogin
} from '../../../../shared/extra-utils'
import { setAccessTokensToServers } from '../../../../shared/extra-utils/index'
import { getStats } from '../../../../shared/extra-utils/server/stats'
describe('Test stats (excluding redundancy)', function () {
let servers: ServerInfo[] = []
+ const user = {
+ username: 'user1',
+ password: 'super_password'
+ }
before(async function () {
this.timeout(60000)
await doubleFollow(servers[0], servers[1])
- const user = {
- username: 'user1',
- password: 'super_password'
- }
await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { fixture: 'video_short.webm' })
})
it('Should have the correct total videos stats after an unfollow', async function () {
+ this.timeout(15000)
+
await unfollow(servers[2].url, servers[2].accessToken, servers[0])
await waitJobs(servers)
expect(data.totalVideos).to.equal(0)
})
+ it('Should have the correct active users stats', async function () {
+ const server = servers[0]
+
+ {
+ const res = await getStats(server.url)
+ const data: ServerStats = res.body
+ expect(data.totalDailyActiveUsers).to.equal(1)
+ expect(data.totalWeeklyActiveUsers).to.equal(1)
+ expect(data.totalMonthlyActiveUsers).to.equal(1)
+ }
+
+ {
+ await userLogin(server, user)
+
+ const res = await getStats(server.url)
+ const data: ServerStats = res.body
+ expect(data.totalDailyActiveUsers).to.equal(2)
+ expect(data.totalWeeklyActiveUsers).to.equal(2)
+ expect(data.totalMonthlyActiveUsers).to.equal(2)
+ }
+ })
+
after(async function () {
await cleanupTests(servers)
})
expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com')
expect(user.nsfwPolicy).to.equal('display')
+ expect(rootUser.lastLoginDate).to.exist
+ expect(user.lastLoginDate).to.exist
+
userId = user.id
})
export interface ServerStats {
totalUsers: number
+ totalDailyActiveUsers: number
+ totalWeeklyActiveUsers: number
+ totalMonthlyActiveUsers: number
+
totalLocalVideos: number
totalLocalVideoViews: number
totalLocalVideoComments: number
createdAt: Date
pluginAuth: string | null
+
+ lastLoginDate: Date | null
}
export interface MyUserSpecialPlaylist {