nextjs-cms 0.9.37 → 0.9.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +279 -279
- package/dist/api/actions/files.d.ts.map +1 -1
- package/dist/api/actions/pages.d.ts +28 -7
- package/dist/api/actions/pages.d.ts.map +1 -1
- package/dist/api/actions/pages.js +112 -27
- package/dist/api/trpc/root.d.ts +65 -10
- package/dist/api/trpc/root.d.ts.map +1 -1
- package/dist/api/trpc/routers/config.d.ts.map +1 -1
- package/dist/api/trpc/routers/hasItemsSection.d.ts +23 -2
- package/dist/api/trpc/routers/hasItemsSection.d.ts.map +1 -1
- package/dist/api/trpc/routers/hasItemsSection.js +2 -1
- package/dist/api/trpc/routers/logs.d.ts +37 -4
- package/dist/api/trpc/routers/logs.d.ts.map +1 -1
- package/dist/api/trpc/routers/logs.js +92 -43
- package/dist/api/trpc/routers/navigation.d.ts +4 -3
- package/dist/api/trpc/routers/navigation.d.ts.map +1 -1
- package/dist/api/trpc/routers/simpleSection.d.ts +1 -1
- package/dist/api/trpc/routers/simpleSection.d.ts.map +1 -1
- package/dist/api/trpc/server.d.ts +195 -30
- package/dist/api/trpc/server.d.ts.map +1 -1
- package/dist/cli/lib/db-config.js +10 -10
- package/dist/cli/lib/update-sections.js +24 -24
- package/dist/core/config/config-loader.d.ts +2 -2
- package/dist/core/db/table-checker/MysqlTable.js +10 -10
- package/dist/core/factories/FieldFactory.d.ts +1 -6
- package/dist/core/factories/FieldFactory.d.ts.map +1 -1
- package/dist/core/factories/FieldFactory.js +3 -68
- package/dist/core/factories/section-factory-with-esbuild.js +9 -9
- package/dist/core/factories/section-factory-with-jiti.js +9 -9
- package/dist/core/fields/checkbox.d.ts +3 -3
- package/dist/core/fields/color.d.ts +3 -3
- package/dist/core/fields/date-range.d.ts +2 -2
- package/dist/core/fields/date.d.ts +3 -3
- package/dist/core/fields/document.d.ts +3 -3
- package/dist/core/fields/field.d.ts +25 -12
- package/dist/core/fields/field.d.ts.map +1 -1
- package/dist/core/fields/field.js +51 -23
- package/dist/core/fields/index.d.ts +1 -0
- package/dist/core/fields/index.d.ts.map +1 -1
- package/dist/core/fields/index.js +2 -0
- package/dist/core/fields/map.d.ts +3 -3
- package/dist/core/fields/number.d.ts +4 -4
- package/dist/core/fields/password.d.ts +3 -3
- package/dist/core/fields/photo.d.ts +9 -9
- package/dist/core/fields/richText.d.ts +12 -12
- package/dist/core/fields/select.d.ts +7 -7
- package/dist/core/fields/selectMultiple.d.ts +4 -4
- package/dist/core/fields/slug.d.ts +9 -9
- package/dist/core/fields/tags.d.ts +3 -3
- package/dist/core/fields/text.d.ts +2 -2
- package/dist/core/fields/textArea.d.ts +2 -2
- package/dist/core/fields/video.d.ts +3 -3
- package/dist/core/helpers/conditional-rules.d.ts +63 -0
- package/dist/core/helpers/conditional-rules.d.ts.map +1 -0
- package/dist/core/helpers/conditional-rules.js +113 -0
- package/dist/core/helpers/index.d.ts +1 -0
- package/dist/core/helpers/index.d.ts.map +1 -1
- package/dist/core/helpers/index.js +4 -0
- package/dist/core/sections/category.d.ts +50 -50
- package/dist/core/sections/hasItems.d.ts +74 -74
- package/dist/core/sections/section.d.ts +23 -23
- package/dist/core/sections/simple.d.ts +6 -6
- package/dist/core/submit/submit.d.ts +4 -1
- package/dist/core/submit/submit.d.ts.map +1 -1
- package/dist/core/submit/submit.js +9 -19
- package/dist/core/types/index.d.ts +30 -8
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/logging/event-labels.d.ts +11 -0
- package/dist/logging/event-labels.d.ts.map +1 -0
- package/dist/logging/event-labels.js +14 -0
- package/dist/logging/log.d.ts +1 -1
- package/dist/logging/log.d.ts.map +1 -1
- package/dist/translations/base/en.d.ts +33 -0
- package/dist/translations/base/en.d.ts.map +1 -1
- package/dist/translations/base/en.js +33 -0
- package/dist/translations/client.d.ts +400 -4
- package/dist/translations/client.d.ts.map +1 -1
- package/dist/translations/server.d.ts +400 -4
- package/dist/translations/server.d.ts.map +1 -1
- package/package.json +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../src/api/trpc/root.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAsBvB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../src/api/trpc/root.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAsBvB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAiBw8d,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAA+0mB,CAAC;;;;;2BAAyE,CAAC;;;;;;2BAA8G,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAA7o+B,CAAC;;;;;2BAAsE,CAAC;;;;;;2BAA2G,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAHhhH,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/api/trpc/routers/config.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/api/trpc/routers/config.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAagvgB,CAAC;;;;;;GADxwgB,CAAA"}
|
|
@@ -26,12 +26,14 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
26
26
|
sectionName: string;
|
|
27
27
|
page?: number | undefined;
|
|
28
28
|
q?: string | undefined;
|
|
29
|
+
locale?: string | undefined;
|
|
29
30
|
};
|
|
30
31
|
output: {
|
|
31
32
|
error: {
|
|
32
33
|
message: string;
|
|
33
34
|
};
|
|
34
35
|
section?: undefined;
|
|
36
|
+
localization?: undefined;
|
|
35
37
|
items?: undefined;
|
|
36
38
|
totalCount?: undefined;
|
|
37
39
|
} | {
|
|
@@ -40,6 +42,24 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
40
42
|
name: string;
|
|
41
43
|
title: string;
|
|
42
44
|
};
|
|
45
|
+
localization: {
|
|
46
|
+
locales: {
|
|
47
|
+
code: string;
|
|
48
|
+
label: string;
|
|
49
|
+
rtl?: boolean;
|
|
50
|
+
}[];
|
|
51
|
+
defaultLocale: {
|
|
52
|
+
code: string;
|
|
53
|
+
label: string;
|
|
54
|
+
rtl?: boolean;
|
|
55
|
+
};
|
|
56
|
+
currentLocale: {
|
|
57
|
+
code: string;
|
|
58
|
+
label: string;
|
|
59
|
+
rtl?: boolean;
|
|
60
|
+
};
|
|
61
|
+
localeSwitcherEnabled: boolean;
|
|
62
|
+
} | null;
|
|
43
63
|
items: (Record<string, any> | {
|
|
44
64
|
id: string | number;
|
|
45
65
|
headingTitle: string;
|
|
@@ -47,6 +67,7 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
47
67
|
createdAt: string;
|
|
48
68
|
createdBy: string;
|
|
49
69
|
permission: number;
|
|
70
|
+
locales: string[];
|
|
50
71
|
})[];
|
|
51
72
|
totalCount: any;
|
|
52
73
|
error?: undefined;
|
|
@@ -86,7 +107,7 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
86
107
|
label: string;
|
|
87
108
|
required: boolean;
|
|
88
109
|
localized?: boolean;
|
|
89
|
-
|
|
110
|
+
conditionalGroup?: import("../../../core/types/index.js").ConditionalGroupClient;
|
|
90
111
|
readonly: boolean;
|
|
91
112
|
defaultValue: any;
|
|
92
113
|
value: any;
|
|
@@ -129,7 +150,7 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
129
150
|
label: string;
|
|
130
151
|
required: boolean;
|
|
131
152
|
localized?: boolean;
|
|
132
|
-
|
|
153
|
+
conditionalGroup?: import("../../../core/types/index.js").ConditionalGroupClient;
|
|
133
154
|
readonly: boolean;
|
|
134
155
|
defaultValue: any;
|
|
135
156
|
value: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hasItemsSection.d.ts","sourceRoot":"","sources":["../../../../src/api/trpc/routers/hasItemsSection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAcxB,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"hasItemsSection.d.ts","sourceRoot":"","sources":["../../../../src/api/trpc/routers/hasItemsSection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAcxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAyGs4/B,CAAC;;;;;uBAAyE,CAAC;;;;;;uBAA8G,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GADhmgC,CAAA"}
|
|
@@ -12,9 +12,10 @@ export const hasItemsSectionRouter = router({
|
|
|
12
12
|
sectionName: z.string(),
|
|
13
13
|
page: z.number().optional(),
|
|
14
14
|
q: z.string().optional(),
|
|
15
|
+
locale: z.string().optional(),
|
|
15
16
|
}))
|
|
16
17
|
.query(async ({ ctx, input }) => {
|
|
17
|
-
return await getBrowsePage(ctx.session, input.sectionName, input.page, input.q);
|
|
18
|
+
return await getBrowsePage(ctx.session, input.sectionName, input.page, input.q, input.locale);
|
|
18
19
|
}),
|
|
19
20
|
newItem: privateProcedure
|
|
20
21
|
.input(z.object({
|
|
@@ -24,7 +24,10 @@ export declare const logsRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
24
24
|
list: import("@trpc/server").TRPCQueryProcedure<{
|
|
25
25
|
input: {
|
|
26
26
|
limit?: number | undefined;
|
|
27
|
-
|
|
27
|
+
cursor?: {
|
|
28
|
+
createdAt: unknown;
|
|
29
|
+
id: number;
|
|
30
|
+
} | null | undefined;
|
|
28
31
|
eventType?: string | undefined;
|
|
29
32
|
actorId?: string | undefined;
|
|
30
33
|
sectionName?: string | undefined;
|
|
@@ -35,6 +38,7 @@ export declare const logsRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
35
38
|
} | undefined;
|
|
36
39
|
output: {
|
|
37
40
|
items: {
|
|
41
|
+
actorDeleted: boolean;
|
|
38
42
|
id: number;
|
|
39
43
|
eventType: string;
|
|
40
44
|
actorId: string | null;
|
|
@@ -48,10 +52,39 @@ export declare const logsRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
48
52
|
userAgent: string | null;
|
|
49
53
|
source: string | null;
|
|
50
54
|
createdAt: Date;
|
|
55
|
+
actorCurrentFullName: string | null;
|
|
56
|
+
}[];
|
|
57
|
+
nextCursor: {
|
|
58
|
+
createdAt: Date;
|
|
59
|
+
id: number;
|
|
60
|
+
} | null;
|
|
61
|
+
};
|
|
62
|
+
meta: object;
|
|
63
|
+
}>;
|
|
64
|
+
recent: import("@trpc/server").TRPCQueryProcedure<{
|
|
65
|
+
input: {
|
|
66
|
+
limit?: number | undefined;
|
|
67
|
+
} | undefined;
|
|
68
|
+
output: {
|
|
69
|
+
id: number;
|
|
70
|
+
eventType: string;
|
|
71
|
+
actorId: string | null;
|
|
72
|
+
actorUsername: string | null;
|
|
73
|
+
entityLabel: string | null;
|
|
74
|
+
sectionName: string | null;
|
|
75
|
+
createdAt: Date;
|
|
76
|
+
}[];
|
|
77
|
+
meta: object;
|
|
78
|
+
}>;
|
|
79
|
+
filterOptions: import("@trpc/server").TRPCQueryProcedure<{
|
|
80
|
+
input: void;
|
|
81
|
+
output: {
|
|
82
|
+
eventTypes: string[];
|
|
83
|
+
sections: string[];
|
|
84
|
+
actors: {
|
|
85
|
+
id: string;
|
|
86
|
+
username: string;
|
|
51
87
|
}[];
|
|
52
|
-
total: number;
|
|
53
|
-
limit: number;
|
|
54
|
-
offset: number;
|
|
55
88
|
};
|
|
56
89
|
meta: object;
|
|
57
90
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../../../src/api/trpc/routers/logs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../../../src/api/trpc/routers/logs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAkCxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAqG8B,MAAM;0BAAY,MAAM;;;;;GAG3E,CAAA"}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
|
2
|
-
import { and, desc, eq, gte, lte,
|
|
2
|
+
import { and, desc, eq, gte, lt, lte, or } from 'drizzle-orm';
|
|
3
3
|
import * as z from 'zod';
|
|
4
4
|
import { db } from '../../../db/client.js';
|
|
5
|
-
import { LogsTable } from '../../../db/schema.js';
|
|
5
|
+
import { AdminsTable, LogsTable } from '../../../db/schema.js';
|
|
6
|
+
import getString from '../../../translations/index.js';
|
|
6
7
|
import { isAccessAllowed } from '../../actions/privileges.js';
|
|
7
8
|
import { privateProcedure, router } from '../trpc.js';
|
|
8
|
-
|
|
9
|
+
const cursorSchema = z.object({
|
|
10
|
+
createdAt: z.coerce.date(),
|
|
11
|
+
id: z.number(),
|
|
12
|
+
});
|
|
9
13
|
const listInputSchema = z
|
|
10
14
|
.object({
|
|
11
15
|
limit: z.number().min(1).max(200).optional(),
|
|
12
|
-
|
|
16
|
+
cursor: cursorSchema.nullish(),
|
|
13
17
|
eventType: z.string().optional(),
|
|
14
18
|
actorId: z.string().optional(),
|
|
15
19
|
sectionName: z.string().optional(),
|
|
@@ -19,61 +23,106 @@ const listInputSchema = z
|
|
|
19
23
|
to: z.string().datetime().optional(),
|
|
20
24
|
})
|
|
21
25
|
.optional();
|
|
26
|
+
const ensureAccess = async (userId, language) => {
|
|
27
|
+
const accessAllowed = await isAccessAllowed({ sectionName: 'log', userId });
|
|
28
|
+
if (!accessAllowed) {
|
|
29
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: getString('sectionNotFound', language) });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
22
32
|
export const logsRouter = router({
|
|
23
33
|
list: privateProcedure.input(listInputSchema).query(async ({ ctx, input }) => {
|
|
24
|
-
|
|
25
|
-
sectionName: 'log',
|
|
26
|
-
userId: ctx.session.user.id,
|
|
27
|
-
});
|
|
28
|
-
if (!accessAllowed) {
|
|
29
|
-
throw new TRPCError({
|
|
30
|
-
code: 'NOT_FOUND',
|
|
31
|
-
message: getString('sectionNotFound', ctx.session.user.language),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
+
await ensureAccess(ctx.session.user.id, ctx.session.user.language);
|
|
34
35
|
const limit = input?.limit ?? 50;
|
|
35
|
-
const offset = input?.offset ?? 0;
|
|
36
36
|
const conditions = [];
|
|
37
|
-
if (input?.eventType)
|
|
37
|
+
if (input?.eventType)
|
|
38
38
|
conditions.push(eq(LogsTable.eventType, input.eventType));
|
|
39
|
-
|
|
40
|
-
if (input?.actorId) {
|
|
39
|
+
if (input?.actorId)
|
|
41
40
|
conditions.push(eq(LogsTable.actorId, input.actorId));
|
|
42
|
-
|
|
43
|
-
if (input?.sectionName) {
|
|
41
|
+
if (input?.sectionName)
|
|
44
42
|
conditions.push(eq(LogsTable.sectionName, input.sectionName));
|
|
45
|
-
|
|
46
|
-
if (input?.entityType) {
|
|
43
|
+
if (input?.entityType)
|
|
47
44
|
conditions.push(eq(LogsTable.entityType, input.entityType));
|
|
48
|
-
|
|
49
|
-
if (input?.entityId) {
|
|
45
|
+
if (input?.entityId)
|
|
50
46
|
conditions.push(eq(LogsTable.entityId, input.entityId));
|
|
51
|
-
|
|
52
|
-
if (input?.from) {
|
|
47
|
+
if (input?.from)
|
|
53
48
|
conditions.push(gte(LogsTable.createdAt, new Date(input.from)));
|
|
54
|
-
|
|
55
|
-
if (input?.to) {
|
|
49
|
+
if (input?.to)
|
|
56
50
|
conditions.push(lte(LogsTable.createdAt, new Date(input.to)));
|
|
51
|
+
// Keyset pagination on (createdAt, id) descending.
|
|
52
|
+
if (input?.cursor) {
|
|
53
|
+
const { createdAt, id } = input.cursor;
|
|
54
|
+
conditions.push(or(lt(LogsTable.createdAt, createdAt), and(eq(LogsTable.createdAt, createdAt), lt(LogsTable.id, id))));
|
|
57
55
|
}
|
|
58
56
|
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
const rows = await db
|
|
58
|
+
.select({
|
|
59
|
+
id: LogsTable.id,
|
|
60
|
+
eventType: LogsTable.eventType,
|
|
61
|
+
actorId: LogsTable.actorId,
|
|
62
|
+
actorUsername: LogsTable.actorUsername,
|
|
63
|
+
entityType: LogsTable.entityType,
|
|
64
|
+
entityId: LogsTable.entityId,
|
|
65
|
+
entityLabel: LogsTable.entityLabel,
|
|
66
|
+
sectionName: LogsTable.sectionName,
|
|
67
|
+
metadata: LogsTable.metadata,
|
|
68
|
+
ipAddress: LogsTable.ipAddress,
|
|
69
|
+
userAgent: LogsTable.userAgent,
|
|
70
|
+
source: LogsTable.source,
|
|
71
|
+
createdAt: LogsTable.createdAt,
|
|
72
|
+
actorCurrentFullName: AdminsTable.fullName,
|
|
73
|
+
actorCurrentId: AdminsTable.id,
|
|
74
|
+
})
|
|
62
75
|
.from(LogsTable)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
.leftJoin(AdminsTable, eq(AdminsTable.id, LogsTable.actorId))
|
|
77
|
+
.where(whereClause)
|
|
78
|
+
.orderBy(desc(LogsTable.createdAt), desc(LogsTable.id))
|
|
79
|
+
.limit(limit + 1);
|
|
80
|
+
const hasMore = rows.length > limit;
|
|
81
|
+
const items = (hasMore ? rows.slice(0, limit) : rows).map((row) => {
|
|
82
|
+
const { actorCurrentId, ...rest } = row;
|
|
83
|
+
return {
|
|
84
|
+
...rest,
|
|
85
|
+
// A log can have a null actorId (system events). Only call it "deleted"
|
|
86
|
+
// when it referenced an actor that no longer resolves to an admin.
|
|
87
|
+
actorDeleted: rest.actorId != null && actorCurrentId == null,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
const last = items[items.length - 1];
|
|
91
|
+
const nextCursor = hasMore && last ? { createdAt: last.createdAt, id: last.id } : null;
|
|
92
|
+
return { items, nextCursor };
|
|
93
|
+
}),
|
|
94
|
+
// Minimal recent-activity feed for the navbar notifications popup.
|
|
95
|
+
// Intentionally NOT gated by the 'log' privilege: available to any authenticated
|
|
96
|
+
// admin. Returns only the few, low-sensitivity fields the popup renders.
|
|
97
|
+
recent: privateProcedure
|
|
98
|
+
.input(z.object({ limit: z.number().min(1).max(50).optional() }).optional())
|
|
99
|
+
.query(async ({ input }) => {
|
|
100
|
+
const limit = input?.limit ?? 20;
|
|
101
|
+
return db
|
|
102
|
+
.select({
|
|
103
|
+
id: LogsTable.id,
|
|
104
|
+
eventType: LogsTable.eventType,
|
|
105
|
+
actorId: LogsTable.actorId,
|
|
106
|
+
actorUsername: LogsTable.actorUsername,
|
|
107
|
+
entityLabel: LogsTable.entityLabel,
|
|
108
|
+
sectionName: LogsTable.sectionName,
|
|
109
|
+
createdAt: LogsTable.createdAt,
|
|
110
|
+
})
|
|
111
|
+
.from(LogsTable)
|
|
112
|
+
.orderBy(desc(LogsTable.createdAt), desc(LogsTable.id))
|
|
113
|
+
.limit(limit);
|
|
114
|
+
}),
|
|
115
|
+
filterOptions: privateProcedure.query(async ({ ctx }) => {
|
|
116
|
+
await ensureAccess(ctx.session.user.id, ctx.session.user.language);
|
|
117
|
+
const [eventTypeRows, sectionRows, actorRows] = await Promise.all([
|
|
118
|
+
db.selectDistinct({ value: LogsTable.eventType }).from(LogsTable),
|
|
119
|
+
db.selectDistinct({ value: LogsTable.sectionName }).from(LogsTable),
|
|
120
|
+
db.selectDistinct({ id: LogsTable.actorId, username: LogsTable.actorUsername }).from(LogsTable),
|
|
71
121
|
]);
|
|
72
122
|
return {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
offset,
|
|
123
|
+
eventTypes: eventTypeRows.map((r) => r.value).filter((v) => v != null),
|
|
124
|
+
sections: sectionRows.map((r) => r.value).filter((v) => v != null),
|
|
125
|
+
actors: actorRows.filter((r) => r.id != null && r.username != null),
|
|
77
126
|
};
|
|
78
127
|
}),
|
|
79
128
|
});
|