Getting "Key not Found:" while accessing static website on s3 - reactjs

I have a working website which is developed in react and is hosted as a static site on AWS-S3. Our home page route is /home however when we try to access it, we get the following error:
Message: The specified key does not exist.
Key: home/home
RequestId: 0CJ72YSRVM1VR7DT
HostId: EnusXIGM/BWUeBk2Y+jjW2XFYtKjpFB66wf2n9om5L/+yX52JQUviK5ZLPJW6U0moywqGuIBc5M=
so, in the root folder, we have put a index.html in the root folder with the following content.
import React from "react";
import { useNavigate } from "react-router-dom";
const Coverpage: React.FC = () => {
const navigate = useNavigate();
return (
<>
<div>I am cover page!</div>
<button onClick={() => navigate("/home")}>Click me to navigate to homepage!</button>
</>
);
};
export default Coverpage;
and the navigation works.
I really need help to find a way where when the user access the site http://xxxx, the site directly navigate to http://xxxx/home and the site opens as desired.
Appreciate your help. not sure if we can do it at reactjs level or AWS s3

The solution is to configure AWS to rewrite most URLs to your React's index.html.
Create a CloudFront distribution which distributes the S3 bucket.
Ensure the distribution's Default Root Object is set to index.html.
Create a CloudFront function with the code below. This rewrites all requests to index.html except for requests to /static/*.
function handler(event) {
var request = event.request;
var parts = request.uri.split('/');
if(parts[1] != 'static'){
request.uri = '/index.html';
console.log('Rewriting: ' + request.uri);
} else {
console.log('Not Rewriting: ' + request.uri);
}
return request;
}
Go to the CF distribution then open the default behaviour (or create it if needed) and at the bottom, associate the function as a View Request type
Now you can visit the CF Distribution's URL (or your domain if configured) with any path and the React app will show.
The other option is to simply set the S3 bucket's Error Document to index.html, but this is very hacky and not recommended. Page visits will show the React app but will have undesirable 404 error codes.

Related

Next JS dynamic routing not working on export production static build

I'm building an app with Next. using nested dynamic routing. In development mode, everything works as expected. But when I deploy the app in production ,When you hit reload (like F5) it gives you 404
use next version : 9.5.2
my directory structure is like this:
pages
-users
-[page]
- [id].tsx
- index.tsx
-index.tsx
Here's how I link to the dynamic page:
<Link href='/users/[page]/[id]' as='/users/edit/${id}'>
<a>Edit</a> </Link>
Here sample code user/[page]/[id].tsx
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
},
};
static getInitialProps({query}) {
return {query}
}
async componentDidMount(){
const { query } = this.props;
if(query?.id){
await this.props.getUserById(query?.id);
}
}
}
Here static build command
`npm run export`
Here static build
my build is a set of HTML, js, css assets I hosted on the web server (Apache, Nginx).
If I clicked in link users from index.tsx page, my URL look '/users/edit/1/' and work fine, but if I refresh the page I get error 404. In the development mode refresh page, everything works fine.
I am stuck on this problem for many days. Do u know how I can fix this?

Cannot use gatsby-plugin-intl for single page site

I'm using Gatsby and I want build a single page site, so without create pages. For achieve this I edited gatsby-node.js with the following code:
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
if (page.path === "/") {
page.matchPath = "/*"
createPage(page)
}
}
in that case, each request is re-routed to the index.js page, which is the only one.
Then, in the index.js page I have:
const IndexPage = () => {
const intl = useIntl()
const locale = intl.locale
return (
<BGTState>
<BlogState>
<Layout>
<Router>
<Home path={`${locale}/`} />
<Section path={`${locale}/:sectionSlug`} />
<Collection path={`${locale}/:sectionSlug/:collectionSlug`} />
<Season
path={`${locale}/:categorySlug/:collectionSlug/:seasonSlug`}
/>
<Product
path={`${locale}/:categorySlug/:collectionSlug/:seasonSlug/:prodSlug`}
/>
<Blog path={`${locale}/blog`} />
<Article path={`${locale}/blog/:articleSlug`} />
<NotFound default />
</Router>
</Layout>
</BlogState>
</BGTState>
)
}
as you can see, I have different routers that load a specific component based on the url.
I have prefixed each path with the current locale to match the correct path.
This mechanism is working fine for the home page only, but for the other links doesn't work. Infact, if I visit something like:
http://localhost:3001/en/category-home/prod-foo
which must load the Collection component, the site simply redirect to:
http://localhost:3001/en
and display the Home component again.
What I did wrong?
UPDATE
Page Structure:
As you can see I have just the index.js which handle all requests as I configured in the gatby-node.js.
If I remove the localization plugin, at least using this configuration:
{
resolve: `gatsby-plugin-intl`,
options: {
// Directory with the strings JSON
path: `${__dirname}/src/languages`,
// Supported languages
languages: ["it", "en", "ci", "fr"],
// Default site language
defaultLanguage: `it`,
// Redirects to `it` in the route `/`
//redirect: true,
// Redirect SEO component
redirectComponent: require.resolve(
`${__dirname}/src/components/redirect.js`
),
},
},
and I don't prefix the url with intl.locale, everything is working fine. But adding redirect: true in the plugin configuration, and prefixing the link with the locale, the site redirect me to the home component.
If you are creating a SPA (Single Page Application, notice the single) you won't have any created pages but index. You are trying yo access to a /category page that's not created because of:
if (page.path === "/") {
page.matchPath = "/*"
createPage(page)
}
That's why your routes don't work (or in other words, only the home page works).
Adapt the previous condition to your needs to allow creating more pages based on your requirements.
I'm using Gatsby and I want build a single page site, so without
create pages. For achieve this I edited gatsby-node.js with the
following code:
It's a non-sense trying to build a SPA application with Gatsby (without creating pages) but then complaining because there's not collection page created.
Make sure that you understand what you are doing, it seems clearly that you need to create dynamically pages for each collection, season, and product so your approach to create SPA won't work for your use-case.
It's possible to keep just index.js without overcomplicating thing? I
just want to understand why my code isn't working 'cause I've passed
the correct url... Removing the localization Gatsby works, so I
suspect there is a localization problem
The only way that http://localhost:3001/category-home/prod-foo (removing the localization) could be resolved is by creating a folder structure such /pages/category-home/prod-foo.js (since Gatsby extrapolates the folder structure as URLs), so, if you want to use localization using your approach, add a structure such en/pages/category-home/prod-foo.js and es/pages/category-home/prod-foo.js (or the whatever locale), and so on. In my opinion, this is overcomplexitying stuff since, for every category, you'll need to create 2 (even more depending on the locales) files.
Gatsby allows you to create dynamic pages and interpolate the locale automatically using built-in plugins on the process, creating each file for the specifically defined locales.

How to force Gatsby to redirect a specific URL path to an external site?

I have a Gatsby site and due to some specific requirements, I need to redirect anyone who attempts to hit a specific URL path, for which there is no page, to an external site. This URL path is not a page within the site, but it's something that a user may be inclined to type due to documentation that is out of my control.
Here's an example: Let's say the site is located at https://www.example.com. A user may visit https://www.example.com/puppies, which does not exist. My file structure does not contain a src/pages/puppies.js file. However, when that URL is entered, I need to redirect the user to another site altogether, such as https://www.stackoverflow.com.
I haven't used Gatsby to that extent to know it has a configuration for this, so someone else may correct me. The way I would handle this is through the hosting provider where your app is.
For example, if you are using Netlify:
Create a _redirects file with the following content:
/* /index.html 200
Or
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
This will cause all https://yourwebsite.com/IDontHaveThisRoute to fallback to /index.html where your .js is loaded.
I provided the Netlify example only to give you the basic idea of how it can be done through the hosting provider of your choice. I would look into configurations I can put into redirects where my domain is deployed.
Thanks to Paul Scanlon he mentioned using onRouteUpdate in Gatsby and it works like a charm
import { navigate } from 'gatsby';
export const onRouteUpdate = ({ location }) => {
if (location.pathname === '/dashboard') {
navigate('/dashboard/reports');
}
};
This question helped point me in the right direction. I was able to get it to work using Gatsby's componentDidMount() to force a redirect as shown below, using a new file called puppies.js to "catch" the path typed by the user:
// puppies.js
import React, { Component } from 'react'
class Puppies extends Component {
componentDidMount() {
window.location.replace("https://www.stackoverflow.com");
}
render() {
return <div />
}
}
export default Puppies

OneDrive file browser authentication issues "GraphError: Unable to retrieve user's mysite URL."

We are working on a request for embedding OneDrive file browser within a web application.
The code we are using comes directly from the examples provided on github.
Here is our client side code:
import React from "react"
import logo from "./logo.svg"
import "./App.css"
import { GraphFileBrowser } from "#microsoft/file-browser"
class App extends React.Component {
render() {
return (
<GraphFileBrowser getAuthenticationToken={this.getAuthenticationToken} />
)
}
getAuthenticationToken() {
return new Promise(resolve =>
resolve(
"eyJ0eXAiOiJKV1QiLCJub25jZSI6ImtkT..."
)
)
}
}
export default App
This is 100% based on examples provided by MS.
As we don't want the users to have to login using oAuth, we are getting a token server side using MicrosoftGraph following this official guide: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow.
We have of course also setup the app in Azure, based on all the requirements defined above.
So once we insert the token in our react component, we just get an error: GraphError: Unable to retrieve user's mysite URL.
When we test the same client side app however with a token generated via the OAUTH user token, the file browser will

Koa.js serve React, api url also render with React page in Chrome

I'm using koa-static to serve static file with root url localhost:3000/, and koa-router to serve RESTful api, like localhost:3000/api/account/login
Make a fakeBuild folder contain only one file index.html. All work fine. root url and api url show correct content.
// serve static file
const serve = require('koa-static')
const staticPath = path.join(__dirname,'..','Client','poker','fakeBuild')
log(staticPath)
app.use(serve(staticPath))
// router
const router = require('./router/Index.js')(app)
Then I try to connect Koa with React
I use yarn build to build React static files, React project is created by create-react-app, not modified.
Then I change the koa-static's target to build folder, just one line changed
const staticPath = path.join(__dirname,'..','Client','poker','build')
Then access localhost:3000/, chrome show React start page, works
Then access localhost:3000/api/account/login, chrome show React start page, what??
Use postman check localhost:3000/api/account/login, it response what I put in router, fine.
So I can use apis, just ignore chrome's result?
It must be React and Chrome doing something extra. I try to remove service-worker.js in React build folder, api url render correctly in chrome.
So anyone can give some documents or explain to help me understand what react done, to keep all the url render it.
And why my api localhost:3000/api/account/login not require service-worker.js (just return some text in response body), also got the js worked. Just because koa-static served the file?
====
Update: my router code, And project available on here
router/Index.js
const Router = require('koa-router');
const R = (app)=>{
const router = new Router();
const api = require('./Api.js');
router.use('/api', api.routes(), api.allowedMethods())
app.use(router.routes())
.use(router.allowedMethods());
};
module.exports = R;
router/Api.js
const Router = require('koa-router')
const log = require('../helper/Utils').log;
module.exports = (function () {
const Api = new Router();
Api.get('/account/login', async (ctx, next)=>{
log("hello");
ctx.status = 200;
ctx.body = `login`
});
Api.get('/account/register', async (ctx, next)=>{
ctx.status = 200;
ctx.body = `register`
});
return Api;
})();

Resources