1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: update seen_at pat column (#2516)

https://linear.app/unleash/issue/2-451/update-last-seen-column-for-pats
This commit is contained in:
Nuno Góis 2022-11-30 06:10:31 +00:00 committed by GitHub
parent 4d8817698a
commit 7ce38ffe89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 1 deletions

View File

@ -228,6 +228,17 @@ class UserStore implements IUserStore {
.first();
return rowToUser(row);
}
async markSeenAt(secrets: string[]): Promise<void> {
const now = new Date();
try {
await this.db('personal_access_tokens')
.whereIn('secret', secrets)
.update({ seen_at: now });
} catch (err) {
this.logger.error('Could not update lastSeen, error: ', err);
}
}
}
module.exports = UserStore;

View File

@ -17,6 +17,7 @@ const patMiddleware = (
apiToken,
);
req.user = user;
userService.addPATSeen(apiToken);
}
} catch (error) {
logger.error(error);

View File

@ -28,6 +28,8 @@ import PasswordMismatch from '../error/password-mismatch';
import BadDataError from '../error/bad-data-error';
import { isDefined } from '../util/isDefined';
import { TokenUserSchema } from '../openapi/spec/token-user-schema';
import { IFlagResolver } from 'lib/types/experimental';
import { minutesToMilliseconds } from 'date-fns';
const systemUser = new User({ id: -1, username: 'system' });
@ -78,12 +80,22 @@ class UserService {
private passwordResetTimeouts: { [key: string]: NodeJS.Timeout } = {};
private seenTimer: NodeJS.Timeout;
private lastSeenSecrets: Set<string> = new Set<string>();
private flagResolver: IFlagResolver;
constructor(
stores: Pick<IUnleashStores, 'userStore' | 'eventStore'>,
{
getLogger,
authentication,
}: Pick<IUnleashConfig, 'getLogger' | 'authentication'>,
flagResolver,
}: Pick<
IUnleashConfig,
'getLogger' | 'authentication' | 'flagResolver'
>,
services: {
accessService: AccessService;
resetTokenService: ResetTokenService;
@ -92,6 +104,7 @@ class UserService {
settingService: SettingService;
},
) {
this.flagResolver = flagResolver;
this.logger = getLogger('service/user-service.js');
this.store = stores.userStore;
this.eventStore = stores.eventStore;
@ -103,6 +116,9 @@ class UserService {
if (authentication && authentication.createAdminUser) {
process.nextTick(() => this.initAdminUser());
}
if (this.flagResolver.isEnabled('tokensLastSeen')) {
this.updateLastSeen();
}
}
validatePassword(password: string): boolean {
@ -426,6 +442,30 @@ class UserService {
async getUserByPersonalAccessToken(secret: string): Promise<IUser> {
return this.store.getUserByPersonalAccessToken(secret);
}
async updateLastSeen(): Promise<void> {
if (this.lastSeenSecrets.size > 0) {
const toStore = [...this.lastSeenSecrets];
this.lastSeenSecrets = new Set<string>();
await this.store.markSeenAt(toStore);
}
this.seenTimer = setTimeout(
async () => this.updateLastSeen(),
minutesToMilliseconds(3),
).unref();
}
addPATSeen(secret: string): void {
if (this.flagResolver.isEnabled('tokensLastSeen')) {
this.lastSeenSecrets.add(secret);
}
}
destroy(): void {
clearTimeout(this.seenTimer);
this.seenTimer = null;
}
}
module.exports = UserService;

View File

@ -33,4 +33,5 @@ export interface IUserStore extends Store<IUser, number> {
successfullyLogin(user: IUser): Promise<void>;
count(): Promise<number>;
getUserByPersonalAccessToken(secret: string): Promise<IUser>;
markSeenAt(secrets: string[]): Promise<void>;
}

View File

@ -142,6 +142,11 @@ class UserStoreMock implements IUserStore {
getUserByPersonalAccessToken(secret: string): Promise<IUser> {
return Promise.resolve(undefined);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async markSeenAt(secrets: string[]): Promise<void> {
throw new Error('Not implemented');
}
}
module.exports = UserStoreMock;