0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(console): outline guide

This commit is contained in:
Gao Sun 2023-08-16 17:41:30 +08:00
parent 00866e0eba
commit a40a4443db
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
12 changed files with 206 additions and 14 deletions

View file

@ -56,8 +56,7 @@ for (const { name, logo } of metadata) {
Logo: ${logo ? `lazy(async () => import('./${name}/${logo}'))` : 'undefined'},
Component: lazy(async () => import('./${name}/README.mdx')),
metadata: ${camelCase(name)},
},
`
},`
);
}

View file

@ -21,6 +21,7 @@ import webGptPlugin from './web-gpt-plugin/index';
import webJava from './web-java/index';
import webNext from './web-next/index';
import webNextAppRouter from './web-next-app-router/index';
import webOutline from './web-outline/index';
import webPhp from './web-php/index';
import webPython from './web-python/index';
@ -103,19 +104,18 @@ const guides: Readonly<Guide[]> = Object.freeze([
Component: lazy(async () => import('./web-express/README.mdx')),
metadata: webExpress,
},
{
id: 'web-gpt-plugin',
Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')),
Component: lazy(async () => import('./web-gpt-plugin/README.mdx')),
metadata: webGptPlugin,
},
{
id: 'web-go',
Logo: lazy(async () => import('./web-go/logo.svg')),
Component: lazy(async () => import('./web-go/README.mdx')),
metadata: webGo,
},
{
id: 'web-gpt-plugin',
Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')),
Component: lazy(async () => import('./web-gpt-plugin/README.mdx')),
metadata: webGptPlugin,
},
{
id: 'web-java',
Logo: lazy(async () => import('./web-java/logo.svg')),
@ -134,6 +134,12 @@ const guides: Readonly<Guide[]> = Object.freeze([
Component: lazy(async () => import('./web-next-app-router/README.mdx')),
metadata: webNextAppRouter,
},
{
id: 'web-outline',
Logo: lazy(async () => import('./web-outline/logo.svg')),
Component: lazy(async () => import('./web-outline/README.mdx')),
metadata: webOutline,
},
{
id: 'web-php',
Logo: lazy(async () => import('./web-php/logo.svg')),

View file

@ -11,9 +11,7 @@ import logtoSignInExperience from './assets/logto-sign-in-experience.png';
<Steps>
<Step
title="Prerequisites"
>
<Step title="Prerequisites">
[ChatGPT plugins](https://openai.com/blog/chatgpt-plugins) are tools designed specifically for language models, and help ChatGPT access up-to-date information, run computations, or use third-party services.

View file

@ -0,0 +1,72 @@
import UriInputField from '@/mdx-components-v2/UriInputField';
import Steps from '@/mdx-components-v2/Steps';
import Step from '@/mdx-components-v2/Step';
import logtoSignInExperience from './assets/logto-sign-in-experience.png';
import outlineHome from './assets/outline-home.png';
import outlineSignIn from './assets/outline-sign-in.png';
import EnvironmentVariables from './components/EnvironmentVariables';
<Steps>
<Step title="Prerequisites">
[Outline](https://github.com/outline/outline) serves as a knowledge base for growing teams. To get started, you need an Outline hosting environment with access to environment variables.
Follow the steps outlined in the [Outline hosting guide](https://docs.getoutline.com/s/hosting/) until you reach the authentication configuration step. We'll leverage the Outline support of [OIDC-compatible authentication providers](https://docs.getoutline.com/s/hosting/doc/oidc-8CPBm6uC0I) in this guide.
</Step>
<Step title="Configure redirect URI">
Add a redirect URI by replacing the origin to your outline host origin.
<UriInputField name="redirectUris" defaultValue="[your-outline-origin]/auth/oidc.callback" />
For example, if your Outline is hosted at `https://outline.example.com`, the redirect URI should be `https://outline.example.com/auth/oidc.callback`.
Don't forget to click the **Save** button.
</Step>
<Step title="Set the outline environment variables">
Refer to the following table for the necessary configuration details:
<EnvironmentVariables />
Here's another table containing additional variables:
| Outline Environment Variable | Description |
| ---------------------------- | ------------------------------ |
| OIDC_USERNAME_CLAIM | Set to `username` |
| OIDC_DISPLAY_NAME | Optional - customize as needed |
| OIDC_SCOPES | Keep default; no need to set |
</Step>
<Step title="Set up email sign-in in Logto">
Since Outline requires user email to be provided, you need to configure email sign-in or a social sign-in that provides trustworthy email address, such as Google sign-in.
See [Passwordless sign-in by adding connectors](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors/) to learn more about configuring passwordless sign-in in Logto.
</Step>
<Step title="Checkpoint: Test Logto and Outline integration">
Start the Outline instance and access its endpoint. You should see a button in the center labeled "Continue with OpenID Connect"; it can be customized by setting the `OIDC_DISPLAY_NAME` environment variable.
<img alt="Outline sign-in page" src={outlineSignIn} width="360" />
Click on the button, and you will be directed to the Logto sign-in experience.
<img alt="Logto sign-in experience" src={logtoSignInExperience} width="360" />
If everything has been configured correctly, once you complete the sign-in or registration process in Logto, you will be redirected back to Outline. You can then see your personal information displayed in the bottom left corner of the page.
<img alt="Outline home" src={outlineHome} width="660" />
</Step>
</Steps>

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,67 @@
import { type SnakeCaseOidcConfig } from '@logto/schemas';
import { useContext } from 'react';
import useSWR from 'swr';
import { openIdProviderConfigPath } from '@/consts/oidc';
import CopyToClipboard from '@/ds-components/CopyToClipboard';
import { type RequestError } from '@/hooks/use-api';
import { GuideContext } from '@/pages/Applications/components/GuideV2';
export default function EnvironmentVariables() {
const {
app: { id, secret },
} = useContext(GuideContext);
const { data } = useSWR<SnakeCaseOidcConfig, RequestError>(openIdProviderConfigPath);
const authorizationEndpoint = data?.authorization_endpoint ?? '[LOADING]';
const tokenEndpoint = data?.token_endpoint ?? '[LOADING]';
const userinfoEndpoint = data?.userinfo_endpoint ?? '[LOADING]';
return (
<table>
<thead>
<tr>
<th>Outline Environment Variable</th>
<th>Logto Display Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>OIDC_CLIENT_ID</td>
<td>App ID</td>
<td>
<CopyToClipboard value={id} />
</td>
</tr>
<tr>
<td>OIDC_CLIENT_SECRET</td>
<td>App secret</td>
<td>
<CopyToClipboard value={secret} />
</td>
</tr>
<tr>
<td>OIDC_AUTH_URI</td>
<td>Authorization endpoint</td>
<td>
<CopyToClipboard value={authorizationEndpoint} />
</td>
</tr>
<tr>
<td>OIDC_TOKEN_URI</td>
<td>Token endpoint</td>
<td>
<CopyToClipboard value={tokenEndpoint} />
</td>
</tr>
<tr>
<td>OIDC_USERINFO_URI</td>
<td>Userinfo endpoint</td>
<td>
<CopyToClipboard value={userinfoEndpoint} />
</td>
</tr>
</tbody>
</table>
);
}

View file

@ -0,0 +1,11 @@
import { ApplicationType } from '@logto/schemas';
import { type GuideMetadata } from '../types';
const metadata: Readonly<GuideMetadata> = Object.freeze({
name: 'Outline',
description: 'Use Logto as an identity provider for Outline',
target: ApplicationType.Traditional,
});
export default metadata;

View file

@ -0,0 +1,5 @@
<svg viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M154.667 213.441V218.65C154.667 226.023 148.697 232 141.333 232C139.044 232 136.792 231.41 134.797 230.286L54.7965 185.229C50.5982 182.865 48 178.417 48 173.594V82.4088C48 77.5858 50.5982 73.1377 54.7965 70.7732L134.797 25.7168C141.215 22.1021 149.344 24.3813 152.954 30.8074C154.077 32.8057 154.667 35.0597 154.667 37.3524V42.561L164.169 39.7068C171.222 37.5882 178.655 41.5956 180.771 48.6577C181.144 49.9022 181.333 51.1945 181.333 52.4938V57.9135L193.013 56.4517C200.32 55.5372 206.984 60.7267 207.897 68.0428C207.966 68.5921 208 69.1451 208 69.6987V186.304C208 193.677 202.03 199.654 194.667 199.654C194.114 199.654 193.561 199.619 193.013 199.551L181.333 198.089V203.509C181.333 210.882 175.364 216.859 168 216.859C166.702 216.859 165.412 216.669 164.169 216.296L154.667 213.441ZM154.667 199.504L168 203.509V52.4938L154.667 56.4988V199.504ZM181.333 71.3674V184.635L194.667 186.304V69.6987L181.333 71.3674ZM61.3333 82.4088V173.594L141.333 218.65V37.3524L61.3333 82.4088ZM74.6667 91.2886L88 84.6136V171.389L74.6667 164.714V91.2886Z"
fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -25,6 +25,29 @@
margin-block: _.unit(2);
padding-inline-start: _.unit(1);
}
section table {
border-spacing: 0;
border: 1px solid var(--color-border);
font: var(--font-body-2);
tr {
width: 100%;
}
td,
th {
padding: _.unit(2) _.unit(4);
}
thead {
font: var(--font-title-3);
}
tbody td {
border-top: 1px solid var(--color-border);
}
}
}
}

View file

@ -39,8 +39,19 @@ type GuideContextType = {
};
};
// eslint-disable-next-line no-restricted-syntax
export const GuideContext = createContext<GuideContextType>({} as GuideContextType);
export const GuideContext = createContext<GuideContextType>({
// The following `as` is for context initialization, they won't be used in production except for
// HMR.
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, no-restricted-syntax
metadata: {} as GuideMetadata,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, no-restricted-syntax
app: {} as Application,
endpoint: '',
redirectUris: [],
postLogoutRedirectUris: [],
isCompact: false,
sampleUrls: { origin: '', callback: '' },
});
type Props = {
guideId: string;