Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions client/src/app/api/org/command-policies/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { NextRequest, NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;
const TIMEOUT_MS = 20000;

async function proxy(method: string, path: string, body?: unknown) {
if (!API_BASE_URL) return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
try {
const res = await fetch(`${API_BASE_URL}${path}`, {
method,
headers: { ...authResult.headers, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
} catch (e) {
if (e instanceof Error && e.name === "AbortError") return NextResponse.json({ error: "Timeout" }, { status: 504 });
return NextResponse.json({ error: "Backend request failed" }, { status: 500 });
} finally {
clearTimeout(timer);
}
}

export async function PUT(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const body = await req.json();
return proxy("PUT", `/api/org/command-policies/${id}`, body);
}

export async function DELETE(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return proxy("DELETE", `/api/org/command-policies/${id}`);
}
42 changes: 42 additions & 0 deletions client/src/app/api/org/command-policies/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { NextRequest, NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;
const TIMEOUT_MS = 20000;

async function proxyRequest(req: NextRequest, method: string, path: string, body?: unknown) {
if (!API_BASE_URL) {
return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
}
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
try {
const res = await fetch(`${API_BASE_URL}${path}`, {
method,
headers: { ...authResult.headers, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
} catch (e) {
if (e instanceof Error && e.name === "AbortError") {
return NextResponse.json({ error: "Request timeout" }, { status: 504 });
}
return NextResponse.json({ error: "Backend request failed" }, { status: 500 });
} finally {
clearTimeout(timer);
}
}

export async function GET() {
return proxyRequest(new NextRequest("http://localhost"), "GET", "/api/org/command-policies");
}

export async function POST(req: NextRequest) {
const body = await req.json();
return proxyRequest(req, "POST", "/api/org/command-policies", body);
}
18 changes: 18 additions & 0 deletions client/src/app/api/org/command-policies/test/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextRequest, NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;

export async function POST(req: NextRequest) {
if (!API_BASE_URL) return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;
const body = await req.json();
const res = await fetch(`${API_BASE_URL}/api/org/command-policies/test`, {
method: "POST",
headers: { ...authResult.headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
}
16 changes: 16 additions & 0 deletions client/src/app/api/org/command-policy-templates/active/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;

export async function DELETE() {
if (!API_BASE_URL) return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;
const res = await fetch(`${API_BASE_URL}/api/org/command-policy-templates/active`, {
method: "DELETE",
headers: authResult.headers,
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
}
18 changes: 18 additions & 0 deletions client/src/app/api/org/command-policy-templates/apply/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextRequest, NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;

export async function POST(req: NextRequest) {
if (!API_BASE_URL) return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;
const body = await req.json();
const res = await fetch(`${API_BASE_URL}/api/org/command-policy-templates/apply`, {
method: "POST",
headers: { ...authResult.headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
}
16 changes: 16 additions & 0 deletions client/src/app/api/org/command-policy-templates/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;

export async function GET() {
if (!API_BASE_URL) return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;
const res = await fetch(`${API_BASE_URL}/api/org/command-policy-templates`, {
method: "GET",
headers: { ...authResult.headers, "Content-Type": "application/json" },
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
}
18 changes: 18 additions & 0 deletions client/src/app/api/org/command-policy-toggle/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextRequest, NextResponse } from "next/server";
import { getAuthenticatedUser } from "@/lib/auth-helper";

const API_BASE_URL = process.env.BACKEND_URL;

export async function PUT(req: NextRequest) {
if (!API_BASE_URL) return NextResponse.json({ error: "BACKEND_URL not configured" }, { status: 500 });
const authResult = await getAuthenticatedUser();
if (authResult instanceof NextResponse) return authResult;
const body = await req.json();
const res = await fetch(`${API_BASE_URL}/api/org/command-policy-toggle`, {
method: "PUT",
headers: { ...authResult.headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
}
17 changes: 17 additions & 0 deletions client/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,23 @@
-ms-overflow-style: none;
}

.scrollbar-thin::-webkit-scrollbar {
height: 1px;
}
.scrollbar-thin::-webkit-scrollbar-track {
background: transparent;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 2px;
}
.scrollbar-thin:hover::-webkit-scrollbar-thumb {
background: hsl(var(--muted-foreground) / 0.25);
}
.scrollbar-thin {
scrollbar-width: thin;
}

/* Code block styles - using Prism okaidia theme */
/* Only override font properties, preserve Prism.js colors */
pre[class*="language-"] {
Expand Down
Loading
Loading