176 lines
4.2 KiB
TypeScript
176 lines
4.2 KiB
TypeScript
import { Elysia } from "elysia";
|
|
import { loginAdmin, logoutAdmin, requireAdminSession, getSessionFromRequest, seedAdminAccount } from "./auth/service";
|
|
import { cleanupDatabase } from "./lib/db";
|
|
import { errorResponse, jsonResponse, emptyResponse, optionsResponse, parseJson, assertAllowedOrigin } from "./lib/http";
|
|
import { config } from "./lib/config";
|
|
import { serveFrontendBuild } from "./lib/frontend";
|
|
import {
|
|
createUserSchema,
|
|
loginSchema,
|
|
parseRouteEmail,
|
|
resetPasswordSchema,
|
|
resetTokenSchema,
|
|
updatePasswordSchema
|
|
} from "./lib/validation";
|
|
import {
|
|
consumePasswordReset,
|
|
createUser,
|
|
deleteUser,
|
|
inspectPasswordReset,
|
|
listUsers,
|
|
triggerPasswordReset,
|
|
updateUserPassword
|
|
} from "./users/service";
|
|
|
|
const app = new Elysia();
|
|
|
|
const route = (
|
|
handler: (context: {
|
|
request: Request;
|
|
params: Record<string, string>;
|
|
}) => Promise<Response> | Response
|
|
) =>
|
|
async ({ request, params }: { request: Request; params: Record<string, string> }) => {
|
|
if (request.method === "OPTIONS") {
|
|
return optionsResponse(request);
|
|
}
|
|
|
|
try {
|
|
assertAllowedOrigin(request);
|
|
cleanupDatabase();
|
|
return await handler({ request, params });
|
|
} catch (error) {
|
|
return errorResponse(request, error);
|
|
}
|
|
};
|
|
|
|
await seedAdminAccount();
|
|
|
|
app.get(
|
|
"/api/health",
|
|
route(async ({ request }) =>
|
|
jsonResponse(request, 200, {
|
|
ok: true
|
|
})
|
|
)
|
|
);
|
|
|
|
app.get(
|
|
"/api/auth/session",
|
|
route(async ({ request }) =>
|
|
jsonResponse(request, 200, {
|
|
session: getSessionFromRequest(request)
|
|
})
|
|
)
|
|
);
|
|
|
|
app.post(
|
|
"/api/auth/login",
|
|
route(async ({ request }) => {
|
|
const body = await parseJson(request, loginSchema);
|
|
const result = await loginAdmin(body.email, body.password);
|
|
|
|
return jsonResponse(
|
|
request,
|
|
200,
|
|
{ session: result.session },
|
|
{ "set-cookie": result.cookie }
|
|
);
|
|
})
|
|
);
|
|
|
|
app.post(
|
|
"/api/auth/logout",
|
|
route(async ({ request }) =>
|
|
jsonResponse(
|
|
request,
|
|
200,
|
|
{ session: { authenticated: false, adminEmail: null } },
|
|
{ "set-cookie": logoutAdmin(request) }
|
|
)
|
|
)
|
|
);
|
|
|
|
app.get(
|
|
"/api/users",
|
|
route(async ({ request }) => {
|
|
requireAdminSession(request);
|
|
return jsonResponse(request, 200, { items: await listUsers() });
|
|
})
|
|
);
|
|
|
|
app.post(
|
|
"/api/users",
|
|
route(async ({ request }) => {
|
|
const session = requireAdminSession(request);
|
|
const body = await parseJson(request, createUserSchema);
|
|
const result = await createUser(body.email, session.adminEmail);
|
|
return jsonResponse(request, 201, result);
|
|
})
|
|
);
|
|
|
|
app.patch(
|
|
"/api/users/:email/password",
|
|
route(async ({ request, params }) => {
|
|
requireAdminSession(request);
|
|
const email = parseRouteEmail(params.email);
|
|
const body = await parseJson(request, updatePasswordSchema);
|
|
await updateUserPassword(email, body.password);
|
|
return emptyResponse(request, 204);
|
|
})
|
|
);
|
|
|
|
app.delete(
|
|
"/api/users/:email",
|
|
route(async ({ request, params }) => {
|
|
requireAdminSession(request);
|
|
const email = parseRouteEmail(params.email);
|
|
await deleteUser(email);
|
|
return emptyResponse(request, 204);
|
|
})
|
|
);
|
|
|
|
app.post(
|
|
"/api/users/:email/password-reset",
|
|
route(async ({ request, params }) => {
|
|
const session = requireAdminSession(request);
|
|
const email = parseRouteEmail(params.email);
|
|
const result = await triggerPasswordReset(email, session.adminEmail);
|
|
return jsonResponse(request, 200, result);
|
|
})
|
|
);
|
|
|
|
app.post(
|
|
"/api/password-resets/inspect",
|
|
route(async ({ request }) => {
|
|
const body = await parseJson(request, resetTokenSchema);
|
|
return jsonResponse(request, 200, await inspectPasswordReset(body.token));
|
|
})
|
|
);
|
|
|
|
app.post(
|
|
"/api/password-resets/consume",
|
|
route(async ({ request }) => {
|
|
const body = await parseJson(request, resetPasswordSchema);
|
|
await consumePasswordReset(body.token, body.password);
|
|
return emptyResponse(request, 204);
|
|
})
|
|
);
|
|
|
|
app.get(
|
|
"/",
|
|
route(async ({ request }) => serveFrontendBuild(request))
|
|
);
|
|
|
|
app.get(
|
|
"/*",
|
|
route(async ({ request }) => serveFrontendBuild(request))
|
|
);
|
|
|
|
app.listen({
|
|
hostname: config.host,
|
|
port: config.port
|
|
});
|
|
|
|
console.log(`Mail admin API listening on http://${config.host}:${config.port}`);
|