docs: swizzle doc page to implement github auth access

Signed-off-by: slumbering <slumbering.pierrot@gmail.com>
This commit is contained in:
slumbering 2021-06-11 12:24:58 +02:00
parent 5a83804cc2
commit 38ee07e5f9
12 changed files with 488 additions and 3 deletions

2
website/.env Normal file
View File

@ -0,0 +1,2 @@
REACT_APP_CLIENT_ID=cd8f9be2562bfc8d6cfc
REACT_APP_CLIENT_SECRET=4856ebc1101d1228e21b0c9705e8d08105804a3e

View File

@ -12,8 +12,6 @@ module.exports = {
organizationName: "Dagger",
projectName: "Dagger",
stylesheets: [
"https://fonts.gstatic.com",
"https://fonts.googleapis.com/css2?family=Poppins:wght@700&display=swap",
"https://fonts.googleapis.com/css2?family=Karla&family=Poppins:wght@700&display=swap",
],
themeConfig: {
@ -58,5 +56,8 @@ module.exports = {
},
],
],
plugins: ["docusaurus-plugin-sass"],
plugins: [
"docusaurus-plugin-sass",
"docusaurus2-dotenv"
],
};

View File

@ -20,9 +20,11 @@
"@svgr/webpack": "^5.5.0",
"clsx": "^1.1.1",
"docusaurus-plugin-sass": "^0.2.0",
"docusaurus2-dotenv": "^1.4.0",
"file-loader": "^6.2.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-social-login-buttons": "^3.4.0",
"sass": "^1.34.1",
"url-loader": "^4.1.1"
},

58
website/src/api/github.js Normal file
View 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);
}
}

View 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>
)
}

View 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;
}

View 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>
)
}

View 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);
}
}

View 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;

View 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
);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -3382,6 +3382,13 @@ docusaurus-plugin-sass@^0.2.0:
dependencies:
sass-loader "^10.1.1"
docusaurus2-dotenv@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/docusaurus2-dotenv/-/docusaurus2-dotenv-1.4.0.tgz#9ab900e29de9081f9f1f28f7224ff63760385641"
integrity sha512-iWqem5fnBAyeBBtX75Fxp71uUAnwFaXzOmade8zAhN4vL3RG9m27sLSRwjJGVVgIkEo3esjGyCcTGTiCjfi+sg==
dependencies:
dotenv-webpack "1.7.0"
dom-converter@^0.2:
version "0.2.0"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@ -3478,6 +3485,25 @@ dot-prop@^5.2.0:
dependencies:
is-obj "^2.0.0"
dotenv-defaults@^1.0.2:
version "1.1.1"
resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-1.1.1.tgz#032c024f4b5906d9990eb06d722dc74cc60ec1bd"
integrity sha512-6fPRo9o/3MxKvmRZBD3oNFdxODdhJtIy1zcJeUSCs6HCy4tarUpd+G67UTU9tF6OWXeSPqsm4fPAB+2eY9Rt9Q==
dependencies:
dotenv "^6.2.0"
dotenv-webpack@1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-1.7.0.tgz#4384d8c57ee6f405c296278c14a9f9167856d3a1"
integrity sha512-wwNtOBW/6gLQSkb8p43y0Wts970A3xtNiG/mpwj9MLUhtPCQG6i+/DSXXoNN7fbPCU/vQ7JjwGmgOeGZSSZnsw==
dependencies:
dotenv-defaults "^1.0.2"
dotenv@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@ -6891,6 +6917,11 @@ react-side-effect@^2.1.0:
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3"
integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==
react-social-login-buttons@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/react-social-login-buttons/-/react-social-login-buttons-3.4.0.tgz#7b3f249c661624435e01881cf163ef4c4a960409"
integrity sha512-bECnTNfRSPKMnwVdfsLNNi0Fv5A8btJw3ANLg1Zj95DCjP11KPKUuhqOj9AQWLmQeyrLjMO7orEFE5M2QiDY9A==
react-textarea-autosize@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.2.tgz#4f9374d357b0a6f6469956726722549124a1b2db"