import { Elysia, t } from "elysia";
import { html } from "@elysiajs/html";
import { cookie } from "@elysiajs/cookie";
import { KeyCloak, generateState, generateCodeVerifier } from "arctic";
import * as elements from "typed-html";
import { BaseHtml } from "../components/BaseHtml";
import { InputGroup, TextAreaGroup, ExperienceForm, EducationForm, ProjectForm } from "../components/AdminForms";
import { getAdminProfile, getAdminExperience, getAdminEducation, getAdminProjects, getAdminExperienceById, getAdminEducationById, getAdminProjectById } from "../db/queries";
import { updateProfile, createExperience, updateExperience, deleteExperience, createEducation, updateEducation, deleteEducation, updateExperienceOrder, updateEducationOrder, createProject, updateProject, deleteProject, updateProjectOrder } from "../db/mutations";
// Initialize Keycloak (Arctic)
const realmURL = process.env.KEYCLOAK_REALM_URL || "";
const clientId = process.env.KEYCLOAK_CLIENT_ID || "";
const clientSecret = process.env.KEYCLOAK_CLIENT_SECRET || "";
const redirectURI = process.env.KEYCLOAK_REDIRECT_URI || "http://localhost:3000/admin/callback";
const keycloak = new KeyCloak(realmURL, clientId, clientSecret, redirectURI);
const AdminLayout = ({ children }: elements.Children) => (
{children}
{/* SortableJS Initialization Script */}
);
export const adminRoutes = new Elysia()
.use(cookie())
.use(html())
// Auth Middleware
.derive(({ cookie: { auth_session } }) => {
return {
isLoggedIn: !!auth_session?.value
};
})
// 1. Login Page
.get("/admin/login", ({ isLoggedIn, set, html, cookie: { oauth_state, oauth_code_verifier } }) => {
if (isLoggedIn) return Response.redirect("/admin/dashboard");
const state = generateState();
const codeVerifier = generateCodeVerifier();
oauth_state.set({
value: state,
path: "/",
secure: process.env.NODE_ENV === "production",
httpOnly: true,
maxAge: 600
});
oauth_code_verifier.set({
value: codeVerifier,
path: "/",
secure: process.env.NODE_ENV === "production",
httpOnly: true,
maxAge: 600
});
try {
const url = keycloak.createAuthorizationURL(state, codeVerifier, ["openid", "profile", "email"]);
return html(
);
} catch (e) {
return "Error generating Keycloak URL. Check .env configuration.";
}
})
// 2. Callback Handler
.get("/admin/callback", async ({ query, cookie: { oauth_state, oauth_code_verifier, auth_session }, set }) => {
const code = query.code as string;
const state = query.state as string;
const storedState = oauth_state.value;
const storedVerifier = oauth_code_verifier.value;
if (!code || !state || !storedState || !storedVerifier || state !== storedState) {
return new Response("Invalid State or Code", { status: 400 });
}
try {
const tokens = await keycloak.validateAuthorizationCode(code, storedVerifier);
auth_session.set({
value: tokens.accessToken(),
httpOnly: true,
path: "/",
secure: process.env.NODE_ENV === "production",
maxAge: 86400 // 1 day
});
oauth_state.remove();
oauth_code_verifier.remove();
return Response.redirect("/admin/dashboard");
} catch (e: any) {
console.error("Keycloak Error:", e);
return new Response(`Authentication failed: ${e.message}\n\nStack: ${e.stack}`, { status: 500 });
}
})
.get("/admin/logout", ({ cookie: { auth_session }, set }) => {
auth_session.remove();
return Response.redirect("/admin/login");
})
// Protected Routes Guard
.onBeforeHandle(({ isLoggedIn, set }) => {
if (!isLoggedIn) return Response.redirect("/admin/login");
})
// Dashboard
.get("/admin/dashboard", ({ html }) => {
const profile = getAdminProfile();
const experience = getAdminExperience();
const education = getAdminEducation();
const projects = getAdminProjects();
return html(
Dashboard
{/* Profile Section */}
{/* Projects Section */}
Projects
{projects.map((proj: any) => (
))}
{/* Experience Section */}
Experience
{experience.map((exp: any) => (
))}
{/* Education Section */}
Education
{education.map((edu: any) => (
))}
);
})
// POST Handlers
.post("/admin/profile", ({ body, set }) => {
updateProfile(1, body);
return Response.redirect("/admin/dashboard");
})
// Projects
.post("/admin/project/new", ({ set }) => {
const id = createProject();
const newItem = getAdminProjectById(Number(id));
if (!newItem) return "";
return ;
})
.post("/admin/project/reorder", ({ body }) => {
const { ids } = body as { ids: string[] };
updateProjectOrder(ids.map(Number));
return "OK";
})
.post("/admin/project/:id", ({ params, body, set }) => {
const id = Number(params.id);
updateProject(id, body);
const updatedProj = getAdminProjectById(id);
if (!updatedProj) return "";
return ;
})
.post("/admin/project/:id/delete", ({ params, set }) => {
deleteProject(Number(params.id));
return "";
})
// Experience
.post("/admin/experience/new", ({ set }) => {
const id = createExperience();
const newItem = getAdminExperienceById(Number(id));
if (!newItem) return "";
return ;
})
.post("/admin/experience/reorder", ({ body }) => {
const { ids } = body as { ids: string[] };
updateExperienceOrder(ids.map(Number));
return "OK";
})
.post("/admin/experience/:id", ({ params, body, set }) => {
const id = Number(params.id);
updateExperience(id, body);
const updatedExp = getAdminExperienceById(id);
if (!updatedExp) return "";
return ;
})
.post("/admin/experience/:id/delete", ({ params, set }) => {
deleteExperience(Number(params.id));
return "";
})
// Education
.post("/admin/education/new", ({ set }) => {
const id = createEducation();
const newItem = getAdminEducationById(Number(id));
if (!newItem) return "";
return ;
})
.post("/admin/education/reorder", ({ body }) => {
const { ids } = body as { ids: string[] };
updateEducationOrder(ids.map(Number));
return "OK";
})
.post("/admin/education/:id", ({ params, body, set }) => {
const id = Number(params.id);
updateEducation(id, body);
const updatedEdu = getAdminEducationById(id);
if (!updatedEdu) return "";
return ;
})
.post("/admin/education/:id/delete", ({ params, set }) => {
deleteEducation(Number(params.id));
return "";
});