1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-26 01:17:00 +02:00
Nuno Góis 2025-03-12 10:06:29 +00:00 committed by GitHub
parent d1d1a740f3
commit 242b0de592
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 39 additions and 3 deletions

View File

@ -89,6 +89,12 @@ const EventCard = ({ entry }: IEventCardProps) => {
<dd>{entry.type}</dd>
<StyledDefinitionTerm>Changed by:</StyledDefinitionTerm>
<dd title={entry.createdBy}>{entry.createdBy}</dd>
{entry.ip && (
<>
<StyledDefinitionTerm>IP:</StyledDefinitionTerm>
<dd title={entry.ip}>{entry.ip}</dd>
</>
)}
<ConditionallyRender
condition={Boolean(entry.project)}
show={

View File

@ -41,6 +41,11 @@ export interface EventSchema {
* @minimum 1
*/
id: number;
/**
* The IP address of the user that created the event. Only available in Enterprise.
* @nullable
*/
ip?: string | null;
/**
* The concise, human-readable name of the event.
* @nullable

View File

@ -32,12 +32,18 @@ export default class EventService {
private eventBus: EventEmitter;
private isEnterprise: boolean;
constructor(
{
eventStore,
featureTagStore,
}: Pick<IUnleashStores, 'eventStore' | 'featureTagStore'>,
{ getLogger, eventBus }: Pick<IUnleashConfig, 'getLogger' | 'eventBus'>,
{
getLogger,
eventBus,
isEnterprise,
}: Pick<IUnleashConfig, 'getLogger' | 'eventBus' | 'isEnterprise'>,
privateProjectChecker: IPrivateProjectChecker,
accessReadModel: IAccessReadModel,
) {
@ -46,6 +52,7 @@ export default class EventService {
this.privateProjectChecker = privateProjectChecker;
this.featureTagStore = featureTagStore;
this.eventBus = eventBus;
this.isEnterprise = isEnterprise;
this.accessReadModel = accessReadModel;
}
@ -101,6 +108,9 @@ export default class EventService {
query: search.query,
},
queryParams,
{
withIp: this.isEnterprise,
},
);
return {
events,

View File

@ -93,6 +93,7 @@ export interface IEventTable {
project?: string;
environment?: string;
tags: ITag[];
ip?: string;
}
const TABLE = 'events';
@ -374,15 +375,20 @@ class EventStore implements IEventStore {
async searchEvents(
params: IEventSearchParams,
queryParams: IQueryParam[],
options?: { withIp?: boolean },
): Promise<IEvent[]> {
const query = this.buildSearchQuery(params, queryParams)
.select(EVENT_COLUMNS)
.select(options?.withIp ? [...EVENT_COLUMNS, 'ip'] : EVENT_COLUMNS)
.orderBy('created_at', 'desc')
.limit(Number(params.limit) ?? 100)
.offset(Number(params.offset) ?? 0);
try {
return (await query).map(this.rowToEvent);
return (await query).map((row) =>
options?.withIp
? { ...this.rowToEvent(row), ip: row.ip }
: this.rowToEvent(row),
);
} catch (err) {
return [];
}

View File

@ -102,6 +102,13 @@ export const eventSchema = {
nullable: true,
description: 'A markdown-formatted summary of the event.',
},
ip: {
type: 'string',
nullable: true,
description:
'The IP address of the user that created the event. Only available in Enterprise.',
example: '192.168.1.1',
},
},
components: {
schemas: {

View File

@ -400,6 +400,7 @@ export interface IBaseEvent {
export interface IEvent extends Omit<IBaseEvent, 'ip'> {
id: number;
createdAt: Date;
ip?: string;
}
export interface IEnrichedEvent extends IEvent {

View File

@ -41,6 +41,7 @@ export interface IEventStore
searchEvents(
params: IEventSearchParams,
queryParams: IQueryParam[],
options?: { withIp?: boolean },
): Promise<IEvent[]>;
getMaxRevisionId(currentMax?: number): Promise<number>;
getRevisionRange(start: number, end: number): Promise<IEvent[]>;