mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-16 18:23:08 +03:00
feat: location filter for global audit log (#662)
This commit is contained in:
@@ -89,6 +89,7 @@ func (alc *AuditLogController) listAuditLogsForUserHandler(c *gin.Context) {
|
|||||||
// @Param filters[userId] query string false "Filter by user ID"
|
// @Param filters[userId] query string false "Filter by user ID"
|
||||||
// @Param filters[event] query string false "Filter by event type"
|
// @Param filters[event] query string false "Filter by event type"
|
||||||
// @Param filters[clientName] query string false "Filter by client name"
|
// @Param filters[clientName] query string false "Filter by client name"
|
||||||
|
// @Param filters[location] query string false "Filter by location type (external or internal)"
|
||||||
// @Success 200 {object} dto.Paginated[dto.AuditLogDto]
|
// @Success 200 {object} dto.Paginated[dto.AuditLogDto]
|
||||||
// @Router /api/audit-logs/all [get]
|
// @Router /api/audit-logs/all [get]
|
||||||
func (alc *AuditLogController) listAllAuditLogsHandler(c *gin.Context) {
|
func (alc *AuditLogController) listAllAuditLogsHandler(c *gin.Context) {
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ type AuditLogFilterDto struct {
|
|||||||
UserID string `form:"filters[userId]"`
|
UserID string `form:"filters[userId]"`
|
||||||
Event string `form:"filters[event]"`
|
Event string `form:"filters[event]"`
|
||||||
ClientName string `form:"filters[clientName]"`
|
ClientName string `form:"filters[clientName]"`
|
||||||
|
Location string `form:"filters[location]"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,14 @@ func (s *AuditLogService) ListAllAuditLogs(ctx context.Context, sortedPagination
|
|||||||
return nil, utils.PaginationResponse{}, fmt.Errorf("unsupported database dialect: %s", dialect)
|
return nil, utils.PaginationResponse{}, fmt.Errorf("unsupported database dialect: %s", dialect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if filters.Location != "" {
|
||||||
|
switch filters.Location {
|
||||||
|
case "external":
|
||||||
|
query = query.Where("country != 'Internal Network'")
|
||||||
|
case "internal":
|
||||||
|
query = query.Where("country = 'Internal Network'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pagination, err := utils.PaginateAndSort(sortedPaginationRequest, query, &logs)
|
pagination, err := utils.PaginateAndSort(sortedPaginationRequest, query, &logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP INDEX idx_audit_logs_country;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CREATE INDEX idx_audit_logs_country ON audit_logs(country);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP INDEX idx_audit_logs_country;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CREATE INDEX idx_audit_logs_country ON audit_logs(country);
|
||||||
@@ -320,6 +320,7 @@
|
|||||||
"all_users": "All Users",
|
"all_users": "All Users",
|
||||||
"all_events": "All Events",
|
"all_events": "All Events",
|
||||||
"all_clients": "All Clients",
|
"all_clients": "All Clients",
|
||||||
|
"all_locations": "All Locations",
|
||||||
"global_audit_log": "Global Audit Log",
|
"global_audit_log": "Global Audit Log",
|
||||||
"see_all_account_activities_from_the_last_3_months": "See all user activity for the last 3 months.",
|
"see_all_account_activities_from_the_last_3_months": "See all user activity for the last 3 months.",
|
||||||
"token_sign_in": "Token Sign In",
|
"token_sign_in": "Token Sign In",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
onSelect,
|
onSelect,
|
||||||
oninput,
|
oninput,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
disableSearch = false,
|
||||||
selectText = m.select_an_option(),
|
selectText = m.select_an_option(),
|
||||||
...restProps
|
...restProps
|
||||||
}: HTMLAttributes<HTMLButtonElement> & {
|
}: HTMLAttributes<HTMLButtonElement> & {
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
oninput?: FormEventHandler<HTMLInputElement>;
|
oninput?: FormEventHandler<HTMLInputElement>;
|
||||||
onSelect?: (value: string) => void;
|
onSelect?: (value: string) => void;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
disableSearch?: boolean;
|
||||||
selectText?: string;
|
selectText?: string;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
@@ -76,13 +78,15 @@
|
|||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content class="p-0" sameWidth>
|
<Popover.Content class="p-0" sameWidth>
|
||||||
<Command.Root shouldFilter={false}>
|
<Command.Root shouldFilter={false}>
|
||||||
<Command.Input
|
{#if !disableSearch}
|
||||||
placeholder={m.search()}
|
<Command.Input
|
||||||
oninput={(e) => {
|
placeholder={m.search()}
|
||||||
filterItems(e.currentTarget.value);
|
oninput={(e) => {
|
||||||
oninput?.(e);
|
filterItems(e.currentTarget.value);
|
||||||
}}
|
oninput?.(e);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
<Command.Empty>
|
<Command.Empty>
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<div class="flex w-full justify-center">
|
<div class="flex w-full justify-center">
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ export type AuditLog = {
|
|||||||
export type AuditLogFilter = {
|
export type AuditLogFilter = {
|
||||||
userId: string;
|
userId: string;
|
||||||
event: string;
|
event: string;
|
||||||
|
location: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,9 +18,15 @@
|
|||||||
let filters: AuditLogFilter = $state({
|
let filters: AuditLogFilter = $state({
|
||||||
userId: '',
|
userId: '',
|
||||||
event: '',
|
event: '',
|
||||||
|
location: '',
|
||||||
clientName: ''
|
clientName: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const locationTypes = $state({
|
||||||
|
external: 'External Networks',
|
||||||
|
internal: 'Internal Networks'
|
||||||
|
});
|
||||||
|
|
||||||
const eventTypes = $state({
|
const eventTypes = $state({
|
||||||
SIGN_IN: m.sign_in(),
|
SIGN_IN: m.sign_in(),
|
||||||
TOKEN_SIGN_IN: m.token_sign_in(),
|
TOKEN_SIGN_IN: m.token_sign_in(),
|
||||||
@@ -47,7 +53,7 @@
|
|||||||
>
|
>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<div class="mb-6 grid grid-cols-1 gap-4 md:grid-cols-3">
|
<div class="mb-6 grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
{#await auditLogService.listUsers()}
|
{#await auditLogService.listUsers()}
|
||||||
<Select.Root type="single">
|
<Select.Root type="single">
|
||||||
@@ -82,6 +88,20 @@
|
|||||||
bind:value={filters.event}
|
bind:value={filters.event}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<SearchableSelect
|
||||||
|
disableSearch={true}
|
||||||
|
class="w-full"
|
||||||
|
items={[
|
||||||
|
{ value: '', label: m.all_locations() },
|
||||||
|
...Object.entries(locationTypes).map(([value, label]) => ({
|
||||||
|
value,
|
||||||
|
label
|
||||||
|
}))
|
||||||
|
]}
|
||||||
|
bind:value={filters.location}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{#await auditLogService.listClientNames()}
|
{#await auditLogService.listClientNames()}
|
||||||
<Select.Root
|
<Select.Root
|
||||||
|
|||||||
Reference in New Issue
Block a user