Skip to content

Commit 3052cc4

Browse files
Merge branch 'main' into fix/task-run-refresh-lag-2798
2 parents 389e037 + 7a7c4b1 commit 3052cc4

File tree

22 files changed

+1878
-65
lines changed

22 files changed

+1878
-65
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ We use the `<root>/references/hello-world` subdirectory as a staging ground for
9292
9393
First, make sure you are running the webapp according to the instructions above. Then:
9494
95-
1. Visit http://localhost:3030 in your browser and create a new V3 project called "hello-world".
95+
1. Visit http://localhost:3030 in your browser and create a new project called "hello-world".
9696
9797
2. In Postgres go to the "Projects" table and for the project you create change the `externalRef` to `proj_rrkpdguyagvsoktglnod`.
9898
@@ -127,7 +127,7 @@ pnpm exec trigger deploy --profile local
127127

128128
### Running
129129

130-
The following steps should be followed any time you start working on a new feature you want to test in v3:
130+
The following steps should be followed any time you start working on a new feature you want to test:
131131

132132
1. Make sure the webapp is running on localhost:3030
133133

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
AdjustmentsHorizontalIcon,
23
ArrowPathRoundedSquareIcon,
34
ArrowRightOnRectangleIcon,
45
BeakerIcon,
@@ -50,6 +51,7 @@ import {
5051
adminPath,
5152
branchesPath,
5253
concurrencyPath,
54+
limitsPath,
5355
logoutPath,
5456
newOrganizationPath,
5557
newProjectPath,
@@ -349,19 +351,26 @@ export function SideMenu({
349351
<SideMenuItem
350352
name="Concurrency"
351353
icon={ConcurrencyIcon}
352-
activeIconColor="text-amber-500"
354+
activeIconColor="text-concurrency"
353355
to={concurrencyPath(organization, project, environment)}
354356
data-action="concurrency"
355357
/>
356358
)}
357359
<SideMenuItem
358360
name="Regions"
359361
icon={GlobeAmericasIcon}
360-
activeIconColor="text-green-500"
362+
activeIconColor="text-regions"
361363
to={regionsPath(organization, project, environment)}
362364
data-action="regions"
363365
badge={<V4Badge />}
364366
/>
367+
<SideMenuItem
368+
name="Limits"
369+
icon={AdjustmentsHorizontalIcon}
370+
activeIconColor="text-limits"
371+
to={limitsPath(organization, project, environment)}
372+
data-action="limits"
373+
/>
365374
<SideMenuItem
366375
name="Project settings"
367376
icon={Cog8ToothIcon}

apps/webapp/app/models/admin.server.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { redirect } from "@remix-run/server-runtime";
22
import { prisma } from "~/db.server";
3+
import { logger } from "~/services/logger.server";
34
import { SearchParams } from "~/routes/admin._index";
45
import {
56
clearImpersonationId,
67
commitImpersonationSession,
8+
getImpersonationId,
79
setImpersonationId,
810
} from "~/services/impersonation.server";
911
import { requireUser } from "~/services/session.server";
12+
import { extractClientIp } from "~/utils/extractClientIp.server";
1013

1114
const pageSize = 20;
1215

13-
export async function adminGetUsers(userId: string, { page, search }: SearchParams) {
16+
export async function adminGetUsers(
17+
userId: string,
18+
{ page, search }: SearchParams,
19+
) {
1420
page = page || 1;
1521

1622
search = search ? decodeURIComponent(search) : undefined;
@@ -212,6 +218,22 @@ export async function redirectWithImpersonation(request: Request, userId: string
212218
throw new Error("Unauthorized");
213219
}
214220

221+
const xff = request.headers.get("x-forwarded-for");
222+
const ipAddress = extractClientIp(xff);
223+
224+
try {
225+
await prisma.impersonationAuditLog.create({
226+
data: {
227+
action: "START",
228+
adminId: user.id,
229+
targetId: userId,
230+
ipAddress,
231+
},
232+
});
233+
} catch (error) {
234+
logger.error("Failed to create impersonation audit log", { error, adminId: user.id, targetId: userId });
235+
}
236+
215237
const session = await setImpersonationId(userId, request);
216238

217239
return redirect(path, {
@@ -220,6 +242,27 @@ export async function redirectWithImpersonation(request: Request, userId: string
220242
}
221243

222244
export async function clearImpersonation(request: Request, path: string) {
245+
const user = await requireUser(request);
246+
const targetId = await getImpersonationId(request);
247+
248+
if (targetId) {
249+
const xff = request.headers.get("x-forwarded-for");
250+
const ipAddress = extractClientIp(xff);
251+
252+
try {
253+
await prisma.impersonationAuditLog.create({
254+
data: {
255+
action: "STOP",
256+
adminId: user.id,
257+
targetId,
258+
ipAddress,
259+
},
260+
});
261+
} catch (error) {
262+
logger.error("Failed to create impersonation audit log", { error, adminId: user.id, targetId });
263+
}
264+
}
265+
223266
const session = await clearImpersonationId(request);
224267

225268
return redirect(path, {

0 commit comments

Comments
 (0)