CamilleLegeron 1 month ago committed by GitHub
commit f2290bd1f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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;

@ -72,6 +72,7 @@ import {
import uuidv4 from "uuid/v4"; import uuidv4 from "uuid/v4";
import flatten = require('lodash/flatten'); import flatten = require('lodash/flatten');
import pick = require('lodash/pick'); import pick = require('lodash/pick');
import moment = require('moment-timezone');
// Support transactions in Sqlite in async code. This is a monkey patch, affecting // Support transactions in Sqlite in async code. This is a monkey patch, affecting
// the prototypes of various TypeORM classes. // the prototypes of various TypeORM classes.
@ -213,6 +214,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 +616,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 to today (need date only, no time)
const today = moment().startOf('day');
if (today !== moment(user.lastConnectionAt).startOf('day')) {
user.lastConnectionAt = today.toDate();
needsSave = true;
}
}
if (needsSave) { if (needsSave) {
await user.save(); await user.save();
} }

@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner, TableColumn} from 'typeorm';
export class UserLastConnection1713186031023 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.addColumn('users', new TableColumn({
name: 'last_connection_at',
type: "date",
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…
Cancel
Save