docs: ✨ swizzle doc page to implement github auth access
Signed-off-by: slumbering <slumbering.pierrot@gmail.com>
This commit is contained in:
58
website/src/api/github.js
Normal file
58
website/src/api/github.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const AxiosInstance = axios.create({
|
||||
headers: { 'Accept': 'application/vnd.github.v3+json' },
|
||||
});
|
||||
|
||||
async function getAccessToken(code) {
|
||||
|
||||
try {
|
||||
const getAccessToken = await AxiosInstance.get('https://github.com/login/oauth/access_token', {
|
||||
params: {
|
||||
code,
|
||||
client_id: process.env.REACT_APP_CLIENT_ID,
|
||||
client_secret: process.env.REACT_APP_CLIENT_SECRET,
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // Resolve only if the status code is less than 500
|
||||
}
|
||||
})
|
||||
|
||||
return getAccessToken.data;
|
||||
} catch (error) {
|
||||
console.log("error getAccessToken", error.message)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUser(access_token) {
|
||||
try {
|
||||
const getUserLogin = await AxiosInstance.get("https://api.github.com/user", {
|
||||
headers: { Authorization: `token ${access_token}` },
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // Resolve only if the status code is less than 500
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
login: getUserLogin.data.login,
|
||||
status: getUserLogin.status
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error getUser", error.message)
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkUserCollaboratorStatus(code) {
|
||||
const { access_token } = await getAccessToken(code)
|
||||
const { login } = await getUser(access_token)
|
||||
try {
|
||||
const isUserCollaborator = await AxiosInstance.get(`https://docs-access.dagger.io/u/${login}`)
|
||||
|
||||
return {
|
||||
status: isUserCollaborator.status,
|
||||
access_token
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error checkUserCollaboratorStatus", error.message);
|
||||
}
|
||||
}
|
13
website/src/components/DocAuthentication.js
Normal file
13
website/src/components/DocAuthentication.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import { GithubLoginButton } from 'react-social-login-buttons';
|
||||
import style from './DocAuthentication.module.css'
|
||||
|
||||
export default function DocAuthentication() {
|
||||
return (
|
||||
<div className={style.container}>
|
||||
<h1 className={style.h1}>Welcome on Dagger documentation</h1>
|
||||
<p>Please Sign in to Github in order to get access to the doc</p>
|
||||
<GithubLoginButton className={style.btn__github} onClick={() => window.location.href = `//github.com/login/oauth/authorize?client_id=${process.env.REACT_APP_CLIENT_ID}&scope=user&allow_signup=false`} />
|
||||
</div>
|
||||
)
|
||||
}
|
13
website/src/components/DocAuthentication.module.css
Normal file
13
website/src/components/DocAuthentication.module.css
Normal file
@@ -0,0 +1,13 @@
|
||||
.container {
|
||||
background: url("/img/Dagger_Website_Space_Uranus.png") no-repeat;
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn__github {
|
||||
width: 240px !important;
|
||||
}
|
8
website/src/components/Spinner.js
Normal file
8
website/src/components/Spinner.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import styles from './Spinner.module.css';
|
||||
|
||||
export default function Spinner() {
|
||||
return (
|
||||
<div className={styles.ellipsis}><div></div><div></div><div></div><div></div></div>
|
||||
)
|
||||
}
|
64
website/src/components/Spinner.module.css
Normal file
64
website/src/components/Spinner.module.css
Normal file
@@ -0,0 +1,64 @@
|
||||
.ellipsis {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 50vh;
|
||||
z-index: 99999;
|
||||
left: 50vw;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.ellipsis div {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: var(--ifm-color-primary-dark);
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .ellipsis div {
|
||||
background: var(--ifm-color-primary-light);
|
||||
}
|
||||
|
||||
.ellipsis div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation: lds-ellipsis1 0.6s infinite;
|
||||
}
|
||||
.ellipsis div:nth-child(2) {
|
||||
left: 8px;
|
||||
animation: lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
.ellipsis div:nth-child(3) {
|
||||
left: 32px;
|
||||
animation: lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
.ellipsis div:nth-child(4) {
|
||||
left: 56px;
|
||||
animation: lds-ellipsis3 0.6s infinite;
|
||||
}
|
||||
@keyframes lds-ellipsis1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes lds-ellipsis3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
@keyframes lds-ellipsis2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(24px, 0);
|
||||
}
|
||||
}
|
195
website/src/theme/DocPage/index.js
Normal file
195
website/src/theme/DocPage/index.js
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import renderRoutes from '@docusaurus/renderRoutes';
|
||||
import Layout from '@theme/Layout';
|
||||
import DocSidebar from '@theme/DocSidebar';
|
||||
import MDXComponents from '@theme/MDXComponents';
|
||||
import NotFound from '@theme/NotFound';
|
||||
import IconArrow from '@theme/IconArrow';
|
||||
import { matchPath } from '@docusaurus/router';
|
||||
import { translate } from '@docusaurus/Translate';
|
||||
import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
import { ThemeClassNames, docVersionSearchTag } from '@docusaurus/theme-common';
|
||||
import { Redirect } from "react-router";
|
||||
import qs from 'querystringify';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { checkUserCollaboratorStatus, getUser } from '../../api/github'
|
||||
import { GithubLoginButton } from 'react-social-login-buttons';
|
||||
import Spinner from '../../components/Spinner';
|
||||
import DocAuthentication from '../../components/DocAuthentication';
|
||||
|
||||
function DocPageContent({ currentDocRoute, versionMetadata, children }) {
|
||||
const { siteConfig, isClient } = useDocusaurusContext();
|
||||
const { pluginId, permalinkToSidebar, docsSidebars, version } = versionMetadata;
|
||||
const sidebarName = permalinkToSidebar[currentDocRoute.path];
|
||||
const sidebar = docsSidebars[sidebarName];
|
||||
const [hiddenSidebarContainer, setHiddenSidebarContainer] = useState(false);
|
||||
const [hiddenSidebar, setHiddenSidebar] = useState(false);
|
||||
const toggleSidebar = useCallback(() => {
|
||||
if (hiddenSidebar) {
|
||||
setHiddenSidebar(false);
|
||||
}
|
||||
|
||||
setHiddenSidebarContainer(!hiddenSidebarContainer);
|
||||
}, [hiddenSidebar]);
|
||||
return (
|
||||
<Layout
|
||||
key={isClient}
|
||||
wrapperClassName={ThemeClassNames.wrapper.docPages}
|
||||
pageClassName={ThemeClassNames.page.docPage}
|
||||
searchMetadatas={{
|
||||
version,
|
||||
tag: docVersionSearchTag(pluginId, version),
|
||||
}}>
|
||||
<div className={styles.docPage}>
|
||||
{sidebar && (
|
||||
<div
|
||||
className={clsx(styles.docSidebarContainer, {
|
||||
[styles.docSidebarContainerHidden]: hiddenSidebarContainer,
|
||||
})}
|
||||
onTransitionEnd={(e) => {
|
||||
if (
|
||||
!e.currentTarget.classList.contains(styles.docSidebarContainer)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hiddenSidebarContainer) {
|
||||
setHiddenSidebar(true);
|
||||
}
|
||||
}}
|
||||
role="complementary">
|
||||
<DocSidebar
|
||||
key={
|
||||
// Reset sidebar state on sidebar changes
|
||||
// See https://github.com/facebook/docusaurus/issues/3414
|
||||
sidebarName
|
||||
}
|
||||
sidebar={sidebar}
|
||||
path={currentDocRoute.path}
|
||||
sidebarCollapsible={
|
||||
siteConfig.themeConfig?.sidebarCollapsible ?? true
|
||||
}
|
||||
onCollapse={toggleSidebar}
|
||||
isHidden={hiddenSidebar}
|
||||
/>
|
||||
|
||||
{hiddenSidebar && (
|
||||
<div
|
||||
className={styles.collapsedDocSidebar}
|
||||
title={translate({
|
||||
id: 'theme.docs.sidebar.expandButtonTitle',
|
||||
message: 'Expand sidebar',
|
||||
description:
|
||||
'The ARIA label and title attribute for expand button of doc sidebar',
|
||||
})}
|
||||
aria-label={translate({
|
||||
id: 'theme.docs.sidebar.expandButtonAriaLabel',
|
||||
message: 'Expand sidebar',
|
||||
description:
|
||||
'The ARIA label and title attribute for expand button of doc sidebar',
|
||||
})}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onKeyDown={toggleSidebar}
|
||||
onClick={toggleSidebar}>
|
||||
<IconArrow className={styles.expandSidebarButtonIcon} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<main
|
||||
className={clsx(styles.docMainContainer, {
|
||||
[styles.docMainContainerEnhanced]:
|
||||
hiddenSidebarContainer || !sidebar,
|
||||
})}>
|
||||
<div
|
||||
className={clsx(
|
||||
'container padding-vert--lg',
|
||||
styles.docItemWrapper,
|
||||
{
|
||||
[styles.docItemWrapperEnhanced]: hiddenSidebarContainer,
|
||||
},
|
||||
)}>
|
||||
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
function DocPage(props) {
|
||||
const {
|
||||
route: { routes: docRoutes },
|
||||
versionMetadata,
|
||||
location,
|
||||
} = props;
|
||||
const currentDocRoute = docRoutes.find((docRoute) =>
|
||||
matchPath(location.pathname, docRoute),
|
||||
);
|
||||
|
||||
// CUSTOM DOCPAGE
|
||||
const [isUserAuthorized, setIsUserAuthorized] = useState()
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [redirectState, setRedirectState] = useState()
|
||||
const authQuery = qs.parse(location.search);
|
||||
const [userAccessToken, setUserAccessToken] = useState((() => {
|
||||
if (typeof window !== "undefined") return window.localStorage.getItem('user-github-key')
|
||||
})())
|
||||
|
||||
useEffect(async () => {
|
||||
if (userAccessToken) {
|
||||
const user = await getUser(userAccessToken)
|
||||
setIsUserAuthorized(user)
|
||||
} else {
|
||||
if (!isEmpty(authQuery)) { //callback after successful auth with github)
|
||||
const isUserCollaborator = await checkUserCollaboratorStatus(authQuery.code);
|
||||
if (isUserCollaborator?.status === 200) {
|
||||
setUserAccessToken(isUserCollaborator.access_token)
|
||||
if (typeof window !== "undefined") window.localStorage.setItem('user-github-key', isUserCollaborator.access_token);
|
||||
} else {
|
||||
setIsUserAuthorized({ status: 401 })
|
||||
}
|
||||
}
|
||||
}
|
||||
setIsLoading(false)
|
||||
}, [userAccessToken])
|
||||
|
||||
|
||||
if (isLoading) return <Spinner />
|
||||
|
||||
if ((isUserAuthorized?.status && isUserAuthorized?.status === 401)) {
|
||||
return <p>Redirection vers dagger.io...</p>
|
||||
}
|
||||
|
||||
if (!isUserAuthorized) {
|
||||
return (
|
||||
<DocAuthentication />
|
||||
)
|
||||
}
|
||||
|
||||
// END CUSTOM DOCPAGE
|
||||
|
||||
if (!currentDocRoute) {
|
||||
return <NotFound {...props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<DocPageContent
|
||||
currentDocRoute={currentDocRoute}
|
||||
versionMetadata={versionMetadata}>
|
||||
{renderRoutes(docRoutes)}
|
||||
</DocPageContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default DocPage;
|
98
website/src/theme/DocPage/styles.module.css
Normal file
98
website/src/theme/DocPage/styles.module.css
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--doc-sidebar-width: 300px;
|
||||
}
|
||||
|
||||
:global(.docs-wrapper) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.docPage,
|
||||
.docMainContainer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 997px) {
|
||||
.docMainContainer {
|
||||
flex-grow: 1;
|
||||
max-width: calc(100% - var(--doc-sidebar-width));
|
||||
}
|
||||
|
||||
.docMainContainerEnhanced {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.docSidebarContainer {
|
||||
width: var(--doc-sidebar-width);
|
||||
margin-top: calc(-1 * var(--ifm-navbar-height));
|
||||
border-right: 1px solid var(--ifm-toc-border-color);
|
||||
will-change: width;
|
||||
transition: width var(--ifm-transition-fast) ease;
|
||||
clip-path: inset(0);
|
||||
}
|
||||
|
||||
.docSidebarContainerHidden {
|
||||
width: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collapsedDocSidebar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color var(--ifm-transition-fast) ease;
|
||||
}
|
||||
|
||||
.collapsedDocSidebar:hover,
|
||||
.collapsedDocSidebar:focus {
|
||||
background-color: var(--ifm-color-emphasis-200);
|
||||
}
|
||||
|
||||
.expandSidebarButtonIcon {
|
||||
transform: rotate(0);
|
||||
}
|
||||
html[dir='rtl'] .expandSidebarButtonIcon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .collapsedDocSidebar:hover,
|
||||
html[data-theme='dark'] .collapsedDocSidebar:focus {
|
||||
background-color: var(--collapse-button-bg-color-dark);
|
||||
}
|
||||
|
||||
.docItemWrapperEnhanced {
|
||||
max-width: calc(var(--ifm-container-width) + var(--doc-sidebar-width));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.docSidebarContainer {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 997px) and (max-width: 1320px) {
|
||||
.docItemWrapper {
|
||||
max-width: calc(
|
||||
var(--ifm-container-width) - var(--doc-sidebar-width) -
|
||||
var(--ifm-spacing-horizontal) * 2
|
||||
);
|
||||
}
|
||||
|
||||
.docItemWrapperEnhanced {
|
||||
max-width: calc(
|
||||
var(--ifm-container-width) - var(--ifm-spacing-horizontal) * 2
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user