diff --git a/docker-compose.yml b/docker-compose.yml
index 08095730..1ea3c06b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -76,6 +76,7 @@ services:
- ./docker/nginx/conf.d:/etc/nginx/conf.d
- ./docker/nginx/conf.d.templates:/etc/nginx/templates
- ./docker/nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
+ - ./docker/data/website/public/errors:/etc/nginx/errors
networks:
shared:
ipv4_address: 10.10.10.30
@@ -99,7 +100,7 @@ services:
logging: *default-logging
volumes:
- ./docker/data/website/.cache:/usr/app/.cache
- - ./docker/data/website/.public:/usr/app/public
+ - ./docker/data/website/public:/usr/app/public
env_file:
- .env
networks:
diff --git a/docker/nginx/conf.d/include/location-skylink b/docker/nginx/conf.d/include/location-skylink
index b214e3a9..25f59815 100644
--- a/docker/nginx/conf.d/include/location-skylink
+++ b/docker/nginx/conf.d/include/location-skylink
@@ -37,6 +37,13 @@ proxy_set_header User-Agent: Sia-Agent;
proxy_pass http://sia:9980/skynet/skylink/$skylink$path$is_args$args;
+error_page 400 /etc/nginx/errors/400/index.html;
+error_page 401 /etc/nginx/errors/401/index.html;
+error_page 403 /etc/nginx/errors/403/index.html;
+error_page 404 /etc/nginx/errors/404/index.html;
+error_page 451 /etc/nginx/errors/451/index.html;
+error_page 500 /etc/nginx/errors/500/index.html;
+
log_by_lua_block {
local skynet_account = require("skynet.account")
local skynet_modules = require("skynet.modules")
diff --git a/packages/website/gatsby-browser.js b/packages/website/gatsby-browser.js
index e086f2cb..423eca08 100644
--- a/packages/website/gatsby-browser.js
+++ b/packages/website/gatsby-browser.js
@@ -17,6 +17,7 @@ import * as React from "react";
import Layout from "./src/components/Layout";
export const wrapPageElement = ({ element, props }) => {
+ if (props.uri.startsWith("/errors")) return element;
// props provide same data to Layout as Page element will get
// including location, data, etc - you don't need to pass it
return {element};
diff --git a/packages/website/src/components/ErrorPage.js b/packages/website/src/components/ErrorPage.js
new file mode 100644
index 00000000..eb2202ff
--- /dev/null
+++ b/packages/website/src/components/ErrorPage.js
@@ -0,0 +1,44 @@
+import * as React from "react";
+import { LogoBlackText } from "./Icons";
+import Seo from "./seo";
+
+export default function ErrorPage({ statusCode, statusReason, header, subheader, redirect, more }) {
+ React.useEffect(() => {
+ if (redirect && typeof window !== "undefined") {
+ setTimeout(() => {
+ window.location.href = redirect;
+ }, 3000); // 3s
+ }
+ }, [redirect]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
{header}
+
+
{subheader}
+
+
+ {more && {more}
}
+
+ {redirect && (
+
+ If you're not redirected automatically,{" "}
+
+ click here
+
+
+ )}
+
+
+
+ >
+ );
+}
diff --git a/packages/website/src/pages/errors/400.js b/packages/website/src/pages/errors/400.js
new file mode 100644
index 00000000..1921901b
--- /dev/null
+++ b/packages/website/src/pages/errors/400.js
@@ -0,0 +1,21 @@
+import * as React from "react";
+import ErrorPage from "../../components/ErrorPage";
+
+const statusCode = 400;
+const statusReason = "Bad Request";
+
+const header = "Bad Request";
+const subheader = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor";
+const redirect = null;
+
+export default function BadRequest() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/pages/errors/401.js b/packages/website/src/pages/errors/401.js
new file mode 100644
index 00000000..297f1706
--- /dev/null
+++ b/packages/website/src/pages/errors/401.js
@@ -0,0 +1,21 @@
+import * as React from "react";
+import ErrorPage from "../../components/ErrorPage";
+
+const statusCode = 401;
+const statusReason = "Unauthorized";
+
+const header = "You must be authenticated to access this content";
+const subheader = "You're being redirected to the Log In page of this Skynet Portal";
+const redirect = `https://account.${process.env.PORTAL_DOMAIN}/auth/login`;
+
+export default function Unauthorized() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/pages/errors/403.js b/packages/website/src/pages/errors/403.js
new file mode 100644
index 00000000..6a24a019
--- /dev/null
+++ b/packages/website/src/pages/errors/403.js
@@ -0,0 +1,21 @@
+import * as React from "react";
+import ErrorPage from "../../components/ErrorPage";
+
+const statusCode = 403;
+const statusReason = "Forbidden";
+
+const header = "You are not authorized to access this content";
+const subheader = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor";
+const redirect = null;
+
+export default function Forbidden() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/pages/errors/404.js b/packages/website/src/pages/errors/404.js
new file mode 100644
index 00000000..7fc8026f
--- /dev/null
+++ b/packages/website/src/pages/errors/404.js
@@ -0,0 +1,21 @@
+import * as React from "react";
+import ErrorPage from "../../components/ErrorPage";
+
+const statusCode = 404;
+const statusReason = "Not Found";
+
+const header = "We could not load this content";
+const subheader = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor";
+const redirect = null;
+
+export default function NotFound() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/pages/errors/451.js b/packages/website/src/pages/errors/451.js
new file mode 100644
index 00000000..722a7833
--- /dev/null
+++ b/packages/website/src/pages/errors/451.js
@@ -0,0 +1,34 @@
+import * as React from "react";
+import Link from "../../components/Link";
+import ErrorPage from "../../components/ErrorPage";
+
+const statusCode = 451;
+const statusReason = "Unavailable For Legal Reasons";
+
+const header = "This file is unavailable for legal reasons";
+const subheader = "This Skynet Portal has blocked access to this file, likely for legal reasons";
+const redirect = null;
+const more = (
+ <>
+ To learn more, see the{" "}
+
+ Skynet FAQ
+
+ >
+);
+
+export default function UnavailableForLegalReasons() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/pages/errors/500.js b/packages/website/src/pages/errors/500.js
new file mode 100644
index 00000000..9b8800e8
--- /dev/null
+++ b/packages/website/src/pages/errors/500.js
@@ -0,0 +1,21 @@
+import * as React from "react";
+import ErrorPage from "../../components/ErrorPage";
+
+const statusCode = 500;
+const statusReason = "Internal Server Error";
+
+const header = "Internal Server Error";
+const subheader = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor";
+const redirect = null;
+
+export default function InternalServerError() {
+ return (
+
+ );
+}
diff --git a/packages/website/src/styles/global.css b/packages/website/src/styles/global.css
index b7b62ed9..65fa8a04 100644
--- a/packages/website/src/styles/global.css
+++ b/packages/website/src/styles/global.css
@@ -2,6 +2,13 @@
@tailwind components;
@tailwind utilities;
+html,
+body,
+#___gatsby,
+#gatsby-focus-wrapper {
+ @apply h-full;
+}
+
.newsletter-message a {
@apply text-primary;
}
diff --git a/packages/website/tailwind.config.js b/packages/website/tailwind.config.js
index a9e9e91d..004baa78 100644
--- a/packages/website/tailwind.config.js
+++ b/packages/website/tailwind.config.js
@@ -58,15 +58,5 @@ module.exports = {
},
},
},
- plugins: [
- require("@tailwindcss/typography"),
- plugin(function ({ addBase, theme }) {
- addBase({
- body: {
- color: theme("textColor.palette.600"),
- backgroundColor: theme("backgroundColor.palette.500"),
- },
- });
- }),
- ],
+ plugins: [require("@tailwindcss/typography")],
};