fix typings and edge config key issue

This commit is contained in:
Sara
2023-11-01 13:48:32 +01:00
committed by Mathieu Virbel
parent 2847ee177b
commit 7ca152992c
6 changed files with 58 additions and 35 deletions

View File

@@ -1,14 +1,8 @@
"use client"; "use client";
import { createContext, useContext, useEffect, useState } from "react"; import { createContext, useContext, useEffect, useState } from "react";
import { DomainConfig } from "../lib/edgeConfig";
type DomainContextType = { type DomainContextType = Omit<DomainConfig, "auth_callback_url">;
features: {
requireLogin: boolean;
privacy: boolean;
browse: boolean;
};
apiUrl: string | null;
};
export const DomainContext = createContext<DomainContextType>({ export const DomainContext = createContext<DomainContextType>({
features: { features: {
@@ -16,22 +10,22 @@ export const DomainContext = createContext<DomainContextType>({
privacy: true, privacy: true,
browse: false, browse: false,
}, },
apiUrl: null, api_url: "",
}); });
export const DomainContextProvider = ({ config, children }) => { export const DomainContextProvider = ({
config,
children,
}: {
config: DomainConfig;
children: any;
}) => {
const [context, setContext] = useState<DomainContextType>(); const [context, setContext] = useState<DomainContextType>();
useEffect(() => { useEffect(() => {
if (!config) return; if (!config) return;
setContext({ const { auth_callback_url, ...others } = config;
features: { setContext(others);
requireLogin: !!config["features"]["requireLogin"],
privacy: !!config["features"]["privacy"],
browse: !!config["features"]["browse"],
},
apiUrl: config["api_url"],
});
}, [config]); }, [config]);
if (!context) return; if (!context) return;
@@ -41,9 +35,12 @@ export const DomainContextProvider = ({ config, children }) => {
); );
}; };
// Get feature config client-side with
export const featureEnabled = ( export const featureEnabled = (
featureName: "requireLogin" | "privacy" | "browse", featureName: "requireLogin" | "privacy" | "browse",
) => { ) => {
const context = useContext(DomainContext); const context = useContext(DomainContext);
return context.features[featureName] as boolean | undefined; return context.features[featureName] as boolean | undefined;
}; };
// Get config server-side (out of react) : see lib/edgeConfig.

View File

@@ -11,6 +11,7 @@ import About from "../(aboutAndPrivacy)/about";
import Privacy from "../(aboutAndPrivacy)/privacy"; import Privacy from "../(aboutAndPrivacy)/privacy";
import { get } from "@vercel/edge-config"; import { get } from "@vercel/edge-config";
import { DomainContextProvider } from "./domainContext"; import { DomainContextProvider } from "./domainContext";
import { getConfig } from "../lib/edgeConfig";
const poppins = Poppins({ subsets: ["latin"], weight: ["200", "400", "600"] }); const poppins = Poppins({ subsets: ["latin"], weight: ["200", "400", "600"] });
@@ -68,10 +69,8 @@ type LayoutProps = {
}; };
export default async function RootLayout({ children, params }: LayoutProps) { export default async function RootLayout({ children, params }: LayoutProps) {
const config = await get(params.domain); const config = await getConfig(params.domain);
const requireLogin = config ? config["features"]["requireLogin"] : false; const { requireLogin, privacy, browse } = config.features;
const privacy = config ? config["features"]["privacy"] : true;
const browse = config ? config["features"]["browse"] : true;
return ( return (
<html lang="en"> <html lang="en">

31
www/app/lib/edgeConfig.ts Normal file
View File

@@ -0,0 +1,31 @@
import { get } from "@vercel/edge-config";
type EdgeConfig = {
[domainWithDash: string]: {
features: {
[featureName in "requireLogin" | "privacy" | "browse"]: boolean;
};
auth_callback_url: string;
api_url: string;
};
};
export type DomainConfig = EdgeConfig["domainWithDash"];
// Edge config main keys can only be alphanumeric and _ or -
export function edgeKeyToDomain(key: string) {
return key.replaceAll(".", "_");
}
export function edgeDomainToKey(domain: string) {
return domain.replaceAll("_", ".");
}
// get edge config server-side (prefer DomainContext when available), domain is the hostname
export async function getConfig(domain: string) {
const config = await get(edgeDomainToKey(domain));
if (typeof config !== "object") throw Error("Error fetchig config");
return config as DomainConfig;
}

View File

@@ -1,8 +1,6 @@
import { Fief, FiefUserInfo } from "@fief/fief"; import { Fief, FiefUserInfo } from "@fief/fief";
import { FiefAuth, IUserInfoCache } from "@fief/fief/nextjs"; import { FiefAuth, IUserInfoCache } from "@fief/fief/nextjs";
import { get } from "@vercel/edge-config"; import { getConfig } from "./edgeConfig";
import { NextRequest, NextResponse } from "next/server";
import { useError } from "../(errors)/errorContext";
export const SESSION_COOKIE_NAME = "reflector-auth"; export const SESSION_COOKIE_NAME = "reflector-auth";
@@ -46,12 +44,12 @@ export const getFiefAuth = async (url: URL) => {
if (FIEF_AUTHS[url.hostname]) { if (FIEF_AUTHS[url.hostname]) {
return FIEF_AUTHS[url.hostname]; return FIEF_AUTHS[url.hostname];
} else { } else {
const config = url && (await get(url.hostname)); const config = url && (await getConfig(url.hostname));
if (config) { if (config) {
FIEF_AUTHS[url.hostname] = new FiefAuth({ FIEF_AUTHS[url.hostname] = new FiefAuth({
client: fiefClient, client: fiefClient,
sessionCookieName: SESSION_COOKIE_NAME, sessionCookieName: SESSION_COOKIE_NAME,
redirectURI: config["auth_callback_url"], redirectURI: config.auth_callback_url,
logoutRedirectURI: url.origin, logoutRedirectURI: url.origin,
userInfoCache: new MemoryUserInfoCache(), userInfoCache: new MemoryUserInfoCache(),
}); });

View File

@@ -7,7 +7,7 @@ import { DomainContext } from "../[domain]/domainContext";
export default function getApi(): DefaultApi { export default function getApi(): DefaultApi {
const accessTokenInfo = useFiefAccessTokenInfo(); const accessTokenInfo = useFiefAccessTokenInfo();
const api_url = useContext(DomainContext).apiUrl; const api_url = useContext(DomainContext).api_url;
if (!api_url) throw new Error("no API URL"); if (!api_url) throw new Error("no API URL");
const apiConfiguration = new Configuration({ const apiConfiguration = new Configuration({

View File

@@ -1,24 +1,22 @@
import { NextResponse, NextRequest } from "next/server"; import { NextResponse, NextRequest } from "next/server";
import { get } from "@vercel/edge-config"; import { get } from "@vercel/edge-config";
import { FiefAuth, IUserInfoCache } from "@fief/fief/nextjs"; import { getFiefAuthMiddleware } from "./app/lib/fief";
import { getFiefAuth, getFiefAuthMiddleware } from "./app/lib/fief"; import { getConfig } from "./app/lib/edgeConfig";
export async function middleware(request: NextRequest) { export async function middleware(request: NextRequest) {
const domain = request.nextUrl.hostname; const domain = request.nextUrl.hostname;
const config = await get(domain); const config = await getConfig(domain);
if (!config) return NextResponse.error();
// Feature-flag protedted paths // Feature-flag protedted paths
if ( if (
!config["features"]["browse"] && !config.features.browse &&
request.nextUrl.pathname.startsWith("/browse") request.nextUrl.pathname.startsWith("/browse")
) { ) {
return NextResponse.redirect(request.nextUrl.origin); return NextResponse.redirect(request.nextUrl.origin);
} }
if (config["features"]["requireLogin"]) { if (config.features.requireLogin) {
const fiefMiddleware = await getFiefAuthMiddleware(request.nextUrl); const fiefMiddleware = await getFiefAuthMiddleware(request.nextUrl);
const fiefResponse = fiefMiddleware(request); const fiefResponse = fiefMiddleware(request);
if ( if (