import {route, Route, Routes} from "../routes"; import {WithSecurity} from "./withSecurity"; import {BaseRequestLens, RequestLens, RoutingResult} from "../lenses"; import {HttpRequest} from "@http4t/core/contract"; import {Mutable} from "../util/mutable"; import {SecuredRoute, SecuredRoutes} from "./"; export class ProvideSecurityTokenLens extends BaseRequestLens { constructor(private readonly securedLens: RequestLens>, private readonly token: () => Promise | TToken) { super(); } async get(from: HttpRequest): Promise> { throw new Error(`${Object.getPrototypeOf(this).name} is not intended to be used on the server, only in clients`) } async setRequest(into: HttpRequest, value: T): Promise { const token = await this.token(); return this.securedLens.set(into, {value, security: token}); } } export type UnsecuredRouteFor = TRoute extends Route, infer TResponse> ? Route : never; export type UnsecuredRoutesFor = { readonly [K in keyof TRoutes]: UnsecuredRouteFor } export function tokenProvidedRoute, any>, TToken>( serverRoute: TRoute, token: () => Promise | TToken) : UnsecuredRouteFor { return route( new ProvideSecurityTokenLens(serverRoute.request, token), serverRoute.response) as UnsecuredRouteFor; } /** * For a route taking a request of {@link WithSecurity<{something:string},TToken>}, turns */ export function tokenProvidedRoutes, TToken>( routesSecuredByTToken: TRoutes, token: () => Promise | TToken) : UnsecuredRoutesFor { return Object.entries(routesSecuredByTToken) .reduce( (acc, [k, route]) => { const secured = tokenProvidedRoute( route as SecuredRoute, token); acc[k as keyof UnsecuredRoutesFor] = secured as any; return acc; }, {} as Mutable>) }