246 lines
8.0 KiB
TypeScript
246 lines
8.0 KiB
TypeScript
import { Hono, type Context } from "hono";
|
|
import { getApiAppDependencies } from "../app-dependencies.js";
|
|
import type { ApiEnv } from "../api-types.js";
|
|
import { apiError } from "../middleware/error-envelope.js";
|
|
import {
|
|
parseWorkspaceContextRevalidationPayload,
|
|
parseWorkspaceInviteAcceptPayload,
|
|
parseWorkspaceInviteCreatePayload,
|
|
parseWorkspaceInviteRevokePayload,
|
|
} from "../validation/request-schemas.js";
|
|
import {
|
|
handleWorkspaceContextRevalidationError,
|
|
handleWorkspaceInviteError,
|
|
parseOptionalBooleanQuery,
|
|
parseWorkspaceAuthProviderKindQuery,
|
|
parseWorkspaceInviteStatusQuery,
|
|
parseWorkspaceTenantId,
|
|
readJsonBody,
|
|
requireWorkspaceInviteServiceCredential,
|
|
requireWorkspaceServiceCredential,
|
|
toWorkspaceAuthProviderApiResponse,
|
|
toWorkspaceInviteApiResponse,
|
|
} from "./shared.js";
|
|
|
|
export function registerWorkspaceRoutes(app: Hono<ApiEnv>) {
|
|
app.get("/v1/workspaces/:tenantId/auth/providers", getWorkspaceAuthProvidersRequest);
|
|
app.post("/v1/workspaces/:tenantId/context/revalidate", postWorkspaceContextRevalidateRequest);
|
|
app.get("/v1/workspaces/:tenantId/invites", getWorkspaceInvitesRequest);
|
|
app.post("/v1/workspaces/:tenantId/invites", postWorkspaceInviteRequest);
|
|
app.post("/v1/workspaces/:tenantId/invites/:inviteId/accept", postWorkspaceInviteAcceptRequest);
|
|
app.post("/v1/workspaces/:tenantId/invites/:inviteId/revoke", postWorkspaceInviteRevokeRequest);
|
|
}
|
|
|
|
async function getWorkspaceAuthProvidersRequest(c: Context<ApiEnv>) {
|
|
const unauthorized = requireWorkspaceServiceCredential(c);
|
|
if (unauthorized) {
|
|
return unauthorized;
|
|
}
|
|
|
|
const tenantId = parseWorkspaceTenantId(c);
|
|
if (!tenantId) {
|
|
return apiError(c, 400, "invalid_request", "tenantId is required.");
|
|
}
|
|
const tenantGuard = await requireActiveWorkspaceTenant(c, tenantId);
|
|
if (tenantGuard) {
|
|
return tenantGuard;
|
|
}
|
|
|
|
try {
|
|
const providers = await getApiAppDependencies().listWorkspaceAuthProviders({
|
|
tenantId,
|
|
kind: parseWorkspaceAuthProviderKindQuery(c.req.query("kind")),
|
|
enabled: parseOptionalBooleanQuery(c.req.query("enabled")),
|
|
});
|
|
|
|
return c.json({ providers: providers.map(toWorkspaceAuthProviderApiResponse) });
|
|
} catch {
|
|
return apiError(c, 500, "workspace_auth_provider_failed", "Workspace auth provider request failed.");
|
|
}
|
|
}
|
|
|
|
async function requireActiveWorkspaceTenant(c: Context<ApiEnv>, tenantId: string) {
|
|
try {
|
|
const tenant = await getApiAppDependencies().findLegacyTenantById(tenantId);
|
|
if (!tenant) {
|
|
return apiError(c, 404, "tenant_not_found", "Workspace tenant was not found.");
|
|
}
|
|
if (!tenant.active) {
|
|
return apiError(c, 403, "tenant_inactive", "Workspace tenant is inactive.");
|
|
}
|
|
return null;
|
|
} catch {
|
|
return apiError(c, 500, "workspace_tenant_failed", "Workspace tenant validation failed.");
|
|
}
|
|
}
|
|
|
|
async function postWorkspaceContextRevalidateRequest(c: Context<ApiEnv>) {
|
|
const unauthorized = requireWorkspaceServiceCredential(c);
|
|
if (unauthorized) {
|
|
return unauthorized;
|
|
}
|
|
|
|
const tenantId = parseWorkspaceTenantId(c);
|
|
if (!tenantId) {
|
|
return apiError(c, 400, "invalid_request", "tenantId is required.");
|
|
}
|
|
|
|
const body = await readJsonBody(c.req.raw);
|
|
const payload = parseWorkspaceContextRevalidationPayload(body);
|
|
if (!payload.ok) {
|
|
return apiError(c, 400, "invalid_request", payload.error.message, { params: payload.error.params });
|
|
}
|
|
|
|
try {
|
|
const context = await getApiAppDependencies().revalidateWorkspaceContext({ tenantId, userId: payload.value.userId });
|
|
return c.json({
|
|
context: {
|
|
valid: context.valid,
|
|
tenantId: context.tenantId,
|
|
userId: context.userId,
|
|
...(payload.value.providerSlug ? { providerSlug: payload.value.providerSlug } : {}),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
return handleWorkspaceContextRevalidationError(c, error);
|
|
}
|
|
}
|
|
|
|
async function getWorkspaceInvitesRequest(c: Context<ApiEnv>) {
|
|
const unauthorized = requireWorkspaceInviteServiceCredential(c);
|
|
if (unauthorized) {
|
|
return unauthorized;
|
|
}
|
|
|
|
const tenantId = parseWorkspaceTenantId(c);
|
|
if (!tenantId) {
|
|
return apiError(c, 400, "invalid_request", "tenantId is required.");
|
|
}
|
|
const tenantGuard = await requireActiveWorkspaceTenant(c, tenantId);
|
|
if (tenantGuard) {
|
|
return tenantGuard;
|
|
}
|
|
|
|
try {
|
|
const invites = await getApiAppDependencies().listWorkspaceInvites({
|
|
tenantId,
|
|
status: parseWorkspaceInviteStatusQuery(c.req.query("status")),
|
|
email: c.req.query("email"),
|
|
});
|
|
|
|
return c.json({ invites: invites.map(toWorkspaceInviteApiResponse) });
|
|
} catch {
|
|
return apiError(c, 500, "workspace_invite_failed", "Workspace invite request failed.");
|
|
}
|
|
}
|
|
|
|
async function postWorkspaceInviteRequest(c: Context<ApiEnv>) {
|
|
const unauthorized = requireWorkspaceInviteServiceCredential(c);
|
|
if (unauthorized) {
|
|
return unauthorized;
|
|
}
|
|
|
|
const tenantId = parseWorkspaceTenantId(c);
|
|
if (!tenantId) {
|
|
return apiError(c, 400, "invalid_request", "tenantId is required.");
|
|
}
|
|
const tenantGuard = await requireActiveWorkspaceTenant(c, tenantId);
|
|
if (tenantGuard) {
|
|
return tenantGuard;
|
|
}
|
|
|
|
const body = await readJsonBody(c.req.raw);
|
|
const payload = parseWorkspaceInviteCreatePayload(body);
|
|
if (!payload.ok) {
|
|
return apiError(c, 400, "invalid_request", payload.error.message, { params: payload.error.params });
|
|
}
|
|
|
|
try {
|
|
const invite = await getApiAppDependencies().createWorkspaceInvite({
|
|
tenantId,
|
|
email: payload.value.email,
|
|
firstName: payload.value.firstName,
|
|
lastName: payload.value.lastName,
|
|
type: payload.value.type,
|
|
expiresAt: payload.value.expiresAt ? new Date(payload.value.expiresAt) : null,
|
|
});
|
|
|
|
return c.json({ invite: toWorkspaceInviteApiResponse(invite), token: invite.token }, 201);
|
|
} catch (error) {
|
|
return handleWorkspaceInviteError(c, error);
|
|
}
|
|
}
|
|
|
|
async function postWorkspaceInviteAcceptRequest(c: Context<ApiEnv>) {
|
|
const unauthorized = requireWorkspaceInviteServiceCredential(c);
|
|
if (unauthorized) {
|
|
return unauthorized;
|
|
}
|
|
|
|
const tenantId = parseWorkspaceTenantId(c);
|
|
if (!tenantId) {
|
|
return apiError(c, 400, "invalid_request", "tenantId is required.");
|
|
}
|
|
const tenantGuard = await requireActiveWorkspaceTenant(c, tenantId);
|
|
if (tenantGuard) {
|
|
return tenantGuard;
|
|
}
|
|
const inviteId = c.req.param("inviteId")?.trim();
|
|
if (!inviteId) {
|
|
return apiError(c, 400, "invalid_request", "inviteId is required.");
|
|
}
|
|
|
|
const body = await readJsonBody(c.req.raw);
|
|
const payload = parseWorkspaceInviteAcceptPayload(body);
|
|
if (!payload.ok) {
|
|
return apiError(c, 400, "invalid_request", payload.error.message, { params: payload.error.params });
|
|
}
|
|
|
|
try {
|
|
const invite = await getApiAppDependencies().acceptWorkspaceInvite({
|
|
tenantId,
|
|
inviteId,
|
|
token: payload.value.token,
|
|
acceptedByUserId: payload.value.userId ?? "",
|
|
});
|
|
|
|
return c.json({ invite: toWorkspaceInviteApiResponse(invite) });
|
|
} catch (error) {
|
|
return handleWorkspaceInviteError(c, error);
|
|
}
|
|
}
|
|
|
|
async function postWorkspaceInviteRevokeRequest(c: Context<ApiEnv>) {
|
|
const unauthorized = requireWorkspaceInviteServiceCredential(c);
|
|
if (unauthorized) {
|
|
return unauthorized;
|
|
}
|
|
|
|
const tenantId = parseWorkspaceTenantId(c);
|
|
const inviteId = c.req.param("inviteId")?.trim();
|
|
if (!tenantId || !inviteId) {
|
|
return apiError(c, 400, "invalid_request", "tenantId and inviteId are required.");
|
|
}
|
|
const tenantGuard = await requireActiveWorkspaceTenant(c, tenantId);
|
|
if (tenantGuard) {
|
|
return tenantGuard;
|
|
}
|
|
|
|
const body = await readJsonBody(c.req.raw);
|
|
const payload = parseWorkspaceInviteRevokePayload(body);
|
|
if (!payload.ok) {
|
|
return apiError(c, 400, "invalid_request", payload.error.message, { params: payload.error.params });
|
|
}
|
|
|
|
try {
|
|
const invite = await getApiAppDependencies().revokeWorkspaceInvite({
|
|
tenantId,
|
|
inviteId,
|
|
revokedByUserId: payload.value.revokedByUserId ?? "",
|
|
});
|
|
return c.json({ invite: toWorkspaceInviteApiResponse(invite) });
|
|
} catch (error) {
|
|
return handleWorkspaceInviteError(c, error);
|
|
}
|
|
}
|