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:
parent
4d8817698a
commit
7ce38ffe89
@ -228,6 +228,17 @@ class UserStore implements IUserStore {
|
|||||||
.first();
|
.first();
|
||||||
return rowToUser(row);
|
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;
|
module.exports = UserStore;
|
||||||
|
@ -17,6 +17,7 @@ const patMiddleware = (
|
|||||||
apiToken,
|
apiToken,
|
||||||
);
|
);
|
||||||
req.user = user;
|
req.user = user;
|
||||||
|
userService.addPATSeen(apiToken);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
|
@ -28,6 +28,8 @@ import PasswordMismatch from '../error/password-mismatch';
|
|||||||
import BadDataError from '../error/bad-data-error';
|
import BadDataError from '../error/bad-data-error';
|
||||||
import { isDefined } from '../util/isDefined';
|
import { isDefined } from '../util/isDefined';
|
||||||
import { TokenUserSchema } from '../openapi/spec/token-user-schema';
|
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' });
|
const systemUser = new User({ id: -1, username: 'system' });
|
||||||
|
|
||||||
@ -78,12 +80,22 @@ class UserService {
|
|||||||
|
|
||||||
private passwordResetTimeouts: { [key: string]: NodeJS.Timeout } = {};
|
private passwordResetTimeouts: { [key: string]: NodeJS.Timeout } = {};
|
||||||
|
|
||||||
|
private seenTimer: NodeJS.Timeout;
|
||||||
|
|
||||||
|
private lastSeenSecrets: Set<string> = new Set<string>();
|
||||||
|
|
||||||
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
stores: Pick<IUnleashStores, 'userStore' | 'eventStore'>,
|
stores: Pick<IUnleashStores, 'userStore' | 'eventStore'>,
|
||||||
{
|
{
|
||||||
getLogger,
|
getLogger,
|
||||||
authentication,
|
authentication,
|
||||||
}: Pick<IUnleashConfig, 'getLogger' | 'authentication'>,
|
flagResolver,
|
||||||
|
}: Pick<
|
||||||
|
IUnleashConfig,
|
||||||
|
'getLogger' | 'authentication' | 'flagResolver'
|
||||||
|
>,
|
||||||
services: {
|
services: {
|
||||||
accessService: AccessService;
|
accessService: AccessService;
|
||||||
resetTokenService: ResetTokenService;
|
resetTokenService: ResetTokenService;
|
||||||
@ -92,6 +104,7 @@ class UserService {
|
|||||||
settingService: SettingService;
|
settingService: SettingService;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
this.flagResolver = flagResolver;
|
||||||
this.logger = getLogger('service/user-service.js');
|
this.logger = getLogger('service/user-service.js');
|
||||||
this.store = stores.userStore;
|
this.store = stores.userStore;
|
||||||
this.eventStore = stores.eventStore;
|
this.eventStore = stores.eventStore;
|
||||||
@ -103,6 +116,9 @@ class UserService {
|
|||||||
if (authentication && authentication.createAdminUser) {
|
if (authentication && authentication.createAdminUser) {
|
||||||
process.nextTick(() => this.initAdminUser());
|
process.nextTick(() => this.initAdminUser());
|
||||||
}
|
}
|
||||||
|
if (this.flagResolver.isEnabled('tokensLastSeen')) {
|
||||||
|
this.updateLastSeen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validatePassword(password: string): boolean {
|
validatePassword(password: string): boolean {
|
||||||
@ -426,6 +442,30 @@ class UserService {
|
|||||||
async getUserByPersonalAccessToken(secret: string): Promise<IUser> {
|
async getUserByPersonalAccessToken(secret: string): Promise<IUser> {
|
||||||
return this.store.getUserByPersonalAccessToken(secret);
|
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;
|
module.exports = UserService;
|
||||||
|
@ -33,4 +33,5 @@ export interface IUserStore extends Store<IUser, number> {
|
|||||||
successfullyLogin(user: IUser): Promise<void>;
|
successfullyLogin(user: IUser): Promise<void>;
|
||||||
count(): Promise<number>;
|
count(): Promise<number>;
|
||||||
getUserByPersonalAccessToken(secret: string): Promise<IUser>;
|
getUserByPersonalAccessToken(secret: string): Promise<IUser>;
|
||||||
|
markSeenAt(secrets: string[]): Promise<void>;
|
||||||
}
|
}
|
||||||
|
5
src/test/fixtures/fake-user-store.ts
vendored
5
src/test/fixtures/fake-user-store.ts
vendored
@ -142,6 +142,11 @@ class UserStoreMock implements IUserStore {
|
|||||||
getUserByPersonalAccessToken(secret: string): Promise<IUser> {
|
getUserByPersonalAccessToken(secret: string): Promise<IUser> {
|
||||||
return Promise.resolve(undefined);
|
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;
|
module.exports = UserStoreMock;
|
||||||
|
Loading…
Reference in New Issue
Block a user