mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
Create user last connection datetime
This commit is contained in:
parent
fe9cc80ccc
commit
643dacafbf
@ -29,6 +29,9 @@ export class User extends BaseEntity {
|
|||||||
@Column({name: 'first_login_at', type: Date, nullable: true})
|
@Column({name: 'first_login_at', type: Date, nullable: true})
|
||||||
public firstLoginAt: Date | null;
|
public firstLoginAt: Date | null;
|
||||||
|
|
||||||
|
@Column({name: 'last_connection_at', type: Date, nullable: true})
|
||||||
|
public lastConnectionAt: Date | null;
|
||||||
|
|
||||||
@OneToOne(type => Organization, organization => organization.owner)
|
@OneToOne(type => Organization, organization => organization.owner)
|
||||||
public personalOrg: Organization;
|
public personalOrg: Organization;
|
||||||
|
|
||||||
|
@ -213,6 +213,7 @@ function isNonGuestGroup(group: Group): group is NonGuestGroup {
|
|||||||
export interface UserProfileChange {
|
export interface UserProfileChange {
|
||||||
name?: string;
|
name?: string;
|
||||||
isFirstTimeUser?: boolean;
|
isFirstTimeUser?: boolean;
|
||||||
|
newConnection?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifies a request to access a document. This combination of values is also used for caching
|
// Identifies a request to access a document. This combination of values is also used for caching
|
||||||
@ -614,6 +615,14 @@ export class HomeDBManager extends EventEmitter {
|
|||||||
// any automation for first logins
|
// any automation for first logins
|
||||||
if (!props.isFirstTimeUser) { isWelcomed = true; }
|
if (!props.isFirstTimeUser) { isWelcomed = true; }
|
||||||
}
|
}
|
||||||
|
if (props.newConnection === true) {
|
||||||
|
// set last connection time to now (remove milliseconds for compatibility with other
|
||||||
|
// timestamps in db set by typeorm, and since second level precision is fine)
|
||||||
|
const nowish = new Date();
|
||||||
|
nowish.setMilliseconds(0);
|
||||||
|
user.lastConnectionAt = nowish;
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
if (needsSave) {
|
if (needsSave) {
|
||||||
await user.save();
|
await user.save();
|
||||||
}
|
}
|
||||||
@ -701,12 +710,15 @@ export class HomeDBManager extends EventEmitter {
|
|||||||
user.name = (profile && (profile.name || email.split('@')[0])) || '';
|
user.name = (profile && (profile.name || email.split('@')[0])) || '';
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
if (profile && !user.firstLoginAt) {
|
if (profile) {
|
||||||
// set first login time to now (remove milliseconds for compatibility with other
|
// set first login time and last connection time to now (remove milliseconds for compatibility with other
|
||||||
// timestamps in db set by typeorm, and since second level precision is fine)
|
// timestamps in db set by typeorm, and since second level precision is fine)
|
||||||
const nowish = new Date();
|
const nowish = new Date();
|
||||||
nowish.setMilliseconds(0);
|
nowish.setMilliseconds(0);
|
||||||
|
user.lastConnectionAt = nowish;
|
||||||
|
if (!user.firstLoginAt) {
|
||||||
user.firstLoginAt = nowish;
|
user.firstLoginAt = nowish;
|
||||||
|
}
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
if (!user.picture && profile && profile.picture) {
|
if (!user.picture && profile && profile.picture) {
|
||||||
|
18
app/gen-server/migration/1713186031023-UserLastConnection.ts
Normal file
18
app/gen-server/migration/1713186031023-UserLastConnection.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { MigrationInterface, QueryRunner, TableColumn} from 'typeorm';
|
||||||
|
|
||||||
|
export class UserLastConnection1713186031023 implements MigrationInterface {
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
const sqlite = queryRunner.connection.driver.options.type === 'sqlite';
|
||||||
|
const datetime = sqlite ? "datetime" : "timestamp with time zone";
|
||||||
|
await queryRunner.addColumn('users', new TableColumn({
|
||||||
|
name: 'last_connection_at',
|
||||||
|
type: datetime,
|
||||||
|
isNullable: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.dropColumn('users', 'last_connection_at');
|
||||||
|
}
|
||||||
|
}
|
@ -501,6 +501,10 @@ export class Client {
|
|||||||
const user = this._profile ? await this._fetchUser(dbManager) : dbManager.getAnonymousUser();
|
const user = this._profile ? await this._fetchUser(dbManager) : dbManager.getAnonymousUser();
|
||||||
this._user = user ? dbManager.makeFullUser(user) : undefined;
|
this._user = user ? dbManager.makeFullUser(user) : undefined;
|
||||||
this._firstLoginAt = user?.firstLoginAt || null;
|
this._firstLoginAt = user?.firstLoginAt || null;
|
||||||
|
if (this._user) {
|
||||||
|
// Send the information to the dbManager that the user has a new activity
|
||||||
|
await dbManager.updateUser(this._user.id, {newConnection: true});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _onMessage(message: string): Promise<void> {
|
private async _onMessage(message: string): Promise<void> {
|
||||||
|
@ -21,9 +21,9 @@ export const TEST_HTTPS_OFFSET = process.env.GRIST_TEST_HTTPS_OFFSET ?
|
|||||||
|
|
||||||
// Database fields that we permit in entities but don't want to cross the api.
|
// Database fields that we permit in entities but don't want to cross the api.
|
||||||
const INTERNAL_FIELDS = new Set([
|
const INTERNAL_FIELDS = new Set([
|
||||||
'apiKey', 'billingAccountId', 'firstLoginAt', 'filteredOut', 'ownerId', 'gracePeriodStart', 'stripeCustomerId',
|
'apiKey', 'billingAccountId', 'firstLoginAt', 'lastConnectionAt', 'filteredOut', 'ownerId', 'gracePeriodStart',
|
||||||
'stripeSubscriptionId', 'stripePlanId', 'stripeProductId', 'userId', 'isFirstTimeUser', 'allowGoogleLogin',
|
'stripeCustomerId', 'stripeSubscriptionId', 'stripePlanId', 'stripeProductId', 'userId', 'isFirstTimeUser',
|
||||||
'authSubject', 'usage', 'createdBy'
|
'allowGoogleLogin', 'authSubject', 'usage', 'createdBy'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +41,8 @@ import {ForkIndexes1678737195050 as ForkIndexes} from 'app/gen-server/migration/
|
|||||||
import {ActivationPrefs1682636695021 as ActivationPrefs} from 'app/gen-server/migration/1682636695021-ActivationPrefs';
|
import {ActivationPrefs1682636695021 as ActivationPrefs} from 'app/gen-server/migration/1682636695021-ActivationPrefs';
|
||||||
import {AssistantLimit1685343047786 as AssistantLimit} from 'app/gen-server/migration/1685343047786-AssistantLimit';
|
import {AssistantLimit1685343047786 as AssistantLimit} from 'app/gen-server/migration/1685343047786-AssistantLimit';
|
||||||
import {Shares1701557445716 as Shares} from 'app/gen-server/migration/1701557445716-Shares';
|
import {Shares1701557445716 as Shares} from 'app/gen-server/migration/1701557445716-Shares';
|
||||||
|
import {UserLastConnection1713186031023
|
||||||
|
as UserLastConnection} from 'app/gen-server/migration/1713186031023-UserLastConnection';
|
||||||
|
|
||||||
const home: HomeDBManager = new HomeDBManager();
|
const home: HomeDBManager = new HomeDBManager();
|
||||||
|
|
||||||
@ -49,7 +51,7 @@ const migrations = [Initial, Login, PinDocs, UserPicture, DisplayEmail, DisplayE
|
|||||||
CustomerIndex, ExtraIndexes, OrgHost, DocRemovedAt, Prefs,
|
CustomerIndex, ExtraIndexes, OrgHost, DocRemovedAt, Prefs,
|
||||||
ExternalBilling, DocOptions, Secret, UserOptions, GracePeriodStart,
|
ExternalBilling, DocOptions, Secret, UserOptions, GracePeriodStart,
|
||||||
DocumentUsage, Activations, UserConnectId, UserUUID, UserUniqueRefUUID,
|
DocumentUsage, Activations, UserConnectId, UserUUID, UserUniqueRefUUID,
|
||||||
Forks, ForkIndexes, ActivationPrefs, AssistantLimit, Shares];
|
Forks, ForkIndexes, ActivationPrefs, AssistantLimit, Shares, UserLastConnection];
|
||||||
|
|
||||||
// Assert that the "members" acl rule and group exist (or not).
|
// Assert that the "members" acl rule and group exist (or not).
|
||||||
function assertMembersGroup(org: Organization, exists: boolean) {
|
function assertMembersGroup(org: Organization, exists: boolean) {
|
||||||
|
Loading…
Reference in New Issue
Block a user