From 02b47157e1e5648fbd5543c7d7ae114571636abe Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Wed, 9 Jun 2021 18:28:51 -0700 Subject: [PATCH 1/8] doc generation: refactor to allow multi-stage processing Signed-off-by: Andrea Luzzardi Signed-off-by: slumbering --- cmd/dagger/cmd/doc.go | 408 +++++++++++++++++++++--------------------- 1 file changed, 207 insertions(+), 201 deletions(-) diff --git a/cmd/dagger/cmd/doc.go b/cmd/dagger/cmd/doc.go index 836423d0..447fa771 100644 --- a/cmd/dagger/cmd/doc.go +++ b/cmd/dagger/cmd/doc.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io" "io/fs" "os" "path" @@ -32,25 +31,217 @@ const ( textPadding = " " ) -// types used for json generation - -type ValueJSON struct { +type Value struct { Name string Type string Description string } -type FieldJSON struct { +type Field struct { Name string Description string - Inputs []ValueJSON - Outputs []ValueJSON + Inputs []Value + Outputs []Value } -type PackageJSON struct { +type Package struct { Name string Description string - Fields []FieldJSON + Fields []Field +} + +func Parse(ctx context.Context, packageName string, val *compiler.Value) *Package { + lg := log.Ctx(ctx) + + parseValues := func(field string, values []*compiler.Value) []Value { + val := []Value{} + + for _, i := range values { + v := Value{} + v.Name = strings.TrimPrefix( + i.Path().String(), + field+".", + ) + v.Type = common.FormatValue(i) + v.Description = common.ValueDocOneLine(i) + val = append(val, v) + } + + return val + } + + fields, err := val.Fields(cue.Definitions(true)) + if err != nil { + lg.Fatal().Err(err).Msg("cannot get fields") + } + + pkg := &Package{} + // Package Name + Description + pkg.Name = packageName + pkg.Description = common.ValueDocFull(val) + + // Package Fields + for _, f := range fields { + field := Field{} + + if !f.Selector.IsDefinition() { + // not a definition, skipping + continue + } + + name := f.Label() + v := f.Value + if v.Cue().IncompleteKind() != cue.StructKind { + // not a struct, skipping + continue + } + + // Field Name + Description + field.Name = name + field.Description = common.ValueDocOneLine(v) + + // Inputs + inp := environment.ScanInputs(ctx, v) + field.Inputs = parseValues(field.Name, inp) + + // Outputs + out := environment.ScanOutputs(ctx, v) + field.Outputs = parseValues(field.Name, out) + + pkg.Fields = append(pkg.Fields, field) + } + + return pkg +} + +func (p *Package) Format(f string) string { + switch f { + case textFormat: + return p.Text() + case jsonFormat: + return p.JSON() + case markdownFormat: + return p.Markdown() + default: + panic(f) + } +} + +func (p *Package) JSON() string { + data, err := json.MarshalIndent(p, "", " ") + if err != nil { + panic(err) + } + return fmt.Sprintf("%s\n", data) +} + +func (p *Package) Text() string { + w := &strings.Builder{} + fmt.Fprintf(w, "Package %s\n", p.Name) + fmt.Fprintf(w, "\n%s\n", p.Description) + + printValuesText := func(values []Value) { + tw := tabwriter.NewWriter(w, 0, 4, len(textPadding), ' ', 0) + for _, i := range values { + fmt.Fprintf(tw, "\t\t%s\t%s\t%s\n", + i.Name, i.Type, terminalTrim(i.Description)) + } + tw.Flush() + } + + // Package Fields + for _, field := range p.Fields { + fmt.Fprintf(w, "\n%s\n\n%s%s\n", field.Name, textPadding, field.Description) + if len(field.Inputs) == 0 { + fmt.Fprintf(w, "\n%sInputs: none\n", textPadding) + } else { + fmt.Fprintf(w, "\n%sInputs:\n", textPadding) + printValuesText(field.Inputs) + } + + if len(field.Outputs) == 0 { + fmt.Fprintf(w, "\n%sOutputs: none\n", textPadding) + } else { + fmt.Fprintf(w, "\n%sOutputs:\n", textPadding) + printValuesText(field.Outputs) + } + } + + return w.String() +} + +func terminalTrim(msg string) string { + // If we're not running on a terminal, return the whole string + size, _, err := term.GetSize(1) + if err != nil { + return msg + } + + // Otherwise, trim to fit half the terminal + size /= 2 + for utf8.RuneCountInString(msg) > size { + msg = msg[0:len(msg)-4] + "…" + } + return msg +} + +func (p *Package) Markdown() string { + w := &strings.Builder{} + + fmt.Fprintf(w, "---\nsidebar_label: %s\n---\n\n", + filepath.Base(p.Name), + ) + + fmt.Fprintf(w, "# %s\n", mdEscape(p.Name)) + if p.Description != "-" { + fmt.Fprintf(w, "\n%s\n", mdEscape(p.Description)) + } + + printValuesMarkdown := func(values []Value) { + tw := tabwriter.NewWriter(w, 0, 4, len(textPadding), ' ', 0) + fmt.Fprintf(tw, "| Name\t| Type\t| Description \t|\n") + fmt.Fprintf(tw, "| -------------\t|:-------------:\t|:-------------:\t|\n") + for _, i := range values { + fmt.Fprintf(tw, "|*%s*\t| `%s`\t|%s\t|\n", + i.Name, + mdEscape(i.Type), + mdEscape(i.Description), + ) + } + tw.Flush() + } + + // Package Fields + for _, field := range p.Fields { + fmt.Fprintf(w, "\n## %s\n\n", field.Name) + if field.Description != "-" { + fmt.Fprintf(w, "%s\n\n", mdEscape(field.Description)) + } + + fmt.Fprintf(w, "### %s Inputs\n\n", mdEscape(field.Name)) + if len(field.Inputs) == 0 { + fmt.Fprintf(w, "_No input._\n") + } else { + printValuesMarkdown(field.Inputs) + } + + fmt.Fprintf(w, "\n### %s Outputs\n\n", mdEscape(field.Name)) + if len(field.Outputs) == 0 { + fmt.Fprintf(w, "_No output._\n") + } else { + printValuesMarkdown(field.Outputs) + } + } + + return w.String() +} + +func mdEscape(s string) string { + escape := []string{"|", "<", ">"} + for _, c := range escape { + s = strings.ReplaceAll(s, c, `\`+c) + } + return s } var docCmd = &cobra.Command{ @@ -94,7 +285,8 @@ var docCmd = &cobra.Command{ if err != nil { lg.Fatal().Err(err).Msg("cannot compile code") } - PrintDoc(ctx, os.Stdout, packageName, val, format) + p := Parse(ctx, packageName, val) + fmt.Printf("%s", p.Format(format)) }, } @@ -107,34 +299,6 @@ func init() { } } -func mdEscape(s string) string { - escape := []string{"|", "<", ">"} - for _, c := range escape { - s = strings.ReplaceAll(s, c, `\`+c) - } - return s -} - -func terminalTrim(msg string) string { - // If we're not running on a terminal, return the whole string - size, _, err := term.GetSize(1) - if err != nil { - return msg - } - - // Otherwise, trim to fit half the terminal - size /= 2 - for utf8.RuneCountInString(msg) > size { - msg = msg[0:len(msg)-4] + "…" - } - return msg -} - -func formatLabel(name string, val *compiler.Value) string { - label := val.Path().String() - return strings.TrimPrefix(label, name+".") -} - func loadCode(packageName string) (*compiler.Value, error) { sources := map[string]fs.FS{ stdlib.Path: stdlib.FS, @@ -148,165 +312,6 @@ func loadCode(packageName string) (*compiler.Value, error) { return src, nil } -// printValuesText (text) formats an array of Values on stdout -func printValuesText(iw io.Writer, libName string, values []*compiler.Value) { - w := tabwriter.NewWriter(iw, 0, 4, len(textPadding), ' ', 0) - for _, i := range values { - docStr := terminalTrim(common.ValueDocOneLine(i)) - fmt.Fprintf(w, "\t\t%s\t%s\t%s\n", - formatLabel(libName, i), common.FormatValue(i), docStr) - } - w.Flush() -} - -// printValuesMarkdown (markdown) formats an array of Values on stdout -func printValuesMarkdown(iw io.Writer, libName string, values []*compiler.Value) { - w := tabwriter.NewWriter(iw, 0, 4, len(textPadding), ' ', 0) - fmt.Fprintf(w, "| Name\t| Type\t| Description \t|\n") - fmt.Fprintf(w, "| -------------\t|:-------------:\t|:-------------:\t|\n") - for _, i := range values { - fmt.Fprintf(w, "|*%s*\t| `%s`\t|%s\t|\n", - formatLabel(libName, i), - mdEscape(common.FormatValue(i)), - mdEscape(common.ValueDocOneLine(i))) - } - w.Flush() -} - -// printValuesJson fills a struct for json output -func valuesToJSON(libName string, values []*compiler.Value) []ValueJSON { - val := []ValueJSON{} - - for _, i := range values { - v := ValueJSON{} - v.Name = formatLabel(libName, i) - v.Type = common.FormatValue(i) - v.Description = common.ValueDocOneLine(i) - val = append(val, v) - } - - return val -} - -func PrintDoc(ctx context.Context, w io.Writer, packageName string, val *compiler.Value, format string) { - lg := log.Ctx(ctx) - - fields, err := val.Fields(cue.Definitions(true)) - if err != nil { - lg.Fatal().Err(err).Msg("cannot get fields") - } - - packageJSON := &PackageJSON{} - // Package Name + Description - switch format { - case textFormat: - fmt.Fprintf(w, "Package %s\n", packageName) - fmt.Fprintf(w, "\n%s\n", common.ValueDocFull(val)) - case markdownFormat: - fmt.Fprintf(w, "---\nsidebar_label: %s\n---\n\n", - filepath.Base(packageName), - ) - fmt.Fprintf(w, "# %s\n", mdEscape(packageName)) - comment := common.ValueDocFull(val) - if comment == "-" { - break - } - fmt.Fprintf(w, "\n%s\n", mdEscape(comment)) - case jsonFormat: - packageJSON.Name = packageName - comment := common.ValueDocFull(val) - if comment != "-" { - packageJSON.Description = comment - } - } - - // Package Fields - for _, field := range fields { - fieldJSON := FieldJSON{} - - if !field.Selector.IsDefinition() { - // not a definition, skipping - continue - } - - name := field.Label() - v := field.Value - if v.Cue().IncompleteKind() != cue.StructKind { - // not a struct, skipping - continue - } - - // Field Name + Description - comment := common.ValueDocOneLine(v) - switch format { - case textFormat: - fmt.Fprintf(w, "\n%s\n\n%s%s\n", name, textPadding, comment) - case markdownFormat: - fmt.Fprintf(w, "\n## %s\n\n", name) - if comment != "-" { - fmt.Fprintf(w, "%s\n\n", mdEscape(comment)) - } - case jsonFormat: - fieldJSON.Name = name - comment := common.ValueDocOneLine(val) - if comment != "-" { - fieldJSON.Description = comment - } - } - - // Inputs - inp := environment.ScanInputs(ctx, v) - switch format { - case textFormat: - if len(inp) == 0 { - fmt.Fprintf(w, "\n%sInputs: none\n", textPadding) - break - } - fmt.Fprintf(w, "\n%sInputs:\n", textPadding) - printValuesText(w, name, inp) - case markdownFormat: - fmt.Fprintf(w, "### %s Inputs\n\n", mdEscape(name)) - if len(inp) == 0 { - fmt.Fprintf(w, "_No input._\n") - break - } - printValuesMarkdown(w, name, inp) - case jsonFormat: - fieldJSON.Inputs = valuesToJSON(name, inp) - } - - // Outputs - out := environment.ScanOutputs(ctx, v) - switch format { - case textFormat: - if len(out) == 0 { - fmt.Fprintf(w, "\n%sOutputs: none\n", textPadding) - break - } - fmt.Fprintf(w, "\n%sOutputs:\n", textPadding) - printValuesText(w, name, out) - case markdownFormat: - fmt.Fprintf(w, "\n### %s Outputs\n\n", mdEscape(name)) - if len(out) == 0 { - fmt.Fprintf(w, "_No output._\n") - break - } - printValuesMarkdown(w, name, out) - case jsonFormat: - fieldJSON.Outputs = valuesToJSON(name, out) - packageJSON.Fields = append(packageJSON.Fields, fieldJSON) - } - } - - if format == jsonFormat { - data, err := json.MarshalIndent(packageJSON, "", " ") - if err != nil { - lg.Fatal().Err(err).Msg("json marshal") - } - fmt.Fprintf(w, "%s\n", data) - } -} - // walkStdlib generate whole docs from stdlib walk func walkStdlib(ctx context.Context, output, format string) { lg := log.Ctx(ctx) @@ -327,9 +332,9 @@ func walkStdlib(ctx context.Context, output, format string) { return err } - pkg := fmt.Sprintf("dagger.io/%s", p) - lg.Info().Str("package", pkg).Str("format", format).Msg("generating doc") - val, err := loadCode(pkg) + pkgName := fmt.Sprintf("dagger.io/%s", p) + lg.Info().Str("package", pkgName).Str("format", format).Msg("generating doc") + val, err := loadCode(pkgName) if err != nil { if strings.Contains(err.Error(), "no CUE files") { lg.Warn().Str("package", p).Err(err).Msg("ignoring") @@ -344,7 +349,8 @@ func walkStdlib(ctx context.Context, output, format string) { } defer f.Close() - PrintDoc(ctx, f, pkg, val, format) + pkg := Parse(ctx, pkgName, val) + fmt.Fprintf(f, "%s", pkg.Format(format)) return nil }) From d221802ff42cf6f3a3598aa63b2a126adca988a6 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Wed, 9 Jun 2021 18:42:03 -0700 Subject: [PATCH 2/8] docs generation: write package doc as README.md if there are sub-packages Signed-off-by: Andrea Luzzardi Signed-off-by: slumbering --- cmd/dagger/cmd/doc.go | 48 +++++++++++++------ .../universe/{aws.md => aws/README.md} | 0 .../universe/{dagger.md => dagger/README.md} | 0 .../universe/{gcp.md => gcp/README.md} | 0 .../{kubernetes.md => kubernetes/README.md} | 0 5 files changed, 34 insertions(+), 14 deletions(-) rename docs/reference/universe/{aws.md => aws/README.md} (100%) rename docs/reference/universe/{dagger.md => dagger/README.md} (100%) rename docs/reference/universe/{gcp.md => gcp/README.md} (100%) rename docs/reference/universe/{kubernetes.md => kubernetes/README.md} (100%) diff --git a/cmd/dagger/cmd/doc.go b/cmd/dagger/cmd/doc.go index 447fa771..1d5f96ef 100644 --- a/cmd/dagger/cmd/doc.go +++ b/cmd/dagger/cmd/doc.go @@ -317,6 +317,8 @@ func walkStdlib(ctx context.Context, output, format string) { lg := log.Ctx(ctx) lg.Info().Str("output", output).Msg("generating stdlib") + + packages := map[string]*Package{} err := fs.WalkDir(stdlib.FS, ".", func(p string, d fs.DirEntry, err error) error { if err != nil { return err @@ -325,13 +327,6 @@ func walkStdlib(ctx context.Context, output, format string) { return nil } - filename := fmt.Sprintf("%s.%s", p, format) - filepath := path.Join(output, filename) - - if err := os.MkdirAll(path.Dir(filepath), 0755); err != nil { - return err - } - pkgName := fmt.Sprintf("dagger.io/%s", p) lg.Info().Str("package", pkgName).Str("format", format).Msg("generating doc") val, err := loadCode(pkgName) @@ -343,18 +338,43 @@ func walkStdlib(ctx context.Context, output, format string) { return err } - f, err := os.Create(filepath) - if err != nil { - return err - } - defer f.Close() - pkg := Parse(ctx, pkgName, val) - fmt.Fprintf(f, "%s", pkg.Format(format)) + packages[p] = pkg return nil }) if err != nil { lg.Fatal().Err(err).Msg("cannot generate stdlib doc") } + + hasSubPackages := func(name string) bool { + for p := range packages { + if strings.HasPrefix(p, name+"/") { + return true + } + } + return false + } + + for p, pkg := range packages { + filename := fmt.Sprintf("%s.%s", p, format) + // If this package has sub-packages (e.g. `aws`), create + // `aws/README.md` instead of `aws.md`. + if hasSubPackages(p) { + filename = fmt.Sprintf("%s/README.%s", p, format) + } + filepath := path.Join(output, filename) + + if err := os.MkdirAll(path.Dir(filepath), 0755); err != nil { + lg.Fatal().Err(err).Msg("cannot create directory") + } + + f, err := os.Create(filepath) + if err != nil { + lg.Fatal().Err(err).Msg("cannot create file") + } + defer f.Close() + + fmt.Fprintf(f, "%s", pkg.Format(format)) + } } diff --git a/docs/reference/universe/aws.md b/docs/reference/universe/aws/README.md similarity index 100% rename from docs/reference/universe/aws.md rename to docs/reference/universe/aws/README.md diff --git a/docs/reference/universe/dagger.md b/docs/reference/universe/dagger/README.md similarity index 100% rename from docs/reference/universe/dagger.md rename to docs/reference/universe/dagger/README.md diff --git a/docs/reference/universe/gcp.md b/docs/reference/universe/gcp/README.md similarity index 100% rename from docs/reference/universe/gcp.md rename to docs/reference/universe/gcp/README.md diff --git a/docs/reference/universe/kubernetes.md b/docs/reference/universe/kubernetes/README.md similarity index 100% rename from docs/reference/universe/kubernetes.md rename to docs/reference/universe/kubernetes/README.md From b549d6e91dd21c6c567420d25c99ceb81acad4f5 Mon Sep 17 00:00:00 2001 From: slumbering Date: Fri, 11 Jun 2021 12:24:58 +0200 Subject: [PATCH 3/8] docs: :sparkles: swizzle doc page to implement github auth access Signed-off-by: slumbering --- website/.env | 2 + website/docusaurus.config.js | 9 +- website/package.json | 2 + website/src/api/github.js | 58 ++++++ website/src/components/DocAuthentication.js | 13 ++ .../components/DocAuthentication.module.css | 13 ++ website/src/components/Spinner.js | 8 + website/src/components/Spinner.module.css | 64 ++++++ website/src/theme/DocPage/index.js | 195 ++++++++++++++++++ website/src/theme/DocPage/styles.module.css | 98 +++++++++ .../img/Dagger_Website_Space_Uranus.png | Bin 0 -> 6732 bytes website/yarn.lock | 31 +++ 12 files changed, 489 insertions(+), 4 deletions(-) create mode 100644 website/.env create mode 100644 website/src/api/github.js create mode 100644 website/src/components/DocAuthentication.js create mode 100644 website/src/components/DocAuthentication.module.css create mode 100644 website/src/components/Spinner.js create mode 100644 website/src/components/Spinner.module.css create mode 100644 website/src/theme/DocPage/index.js create mode 100644 website/src/theme/DocPage/styles.module.css create mode 100644 website/static/img/Dagger_Website_Space_Uranus.png diff --git a/website/.env b/website/.env new file mode 100644 index 00000000..6737a79d --- /dev/null +++ b/website/.env @@ -0,0 +1,2 @@ +REACT_APP_CLIENT_ID=cd8f9be2562bfc8d6cfc +REACT_APP_CLIENT_SECRET=4856ebc1101d1228e21b0c9705e8d08105804a3e \ No newline at end of file diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 3ed9b38a..61866f85 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -12,12 +12,10 @@ 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: { - sidebarCollapsible: false, + sidebarCollapsible: true, prism: { theme: require("prism-react-renderer/themes/okaidia"), }, @@ -58,5 +56,8 @@ module.exports = { }, ], ], - plugins: ["docusaurus-plugin-sass"], + plugins: [ + "docusaurus-plugin-sass", + "docusaurus2-dotenv" + ], }; diff --git a/website/package.json b/website/package.json index 795dccc3..575a5e80 100644 --- a/website/package.json +++ b/website/package.json @@ -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" }, diff --git a/website/src/api/github.js b/website/src/api/github.js new file mode 100644 index 00000000..30ff6bfb --- /dev/null +++ b/website/src/api/github.js @@ -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); + } +} \ No newline at end of file diff --git a/website/src/components/DocAuthentication.js b/website/src/components/DocAuthentication.js new file mode 100644 index 00000000..de2199fa --- /dev/null +++ b/website/src/components/DocAuthentication.js @@ -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 ( +
+

Welcome on Dagger documentation

+

Please Sign in to Github in order to get access to the doc

+ window.location.href = `//github.com/login/oauth/authorize?client_id=${process.env.REACT_APP_CLIENT_ID}&scope=user&allow_signup=false`} /> +
+ ) +} \ No newline at end of file diff --git a/website/src/components/DocAuthentication.module.css b/website/src/components/DocAuthentication.module.css new file mode 100644 index 00000000..5fd0ada2 --- /dev/null +++ b/website/src/components/DocAuthentication.module.css @@ -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; +} \ No newline at end of file diff --git a/website/src/components/Spinner.js b/website/src/components/Spinner.js new file mode 100644 index 00000000..1cdb3161 --- /dev/null +++ b/website/src/components/Spinner.js @@ -0,0 +1,8 @@ +import React from 'react'; +import styles from './Spinner.module.css'; + +export default function Spinner() { + return ( +
+ ) +} \ No newline at end of file diff --git a/website/src/components/Spinner.module.css b/website/src/components/Spinner.module.css new file mode 100644 index 00000000..15f5d9de --- /dev/null +++ b/website/src/components/Spinner.module.css @@ -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); + } +} diff --git a/website/src/theme/DocPage/index.js b/website/src/theme/DocPage/index.js new file mode 100644 index 00000000..b5d67846 --- /dev/null +++ b/website/src/theme/DocPage/index.js @@ -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 ( + +
+ {sidebar && ( +
{ + if ( + !e.currentTarget.classList.contains(styles.docSidebarContainer) + ) { + return; + } + + if (hiddenSidebarContainer) { + setHiddenSidebar(true); + } + }} + role="complementary"> + + + {hiddenSidebar && ( +
+ +
+ )} +
+ )} +
+
+ {children} +
+
+
+
+ ); +} + +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 + + if ((isUserAuthorized?.status && isUserAuthorized?.status === 401)) { + return

Redirection vers dagger.io...

+ } + + if (!isUserAuthorized) { + return ( + + ) + } + + // END CUSTOM DOCPAGE + + if (!currentDocRoute) { + return ; + } + + return ( + + {renderRoutes(docRoutes)} + + ); +} + +export default DocPage; diff --git a/website/src/theme/DocPage/styles.module.css b/website/src/theme/DocPage/styles.module.css new file mode 100644 index 00000000..19bfa5fc --- /dev/null +++ b/website/src/theme/DocPage/styles.module.css @@ -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 + ); + } +} diff --git a/website/static/img/Dagger_Website_Space_Uranus.png b/website/static/img/Dagger_Website_Space_Uranus.png new file mode 100644 index 0000000000000000000000000000000000000000..b1b3f4bf2f773aafc5be48b8e3d9143cf3aff981 GIT binary patch literal 6732 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 vz_lWA7~*T)P Date: Fri, 11 Jun 2021 14:50:29 +0200 Subject: [PATCH 4/8] docs: :sparkles: display styled pages when signIn and unauthorized Signed-off-by: slumbering --- ...entication.js => DocPageAuthentication.js} | 2 +- ...e.css => DocPageAuthentication.module.css} | 0 website/src/components/DocPageRedirect.js | 29 ++++++++++++++++++ .../src/components/DocPageRedirect.module.css | 25 +++++++++++++++ website/src/theme/DocPage/index.js | 7 +++-- website/static/img/dagger-astronaute.png | Bin 0 -> 60145 bytes 6 files changed, 59 insertions(+), 4 deletions(-) rename website/src/components/{DocAuthentication.js => DocPageAuthentication.js} (91%) rename website/src/components/{DocAuthentication.module.css => DocPageAuthentication.module.css} (100%) create mode 100644 website/src/components/DocPageRedirect.js create mode 100644 website/src/components/DocPageRedirect.module.css create mode 100644 website/static/img/dagger-astronaute.png diff --git a/website/src/components/DocAuthentication.js b/website/src/components/DocPageAuthentication.js similarity index 91% rename from website/src/components/DocAuthentication.js rename to website/src/components/DocPageAuthentication.js index de2199fa..d1bb5e3f 100644 --- a/website/src/components/DocAuthentication.js +++ b/website/src/components/DocPageAuthentication.js @@ -1,6 +1,6 @@ import React from "react"; import { GithubLoginButton } from 'react-social-login-buttons'; -import style from './DocAuthentication.module.css' +import style from './DocPageAuthentication.module.css' export default function DocAuthentication() { return ( diff --git a/website/src/components/DocAuthentication.module.css b/website/src/components/DocPageAuthentication.module.css similarity index 100% rename from website/src/components/DocAuthentication.module.css rename to website/src/components/DocPageAuthentication.module.css diff --git a/website/src/components/DocPageRedirect.js b/website/src/components/DocPageRedirect.js new file mode 100644 index 00000000..e172ed20 --- /dev/null +++ b/website/src/components/DocPageRedirect.js @@ -0,0 +1,29 @@ +import React, { useEffect, useState } from "react"; +import style from './DocPageRedirect.module.css' + + +export default function DocPageRedirect() { + const [counter, setCounter] = useState(10) + + useEffect(() => { + setTimeout(() => window.location.href = "https://dagger.io", 10000) + setInterval(() => setCounter((prevState) => prevState - 1), 1000) + }, []) + + return ( +
+
+
+

Oups!

+

It seems you don't have the permission to see Dagger's documentation. But don't worry you can request an Eary Access :). You'll be redirect to Dagger website in {counter} seconds

+

See you soon !

+
+ If nothing happen, click here to go to Dagger website +
+
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/website/src/components/DocPageRedirect.module.css b/website/src/components/DocPageRedirect.module.css new file mode 100644 index 00000000..32f717b2 --- /dev/null +++ b/website/src/components/DocPageRedirect.module.css @@ -0,0 +1,25 @@ +.wrapper { + background: linear-gradient(180deg, #131226, #0e2b3d); + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: var(--ifm-color-primary-light); + max-width: 100%; +} + +.wrapper a { + color: var(--ifm-color-primary-light); + text-decoration: underline; +} + +.h1 { + margin-bottom: 2rem; +} + +.row { + justify-content: center; + align-content: center; + align-items: center; +} diff --git a/website/src/theme/DocPage/index.js b/website/src/theme/DocPage/index.js index b5d67846..9e512ad6 100644 --- a/website/src/theme/DocPage/index.js +++ b/website/src/theme/DocPage/index.js @@ -24,7 +24,8 @@ 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'; +import DocPageAuthentication from '../../components/DocPageAuthentication'; +import DocPageRedirect from '../../components/DocPageRedirect'; function DocPageContent({ currentDocRoute, versionMetadata, children }) { const { siteConfig, isClient } = useDocusaurusContext(); @@ -168,12 +169,12 @@ function DocPage(props) { if (isLoading) return if ((isUserAuthorized?.status && isUserAuthorized?.status === 401)) { - return

Redirection vers dagger.io...

+ return } if (!isUserAuthorized) { return ( - + ) } diff --git a/website/static/img/dagger-astronaute.png b/website/static/img/dagger-astronaute.png new file mode 100644 index 0000000000000000000000000000000000000000..07dec833e520dd5861b3f7622f8780704f25d136 GIT binary patch literal 60145 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 @o= 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%0A(}B-h#>^e9 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 Zcj!RNmJalt@XimAAt@#+S}v^T_kRcO&8PqX literal 0 HcmV?d00001 From 1a4f657a2f12319cd566d885ad143d9ead2f35e8 Mon Sep 17 00:00:00 2001 From: slumbering Date: Fri, 11 Jun 2021 14:55:49 +0200 Subject: [PATCH 5/8] docs: remove .env Signed-off-by: slumbering --- website/.env | 2 -- website/.gitignore | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 website/.env diff --git a/website/.env b/website/.env deleted file mode 100644 index 6737a79d..00000000 --- a/website/.env +++ /dev/null @@ -1,2 +0,0 @@ -REACT_APP_CLIENT_ID=cd8f9be2562bfc8d6cfc -REACT_APP_CLIENT_SECRET=4856ebc1101d1228e21b0c9705e8d08105804a3e \ No newline at end of file diff --git a/website/.gitignore b/website/.gitignore index b2d6de30..b083ed4f 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -10,6 +10,7 @@ # Misc .DS_Store +.env .env.local .env.development.local .env.test.local From a12e08bd76f7afa310a125c17b96c4d3f5b0f6d5 Mon Sep 17 00:00:00 2001 From: slumbering Date: Fri, 11 Jun 2021 16:39:08 +0200 Subject: [PATCH 6/8] docs: :bug: try oauth keys without .env Signed-off-by: slumbering --- website/src/api/github.js | 4 ++-- website/src/components/DocPageAuthentication.js | 2 +- website/src/theme/DocPage/index.js | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/website/src/api/github.js b/website/src/api/github.js index 30ff6bfb..435e097b 100644 --- a/website/src/api/github.js +++ b/website/src/api/github.js @@ -10,8 +10,8 @@ async function getAccessToken(code) { 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, + client_id: 'cd8f9be2562bfc8d6cfc', + client_secret: '2509358055095d52dfd7331d072f378e7f16940f', }, validateStatus: function (status) { return status < 500; // Resolve only if the status code is less than 500 diff --git a/website/src/components/DocPageAuthentication.js b/website/src/components/DocPageAuthentication.js index d1bb5e3f..2f8f6667 100644 --- a/website/src/components/DocPageAuthentication.js +++ b/website/src/components/DocPageAuthentication.js @@ -7,7 +7,7 @@ export default function DocAuthentication() {

Welcome on Dagger documentation

Please Sign in to Github in order to get access to the doc

- window.location.href = `//github.com/login/oauth/authorize?client_id=${process.env.REACT_APP_CLIENT_ID}&scope=user&allow_signup=false`} /> + window.location.href = `//github.com/login/oauth/authorize?client_id=cd8f9be2562bfc8d6cfc&scope=user&allow_signup=false`} />
) } \ No newline at end of file diff --git a/website/src/theme/DocPage/index.js b/website/src/theme/DocPage/index.js index 9e512ad6..447b81cf 100644 --- a/website/src/theme/DocPage/index.js +++ b/website/src/theme/DocPage/index.js @@ -178,6 +178,7 @@ function DocPage(props) { ) } + // END CUSTOM DOCPAGE if (!currentDocRoute) { From da08aa54e98b1d655b0540039d4c0b4d1fafe7a5 Mon Sep 17 00:00:00 2001 From: slumbering Date: Fri, 11 Jun 2021 17:03:04 +0200 Subject: [PATCH 7/8] disable github auth when localhost environment Signed-off-by: slumbering --- website/src/theme/DocPage/index.js | 68 +++++++++++++++--------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/website/src/theme/DocPage/index.js b/website/src/theme/DocPage/index.js index 447b81cf..ce73867b 100644 --- a/website/src/theme/DocPage/index.js +++ b/website/src/theme/DocPage/index.js @@ -139,46 +139,46 @@ function DocPage(props) { ); // 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') - })()) + if (typeof window !== "undefined" && window?.location?.hostname !== "localhost") { + 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 }) + 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 + + if ((isUserAuthorized?.status && isUserAuthorized?.status === 401)) { + return } - setIsLoading(false) - }, [userAccessToken]) - - if (isLoading) return - - if ((isUserAuthorized?.status && isUserAuthorized?.status === 401)) { - return + if (!isUserAuthorized) { + return ( + + ) + } } - - if (!isUserAuthorized) { - return ( - - ) - } - - // END CUSTOM DOCPAGE if (!currentDocRoute) { From 7001f1ccfc957322009a43f101b55ab0d8890775 Mon Sep 17 00:00:00 2001 From: slumbering Date: Mon, 14 Jun 2021 14:13:31 +0200 Subject: [PATCH 8/8] docs: :bug: fix user auth issue + refacto api calls Signed-off-by: slumbering --- website/src/api/github.js | 72 +++++++++++-------- .../src/components/DocPageAuthentication.js | 2 +- website/src/theme/DocPage/index.js | 12 ++-- 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/website/src/api/github.js b/website/src/api/github.js index 435e097b..ce2b66df 100644 --- a/website/src/api/github.js +++ b/website/src/api/github.js @@ -4,55 +4,65 @@ const AxiosInstance = axios.create({ headers: { 'Accept': 'application/vnd.github.v3+json' }, }); -async function getAccessToken(code) { - +function bindApiCall({ url, config, errorMessage }) { try { - const getAccessToken = await AxiosInstance.get('https://github.com/login/oauth/access_token', { + const apiCall = AxiosInstance.get(url, { + ...config, + validateStatus: function (status) { + return status < 500; // Resolve only if the status code is less than 500 + } + }) + + return apiCall + } catch (error) { + console.log(errorMessage, error.message) + } +} + +async function getAccessToken(code) { + const accessToken = await bindApiCall({ + url: 'https://github.com/login/oauth/access_token', + config: { params: { code, client_id: 'cd8f9be2562bfc8d6cfc', client_secret: '2509358055095d52dfd7331d072f378e7f16940f', }, - validateStatus: function (status) { - return status < 500; // Resolve only if the status code is less than 500 - } - }) + errorMessage: 'error getAccessToken' + } + }) - return getAccessToken.data; - } catch (error) { - console.log("error getAccessToken", error.message) - } + return accessToken.data } export async function getUser(access_token) { - try { - const getUserLogin = await AxiosInstance.get("https://api.github.com/user", { + const user = await bindApiCall({ + url: 'https://api.github.com/user', + config: { headers: { Authorization: `token ${access_token}` }, - validateStatus: function (status) { - return status < 500; // Resolve only if the status code is less than 500 - } - }) + }, + errorMessage: 'error getUser' + }) - return { - login: getUserLogin.data.login, - status: getUserLogin.status - } - } catch (error) { - console.log("error getUser", error.message) + return { + login: user.data?.login, + error: user.data?.error_description, + status: user.status } + } 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); + const isUserCollaborator = await bindApiCall({ + url: `https://docs-access.dagger.io/u/${login}`, + errorMessage: 'error checkUserCollaboratorStatus' + }) + + return { + isAllowed: isUserCollaborator.data, + access_token } } \ No newline at end of file diff --git a/website/src/components/DocPageAuthentication.js b/website/src/components/DocPageAuthentication.js index 2f8f6667..8264aaeb 100644 --- a/website/src/components/DocPageAuthentication.js +++ b/website/src/components/DocPageAuthentication.js @@ -6,7 +6,7 @@ export default function DocAuthentication() { return (

Welcome on Dagger documentation

-

Please Sign in to Github in order to get access to the doc

+

Please Sign In to Github to get access to the doc

window.location.href = `//github.com/login/oauth/authorize?client_id=cd8f9be2562bfc8d6cfc&scope=user&allow_signup=false`} />
) diff --git a/website/src/theme/DocPage/index.js b/website/src/theme/DocPage/index.js index ce73867b..7f9d8c50 100644 --- a/website/src/theme/DocPage/index.js +++ b/website/src/theme/DocPage/index.js @@ -153,14 +153,14 @@ function DocPage(props) { const user = await getUser(userAccessToken) setIsUserAuthorized(user) } else { - if (!isEmpty(authQuery)) { //callback after successful auth with github) + if (!isEmpty(authQuery)) { //callback after successful auth with github const isUserCollaborator = await checkUserCollaboratorStatus(authQuery.code); - if (isUserCollaborator?.status === 200) { + if (isUserCollaborator?.isAllowed) { setUserAccessToken(isUserCollaborator.access_token) if (typeof window !== "undefined") window.localStorage.setItem('user-github-key', isUserCollaborator.access_token); - } else { - setIsUserAuthorized({ status: 401 }) } + + setIsUserAuthorized(isUserCollaborator?.isAllowed) } } setIsLoading(false) @@ -169,11 +169,11 @@ function DocPage(props) { if (isLoading) return - if ((isUserAuthorized?.status && isUserAuthorized?.status === 401)) { + if (isUserAuthorized === false) { return } - if (!isUserAuthorized) { + if (typeof isUserAuthorized == 'undefined' || isUserAuthorized?.status === 401) { return ( )