import { isDebug } from "core"; import { autoFormatString } from "core/utils"; import { type ChangeEvent, useState } from "react"; import { TbAt, TbBrandAppleFilled, TbBrandDiscordFilled, TbBrandFacebookFilled, TbBrandGithubFilled, TbBrandGoogleFilled, TbBrandInstagram, TbBrandOauth, TbBrandX, TbSettings, } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; import { useBknd } from "ui/client/bknd"; import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; import { Message } from "ui/components/display/Message"; import { Field, Form, FormContextOverride, FormDebug, HiddenField, ObjectField, Subscribe, useDerivedFieldContext, useFormError, useFormValue, } from "ui/components/form/json-schema-form"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "../../layouts/AppShell/AppShell"; export function AuthStrategiesList(props) { useBrowserTitle(["Auth", "Strategies"]); const { hasSecrets, config: { auth: { enabled }, }, } = useBknd({ withSecrets: true }); if (!hasSecrets) { return ; } else if (!enabled) { return ; } return ; } const formOptions = { keepEmpty: true, debug: isDebug(), }; function AuthStrategiesListInternal() { const $auth = useBkndAuth(); const config = $auth.config.strategies; const schema = $auth.schema.properties.strategies; const schemas = Object.fromEntries( // @ts-ignore $auth.schema.properties.strategies.additionalProperties.anyOf.map((s) => [ s.properties.type.const, s, ]), ); async function handleSubmit(data: any) { await $auth.actions.config.set({ strategies: data }); } return (
({ dirty: state.dirty, errors: state.errors.length > 0, submitting: state.submitting, })} > {({ dirty, errors, submitting }) => ( Update } > Strategies )}

Allow users to sign in or sign up using different strategies.

); } type StrategyProps = { type: "password" | "oauth" | "custom_oauth"; name: string; unavailable?: boolean; }; const Strategy = ({ type, name, unavailable }: StrategyProps) => { const errors = useFormError(name, { strict: true }); const $auth = useBkndAuth(); const schemas = Object.fromEntries( // @ts-ignore $auth.schema.properties.strategies.additionalProperties.anyOf.map((s) => [ s.properties.type.const, s, ]), ); const schema = schemas[type]; const [open, setOpen] = useState(false); if (!schema) return null; return (
0 && "border-red-500", )} >
{autoFormatString(name)}
setOpen((o) => !o)} />
{open && (
)}
); }; const StrategyToggle = ({ type }: { type: StrategyProps["type"] }) => { const ctx = useDerivedFieldContext(""); const { value } = useFormValue(""); function handleToggleChange(e: ChangeEvent) { const checked = e.target.value; const value_keys = Object.keys(value ?? {}); const can_remove = value_keys.length === 0 || (value_keys.length === 1 && value_keys[0] === "enabled"); if (!checked && can_remove) { ctx.deleteValue(ctx.path); } else { ctx.setValue([ctx.path, "enabled"].join("."), checked); } } return ; }; const StrategyIcon = ({ type, provider }: { type: StrategyProps["type"]; provider?: string }) => { if (type === "password") { return ; } if (provider && provider in OAUTH_BRANDS) { const BrandIcon = OAUTH_BRANDS[provider]; return ; } return ; }; const OAUTH_BRANDS = { google: TbBrandGoogleFilled, github: TbBrandGithubFilled, facebook: TbBrandFacebookFilled, x: TbBrandX, instagram: TbBrandInstagram, apple: TbBrandAppleFilled, discord: TbBrandDiscordFilled, }; const StrategyForm = ({ type, name }: Pick) => { let Component = (p: any) => ( ); switch (type) { case "password": Component = StrategyPasswordForm; break; case "oauth": Component = StrategyOAuthForm; break; } return ( <> ); }; const StrategyPasswordForm = () => { return ; }; const StrategyOAuthForm = ({ type, name }: Pick) => { return ( <> ); };