2020-07-21 13:20:51 +00:00
|
|
|
import {Document} from 'app/gen-server/entity/Document';
|
|
|
|
import {Organization} from 'app/gen-server/entity/Organization';
|
|
|
|
import {User} from 'app/gen-server/entity/User';
|
2024-07-05 14:02:39 +00:00
|
|
|
import {HomeDBManager} from 'app/gen-server/lib/homedb/HomeDBManager';
|
2022-07-04 14:14:55 +00:00
|
|
|
import log from 'app/server/lib/log';
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
// Frequency of logging usage information. Not something we need
|
|
|
|
// to track with much granularity.
|
|
|
|
const USAGE_PERIOD_MS = 1 * 60 * 60 * 1000; // log every 1 hour
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Occasionally log usage information - number of users, orgs,
|
|
|
|
* docs, etc.
|
|
|
|
*/
|
|
|
|
export class Usage {
|
|
|
|
private _interval: NodeJS.Timeout;
|
2021-05-12 15:04:37 +00:00
|
|
|
private _currentOperation?: Promise<void>;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
public constructor(private _dbManager: HomeDBManager) {
|
2021-05-12 15:04:37 +00:00
|
|
|
this._interval = setInterval(() => this.apply(), USAGE_PERIOD_MS);
|
2020-07-21 13:20:51 +00:00
|
|
|
// Log once at beginning, in case we roll over servers faster than
|
|
|
|
// the logging period for an extended length of time,
|
|
|
|
// and to raise the visibility of this logging step so if it gets
|
|
|
|
// slow devs notice.
|
2021-05-12 15:04:37 +00:00
|
|
|
this.apply();
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
2021-05-12 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* Remove any scheduled operation, and wait for the current one to complete
|
|
|
|
* (if one is in progress).
|
|
|
|
*/
|
|
|
|
public async close() {
|
2020-07-21 13:20:51 +00:00
|
|
|
clearInterval(this._interval);
|
2021-05-12 15:04:37 +00:00
|
|
|
await this._currentOperation;
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
2021-05-12 15:04:37 +00:00
|
|
|
public apply() {
|
|
|
|
if (!this._currentOperation) {
|
|
|
|
this._currentOperation = this._apply()
|
|
|
|
.finally(() => this._currentOperation = undefined);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private async _apply(): Promise<void> {
|
|
|
|
try {
|
|
|
|
const manager = this._dbManager.connection.manager;
|
|
|
|
// raw count of users
|
|
|
|
const userCount = await manager.count(User);
|
|
|
|
// users who have logged in at least once
|
|
|
|
const userWithLoginCount = await manager.createQueryBuilder()
|
|
|
|
.from(User, 'users')
|
|
|
|
.where('first_login_at is not null')
|
|
|
|
.getCount();
|
|
|
|
// raw count of organizations (excluding personal orgs)
|
|
|
|
const orgCount = await manager.createQueryBuilder()
|
|
|
|
.from(Organization, 'orgs')
|
|
|
|
.where('owner_id is null')
|
|
|
|
.getCount();
|
|
|
|
// organizations with subscriptions that are in a non-terminated state
|
|
|
|
const orgInGoodStandingCount = await manager.createQueryBuilder()
|
|
|
|
.from(Organization, 'orgs')
|
|
|
|
.leftJoin('orgs.billingAccount', 'billing_accounts')
|
|
|
|
.where('owner_id is null')
|
|
|
|
.andWhere('billing_accounts.in_good_standing = true')
|
|
|
|
.getCount();
|
|
|
|
// raw count of documents
|
|
|
|
const docCount = await manager.count(Document);
|
|
|
|
log.rawInfo('activity', {
|
|
|
|
docCount,
|
|
|
|
orgCount,
|
|
|
|
orgInGoodStandingCount,
|
|
|
|
userCount,
|
|
|
|
userWithLoginCount,
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
log.warn("Error in Usage._apply", e);
|
|
|
|
}
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
}
|