mirror of https://github.com/boxyhq/jackson.git
192 lines
5.5 KiB
TypeScript
192 lines
5.5 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import Link from 'next/link';
|
|
import { useEffect } from 'react';
|
|
import remarkGfm from 'remark-gfm';
|
|
import mediumZoom from 'medium-zoom';
|
|
import { MDXRemote } from 'next-mdx-remote';
|
|
import { serialize } from 'next-mdx-remote/serialize';
|
|
import type { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
|
|
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
|
import { ArrowsRightLeftIcon } from '@heroicons/react/24/outline';
|
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
|
import jackson from '@lib/jackson';
|
|
import { jacksonOptions } from '@lib/env';
|
|
import { identityProviders } from '@lib/constants';
|
|
import Footer from '@components/setup-link-instructions/Footer';
|
|
import NextButton from '@components/setup-link-instructions/NextButton';
|
|
import { InputWithCopyButton } from '@components/ClipboardButton';
|
|
import PreviousButton from '@components/setup-link-instructions/PreviousButton';
|
|
import CreateSSOConnection from '@components/setup-link-instructions/CreateSSOConnection';
|
|
import SelectIdentityProviders from '@components/setup-link-instructions/SelectIdentityProviders';
|
|
|
|
type NewConnectionProps = InferGetServerSidePropsType<typeof getServerSideProps>;
|
|
|
|
const AdvancedSPConfigLink = () => {
|
|
const { t } = useTranslation('common');
|
|
|
|
return (
|
|
<div className='py-2'>
|
|
<Link href='/.well-known/saml-configuration' target='_blank' className='underline-offset-4'>
|
|
<span className='text-xs'>{t('advanced_sp_saml_configuration')}</span>
|
|
</Link>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const components = {
|
|
Footer,
|
|
NextButton,
|
|
PreviousButton,
|
|
InputWithCopyButton,
|
|
CreateSSOConnection,
|
|
AdvancedSPConfigLink,
|
|
};
|
|
|
|
const proseClassNames = [
|
|
'prose',
|
|
'prose-sm',
|
|
'prose-h2:text-lg',
|
|
'prose-h2:text-slate-700',
|
|
'prose-h2:font-semibold',
|
|
'prose-p:text-base',
|
|
'prose-img:rounded',
|
|
'prose-img:h-96',
|
|
'prose-img:w-full',
|
|
'prose-table:table',
|
|
'prose-table:border',
|
|
'prose-a:text-sm',
|
|
];
|
|
|
|
const findIdp = (idp: string) => {
|
|
return identityProviders.find((provider) => provider.id === idp);
|
|
};
|
|
|
|
const NewConnection = ({
|
|
step,
|
|
idp,
|
|
setupLinkToken,
|
|
idpEntityId,
|
|
source,
|
|
spConfig,
|
|
spMetadataUrl,
|
|
publicCertUrl,
|
|
oidcCallbackUrl,
|
|
}: NewConnectionProps) => {
|
|
const { t } = useTranslation('common');
|
|
|
|
const linkSelectIdp = { pathname: '/setup/[token]/sso-connection/new', query: { token: setupLinkToken } };
|
|
|
|
const scope = {
|
|
idpEntityId,
|
|
spConfig,
|
|
setupLinkToken,
|
|
spMetadataUrl,
|
|
publicCertUrl,
|
|
oidcCallbackUrl,
|
|
};
|
|
|
|
let heading = '';
|
|
let progress = 0;
|
|
|
|
// Add zoom to images
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') return;
|
|
|
|
const zoom = mediumZoom('img', {
|
|
scrollOffset: 0,
|
|
margin: 50,
|
|
});
|
|
|
|
return () => {
|
|
zoom.detach();
|
|
};
|
|
}, [step]);
|
|
|
|
if (source && idp && step) {
|
|
const selectedIdP = findIdp(idp);
|
|
|
|
if (!selectedIdP) {
|
|
throw new Error('Identity Provider not found');
|
|
}
|
|
|
|
progress = (100 / selectedIdP.stepCount) * parseInt(step);
|
|
heading = t('configure_identity_provider', { provider: selectedIdP.name });
|
|
} else {
|
|
heading = t('select_identity_provider');
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className='flex space-y-4 flex-col pb-6'>
|
|
<div className='flex justify-between items-center'>
|
|
<h1 className='text-xl font-noraml'>{heading}</h1>
|
|
{source && (
|
|
<Link className='btn btn-xs h-0' href={linkSelectIdp}>
|
|
<ArrowsRightLeftIcon className='w-5 h-5' />
|
|
{t('change_identity_provider')}
|
|
</Link>
|
|
)}
|
|
</div>
|
|
{source && <progress className='progress progress-primary w-full' value={progress} max={100} />}
|
|
</div>
|
|
|
|
<article className={`${proseClassNames.join(' ')} max-w-4xl`}>
|
|
{source ? (
|
|
<MDXRemote {...source} components={components} scope={scope} />
|
|
) : (
|
|
<SelectIdentityProviders />
|
|
)}
|
|
</article>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export async function getServerSideProps({ locale, query }: GetServerSidePropsContext) {
|
|
const { spConfig, setupLinkController, connectionAPIController } = await jackson();
|
|
|
|
const { idp, step, token } = query as { idp: string; step: string; token: string };
|
|
|
|
const { tenant, product } = await setupLinkController.getByToken(token);
|
|
const idpEntityId = connectionAPIController.getIDPEntityID({
|
|
tenant,
|
|
product,
|
|
});
|
|
|
|
let mdxSource: MDXRemoteSerializeResult<Record<string, unknown>> | null = null;
|
|
|
|
// Read the MDX file based on the idp and step
|
|
if (idp && step) {
|
|
try {
|
|
const mdxDirectory = path.join(process.cwd(), `components/setup-link-instructions/${idp}`);
|
|
const source = fs.readFileSync(`${mdxDirectory}/${step}.mdx`, 'utf8');
|
|
mdxSource = await serialize(source, { mdxOptions: { remarkPlugins: [remarkGfm] } });
|
|
} catch (error: any) {
|
|
return {
|
|
redirect: {
|
|
destination: `/setup/${token}/sso-connection/new`,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
props: {
|
|
idp: idp || null,
|
|
step: step || null,
|
|
setupLinkToken: token,
|
|
idpEntityId,
|
|
source: mdxSource,
|
|
spConfig: await spConfig.get(),
|
|
spMetadataUrl: `${jacksonOptions.externalUrl}/.well-known/sp-metadata`,
|
|
publicCertUrl: `${jacksonOptions.externalUrl}/.well-known/saml.cer`,
|
|
oidcCallbackUrl: `${jacksonOptions.externalUrl}${jacksonOptions.oidcPath}`,
|
|
...(locale ? await serverSideTranslations(locale, ['common']) : {}),
|
|
},
|
|
};
|
|
}
|
|
|
|
export default NewConnection;
|