-
Notifications
You must be signed in to change notification settings - Fork 318
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4801 from jeradrutnam/master
Add Quick Start Template and made some style improvements + Add script to check broken links
- Loading branch information
Showing
14 changed files
with
3,134 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
--- | ||
template: templates/quick-start.html | ||
heading: React Quickstart | ||
description: Welcome to the React Quickstart guide! In this document, you will learn to build a React application, add user login and display user profile information using Asgardeo. | ||
what_you_will_learn: | ||
- Create new React app using Vite | ||
- Install <a href="https://github.com/asgardeo/asgardeo-auth-react-sdk" target="_blank">@asgardeo/auth-react</a> package | ||
- Add user login and logout | ||
- Display user profile information | ||
prerequisites: | ||
- About 15 minutes | ||
- <a href="{{ config.extra.base_path }}/get-started/create-asgardeo-account/">Asgardeo account</a> | ||
- Install a JS package manager | ||
- A favorite text editor or IDE | ||
source_code: <a href="https://github.com/asgardeo/asgardeo-auth-react-sdk/tree/main/samples/asgardeo-react-app" target="_blank" class="github-icon">React Vite App Sample</a> | ||
whats_next: | ||
- Try out Asgardeo complete React guide | ||
- Try out Asgardeo user onboarding complete guide for React | ||
- Read Asgardeo security best practices for React app guide | ||
--- | ||
|
||
## Configure an application Asgardeo | ||
|
||
- Sign into Asgardeo console and navigate to Applications > New Application. | ||
|
||
- Select Single Page Application and Complete the wizard popup by providing a suitable name and an authorized redirect URL | ||
- Name - Asgardeo-React | ||
- Authorized redirect URL - `https://localhost:5173` | ||
|
||
!!! abstract | ||
|
||
The authorized redirect URL determines where Asgardeo should send users after they successfully log in. Typically, this will be the web address where your application is hosted. For this guide, we'll use `https://localhost:5173`, as the sample application will be accessible at this URL. | ||
|
||
!!! note | ||
|
||
Note down the following values : you will need them during the **Step 4** | ||
|
||
- `client-id` | ||
- `base-url` | ||
- `redirect-url` | ||
|
||
## Create a React application using Vite | ||
|
||
Create (a.k.a scaffold) your new React application using Vite. | ||
|
||
=== "npm" | ||
|
||
``` bash | ||
npm create vite@latest asgardeo-react -- --template react | ||
|
||
cd asgardeo-react | ||
|
||
npm install | ||
|
||
npm run dev | ||
``` | ||
|
||
=== "yarn" | ||
|
||
``` bash | ||
yarn create vite@latest asgardeo-react -- --template react | ||
|
||
cd asgardeo-react | ||
|
||
yran install | ||
|
||
yarn dev | ||
``` | ||
|
||
=== "pnpm" | ||
|
||
``` bash | ||
pnpm create vite@latest asgardeo-react -- --template react | ||
|
||
cd asgardeo-react | ||
|
||
pnpm install | ||
|
||
pnpm run dev | ||
``` | ||
|
||
## Install @asgardeo/auth-react | ||
|
||
Asgardeo React SDK provides all the components and hooks you need to integrate Asgardeo into your app. To get started, simply add the Asgardeo React SDK to the project. | ||
|
||
=== "npm" | ||
|
||
``` bash | ||
npm install @asgardeo/auth-react | ||
``` | ||
|
||
=== "yarn" | ||
|
||
``` bash | ||
yarn add @asgardeo/auth-react | ||
``` | ||
|
||
=== "pnpm" | ||
|
||
``` bash | ||
pnpm add @asgardeo/auth-react | ||
``` | ||
|
||
## Add <AuthProvider /> to your app | ||
|
||
The `<AuthProvider />` serves as a context provider for user login in the app. You can add the AuthProvider to your app by wrapping the root component. | ||
|
||
Add the following changes to the `main.jsx` file. | ||
|
||
!!! note | ||
|
||
Replace below placeholders with your registered organization name in Asgardeo and the generated `client-id` from the app you registered in Asgardeo. | ||
|
||
- `<your-app-client-id>` | ||
- `https://api.asgardeo.io/t/<your-organization-name>` | ||
|
||
```javascript title="src/main.jsx" hl_lines="4 7-13 17 19" linenums="1" | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom/client'; | ||
import App from './App.jsx'; | ||
import { AuthProvider } from '@asgardeo/auth-react'; | ||
import './index.css'; | ||
|
||
const config = { | ||
signInRedirectURL: "http://localhost:5173", | ||
signOutRedirectURL: "http://localhost:5173", | ||
clientID: "<your-app-client-id>", | ||
baseUrl: "https://api.asgardeo.io/t/<your-organization-name>", | ||
scope: [ "openid","profile" ] | ||
} | ||
|
||
ReactDOM.createRoot(document.getElementById('root')).render( | ||
<React.StrictMode> | ||
<AuthProvider config={ config }> | ||
<App /> | ||
</AuthProvider> | ||
</React.StrictMode>, | ||
); | ||
|
||
``` | ||
|
||
## Add login and logout link to your app | ||
|
||
Asgardeo provides `useAuthContext` hook to conveniently access user authentication data and sign-in and sign-out methods. | ||
|
||
Replace the existing content of the `App.jsx` file with following content. | ||
|
||
```javascript title="src/App.jsx" hl_lines="1 5 9-13" linenums="1" | ||
import { useAuthContext } from "@asgardeo/auth-react"; | ||
import './App.css'; | ||
|
||
const App = () => { | ||
const { state, signIn, signOut } = useAuthContext(); | ||
|
||
return ( | ||
<> | ||
{ | ||
state.isAuthenticated | ||
? <button onClick={() => signOut()}>Logout</button> | ||
: <button onClick={() => signIn()}>Login</button> | ||
} | ||
</> | ||
) | ||
}; | ||
|
||
export default App; | ||
``` | ||
|
||
Visit your app's homepage at [http://localhost:5173](http://localhost:5173). | ||
|
||
!!! tip | ||
|
||
You need to create a test user in Asgardeo by following this guide to tryout login and logout features. | ||
|
||
## Display logged in user details | ||
|
||
Modified the code as below to see logged in user details. | ||
|
||
```javascript title="src/App.jsx" hl_lines="8-15" linenums="1" | ||
... | ||
|
||
const App = () => { | ||
... | ||
|
||
return ( | ||
<> | ||
{ | ||
state.isAuthenticated ? | ||
<> | ||
<p>Welocme {state.username}</p> | ||
<button onClick={() => signOut()}>Logout</button> | ||
</> | ||
: <button onClick={() => signIn()}>Login</button> | ||
} | ||
</> | ||
) | ||
}; | ||
|
||
... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"liveUrl": "http://localhost:8000/asgardeo/docs", | ||
"ignorePatterns": [ | ||
"https://wso2.com/.*", | ||
"mailto:.*", | ||
"https://example.com/.*", | ||
"https://api.asgardeo.io/t/.*" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
/** | ||
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). | ||
* | ||
* WSO2 LLC. licenses this file to you under the Apache License, | ||
* Version 2.0 (the "License"); you may not use this file except | ||
* in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
const markdownLinkCheck = require('markdown-link-check'); | ||
const fs = require('fs'); | ||
const puppeteer = require('puppeteer'); | ||
const config = require('./check-broken-links-config.json'); | ||
|
||
const logFile = 'broken-links-log.txt'; | ||
const ignoredLinkPatterns = config.ignorePatterns.map(pattern => new RegExp(pattern)); | ||
const startUrl = config.liveUrl; // Live URL from config | ||
const visitedUrls = new Set(); // Set to track visited URLs | ||
const baseDomain = new URL(startUrl).hostname; // Get the base domain for comparison | ||
|
||
let brokenLinksCount = 0; // Counter for broken links | ||
let localLinksCount = 0; // Counter for local links | ||
let externalLinksCount = 0; // Counter for external links | ||
let isShuttingDown = false; // Flag to indicate shutdown | ||
let spinner; // Spinner for loader | ||
|
||
// Prepare the broken-links-log.txt file (wipes content on new run) | ||
fs.writeFileSync(logFile, 'Broken Links Report\n====================\n\n'); | ||
|
||
// Function to check if a link should be ignored based on the patterns | ||
function shouldIgnore(link) { | ||
return ignoredLinkPatterns.some(pattern => pattern.test(link)); | ||
} | ||
|
||
// Function to log broken links | ||
function logBrokenLink(link, sourceUrl) { | ||
brokenLinksCount++; | ||
const logEntry = `Broken Link Found on: ${sourceUrl}\nLink: ${link}\n\n`; | ||
fs.appendFileSync(logFile, logEntry); | ||
} | ||
|
||
// Function to check links on a page and crawl nested links | ||
async function checkLinksOnPage(url, depth = 2) { | ||
if (isShuttingDown || depth < 0 || visitedUrls.has(url)) return; // Stop if shutting down, depth limit reached, or already visited | ||
|
||
visitedUrls.add(url); // Mark the URL as visited | ||
|
||
return new Promise((resolve) => { | ||
// Use Puppeteer to get links from the page | ||
(async () => { | ||
const browser = await puppeteer.launch(); | ||
const page = await browser.newPage(); | ||
|
||
try { | ||
spinner.text = `Visiting: ${url}`; | ||
await page.goto(url, { waitUntil: 'networkidle2' }); | ||
const linksWithInfo = await page.evaluate(() => { | ||
const anchorTags = Array.from(document.querySelectorAll('a')); | ||
return anchorTags.map(tag => tag.href).filter(link => link.startsWith('http')); | ||
}); | ||
|
||
// Check each link | ||
for (const link of linksWithInfo) { | ||
if (shouldIgnore(link)) { | ||
continue; // Skip ignored links | ||
} | ||
|
||
// Check if the link is in the same domain | ||
const linkDomain = new URL(link).hostname; | ||
|
||
// Increment local or external link count | ||
if (linkDomain === baseDomain) { | ||
localLinksCount++; | ||
} else { | ||
externalLinksCount++; | ||
} | ||
|
||
// Check the link status | ||
markdownLinkCheck(link, { retry: true }, (err, results) => { | ||
if (err) { | ||
console.error(`Error checking ${link}:`, err); | ||
return; | ||
} | ||
|
||
results.forEach(result => { | ||
const { dead, statusCode } = result; | ||
|
||
if (dead || statusCode === 404) { | ||
logBrokenLink(link, url); | ||
console.log(`\n[Broken Link] Found: ${link}\n`); | ||
} | ||
}); | ||
}); | ||
|
||
// Recursively check nested links if they are in the same domain | ||
if (linkDomain === baseDomain) { | ||
await checkLinksOnPage(link, depth - 1); | ||
} | ||
} | ||
} catch (error) { | ||
console.error(`Error visiting ${url}:`, error); | ||
} finally { | ||
await browser.close(); // Ensure the browser is closed | ||
|
||
resolve(); | ||
} | ||
})(); | ||
}); | ||
} | ||
|
||
// Function to handle termination | ||
const handleExit = () => { | ||
isShuttingDown = true; // Set shutdown flag | ||
spinner.stop(); // Stop the spinner | ||
|
||
console.log(`Total Broken Links Found: ${brokenLinksCount}`); | ||
console.log(`Total Local Links Scanned: ${localLinksCount}`); | ||
console.log(`Total External Links Scanned: ${externalLinksCount}`); | ||
|
||
fs.appendFileSync(logFile, `\nTotal Broken Links: ${brokenLinksCount}\n`); | ||
fs.appendFileSync(logFile, `Total Local Links Scanned: ${localLinksCount}\n`); | ||
fs.appendFileSync(logFile, `Total External Links Scanned: ${externalLinksCount}\n`); | ||
|
||
process.exit(); // Exit the process | ||
}; | ||
|
||
// Main function to start checking links | ||
(async () => { | ||
// Handle SIGINT (Ctrl + C) | ||
process.on('SIGINT', handleExit); | ||
|
||
// Dynamic import of ora | ||
const ora = (await import('ora')).default; | ||
spinner = ora('Checking links...').start(); // Start the spinner | ||
|
||
try { | ||
await checkLinksOnPage(startUrl); // Start checking links | ||
} catch (error) { | ||
console.error('Error during link checking:', error); | ||
} | ||
|
||
spinner.succeed('Link checking complete!'); // Stop the spinner | ||
|
||
console.log(`Total Broken Links: ${brokenLinksCount}`); | ||
console.log(`Total Local Links Scanned: ${localLinksCount}`); | ||
console.log(`Total External Links Scanned: ${externalLinksCount}`); | ||
|
||
fs.appendFileSync(logFile, `\nTotal Broken Links: ${brokenLinksCount}\n`); | ||
fs.appendFileSync(logFile, `Total Local Links Scanned: ${localLinksCount}\n`); | ||
fs.appendFileSync(logFile, `Total External Links Scanned: ${externalLinksCount}\n`); | ||
})(); |
Oops, something went wrong.