diff --git a/apps/client/src/App.js b/apps/client/src/App.js
index 33ab974..fa4e597 100644
--- a/apps/client/src/App.js
+++ b/apps/client/src/App.js
@@ -10,6 +10,7 @@ import {
LayoutMain,
LayoutFooter,
Markdown,
+ Catch,
} from 'components'
import { AuthInitializer } from 'containers/Auth'
import { ApolloInitializer } from 'containers/Apollo'
@@ -20,6 +21,7 @@ import { AppNavbar } from 'containers/AppNavbar'
import { AppFooter } from 'containers/AppFooter'
import { Home } from 'pages/Home'
import { Owner } from 'pages/Owner'
+import { ErrorPage } from 'pages/ErrorPage'
import { Repository } from 'pages/Repository'
import { AuthCallback } from 'pages/AuthCallback'
import { NotFound } from 'pages/NotFound'
@@ -54,46 +56,50 @@ export function App() {
-
-
-
- (
- {Privacy}
- )}
- />
- (
- {Terms}
- )}
- />
- (
- {Security}
- )}
- />
-
-
-
-
-
+ }>
+
+
+
+ (
+ {Privacy}
+ )}
+ />
+ (
+ {Terms}
+ )}
+ />
+ (
+
+ {Security}
+
+ )}
+ />
+
+
+
+
+
+
diff --git a/apps/client/src/components/Catch.js b/apps/client/src/components/Catch.js
new file mode 100644
index 0000000..96a71d8
--- /dev/null
+++ b/apps/client/src/components/Catch.js
@@ -0,0 +1,29 @@
+import React from 'react'
+import * as Sentry from '@sentry/browser'
+
+export class Catch extends React.Component {
+ constructor(props) {
+ super(props)
+ this.state = { error: null }
+ }
+
+ static getDerivedStateFromError(error) {
+ return { error }
+ }
+
+ componentDidCatch(error) {
+ if (this.props.capture) {
+ Sentry.captureException(error)
+ }
+ }
+
+ render() {
+ if (this.state.error) return this.props.fallback
+ return this.props.children
+ }
+}
+
+Catch.defaultProps = {
+ capture: true,
+ fallback: null,
+}
diff --git a/apps/client/src/components/index.js b/apps/client/src/components/index.js
index 1abc674..342c0af 100644
--- a/apps/client/src/components/index.js
+++ b/apps/client/src/components/index.js
@@ -1,6 +1,7 @@
export * from './AlertBar'
export * from './Avatar'
export * from './BrandLogo'
+export * from './Catch'
export * from './Card'
export * from './Code'
export * from './Container'
diff --git a/apps/client/src/containers/StatsLoader.js b/apps/client/src/containers/StatsLoader.js
index e1a5a17..e6ee3b3 100644
--- a/apps/client/src/containers/StatsLoader.js
+++ b/apps/client/src/containers/StatsLoader.js
@@ -2,17 +2,25 @@ import React from 'react'
import axios from 'axios'
export function StatsLoader({ url, fallback = null, children }) {
- const [loading, setLoading] = React.useState(true)
- const [stats, setStats] = React.useState(null)
+ const [state, setState] = React.useState({
+ error: null,
+ result: null,
+ loading: true,
+ })
React.useEffect(() => {
- setLoading(true)
- axios.get(url).then(response => {
- setStats(response.data)
- setLoading(false)
- })
+ setState({ error: null, result: null, loading: true })
+ axios
+ .get(url)
+ .then(response => {
+ setState({ error: null, result: response.data, loading: false })
+ })
+ .catch(error => {
+ setState({ error, result: null, loading: false })
+ })
}, [url])
- if (loading) return fallback
- if (!stats) return null
- return children(stats)
+ if (state.error) throw state.error
+ if (state.loading) return fallback
+ if (!state.result) return null
+ return children(state.result)
}
diff --git a/apps/client/src/pages/ErrorPage.js b/apps/client/src/pages/ErrorPage.js
new file mode 100644
index 0000000..4291b41
--- /dev/null
+++ b/apps/client/src/pages/ErrorPage.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import { Helmet } from 'react-helmet'
+import { Link } from 'react-router-dom'
+import { Container, FadeLink } from 'components'
+
+export function ErrorPage() {
+ return (
+
+
+ Error
+
+ Sorry an error occurs.
+
+
+ Back to home
+
+
+
+ )
+}
diff --git a/apps/client/src/pages/Repository/BuildDetail.js b/apps/client/src/pages/Repository/BuildDetail.js
index c11fce0..1d911d7 100644
--- a/apps/client/src/pages/Repository/BuildDetail.js
+++ b/apps/client/src/pages/Repository/BuildDetail.js
@@ -13,6 +13,7 @@ import {
CardBody,
CardTitle,
FadeLink,
+ Catch,
} from 'components'
import moment from 'moment'
import loadable from '@loadable/component'
@@ -413,9 +414,14 @@ export function Build({ build }) {
Modules
-
- {stats => }
-
+
+
+ {stats => }
+
+
diff --git a/apps/server/src/models/Bundle.js b/apps/server/src/models/Bundle.js
index f04a42b..43e43e6 100644
--- a/apps/server/src/models/Bundle.js
+++ b/apps/server/src/models/Bundle.js
@@ -29,6 +29,7 @@ export class Bundle extends BaseModel {
return s3.getSignedUrl('getObject', {
Bucket: config.get('s3.bucket'),
Key: Bundle.getWebpackStatsPath(bundleId),
+ Expires: 7200,
})
}