- Add isSafeRedirectUrl utility to prevent open-redirect vulnerabilities. - Update GET /logout to handle ?redirect=, clear cookies safely, and log audit events. - Create AppLaunchpadPage.tsx using pure Hono SSR JSX for application visibility and SSO launching. - Update GET /dashboard and AuthenticatedLayout.tsx to mount the Launchpad as the default authenticated view with Zero-Knowledge querying. - Add HYBRID_INGRESS_PLAYBOOK.md documentation for Traefik ForwardAuth routing. - Implement exhaustive unit tests in server/main.test.ts for redirect preservation, anomaly logging, and Zero-Knowledge role filtering. Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import { AuthenticatedLayout } from "./AuthenticatedLayout.tsx";
|
|
|
|
export interface AppCard {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
domain: string;
|
|
role: string;
|
|
}
|
|
|
|
export const AppLaunchpadPage = ({
|
|
apps,
|
|
isAdmin,
|
|
}: {
|
|
apps: AppCard[];
|
|
isAdmin: boolean;
|
|
}) => {
|
|
return (
|
|
<AuthenticatedLayout
|
|
title="Launchpad"
|
|
currentPath="/dashboard"
|
|
isAdmin={isAdmin}
|
|
>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
marginBottom: "2rem",
|
|
}}
|
|
>
|
|
<h2>Application Launchpad</h2>
|
|
</div>
|
|
|
|
<div
|
|
style={{
|
|
display: "grid",
|
|
gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))",
|
|
gap: "1.5rem",
|
|
}}
|
|
>
|
|
{apps.length === 0
|
|
? (
|
|
<div style={{ color: "#6c757d", gridColumn: "1 / -1" }}>
|
|
No authorized applications found.
|
|
</div>
|
|
)
|
|
: (
|
|
apps.map((app) => (
|
|
<div
|
|
class="card"
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
height: "100%",
|
|
marginBottom: "0",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
justifyContent: "space-between",
|
|
alignItems: "flex-start",
|
|
marginBottom: "0.5rem",
|
|
}}
|
|
>
|
|
<h3 style={{ margin: 0, color: "#212529" }}>{app.name}</h3>
|
|
<span
|
|
class={`badge ${
|
|
app.role === "Admin" || app.role === "Global Admin"
|
|
? "badge-success"
|
|
: "badge-primary"
|
|
}`}
|
|
style={{
|
|
backgroundColor:
|
|
app.role === "Admin" || app.role === "Global Admin"
|
|
? "#28a745"
|
|
: "#007bff",
|
|
}}
|
|
>
|
|
{app.role}
|
|
</span>
|
|
</div>
|
|
<p style={{ color: "#6c757d", flex: 1, fontSize: "0.9rem" }}>
|
|
{app.description || "No description provided."}
|
|
</p>
|
|
<div style={{ marginTop: "1rem", textAlign: "right" }}>
|
|
<a
|
|
href={`https://${app.domain}`}
|
|
class="btn-primary"
|
|
style={{ textDecoration: "none", display: "inline-block" }}
|
|
>
|
|
Launch
|
|
</a>
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</AuthenticatedLayout>
|
|
);
|
|
};
|