Bläddra i källkod

feat: use nextjs public config instead of webpack env

poeti8 5 år sedan
förälder
incheckning
4952e88e4f

+ 5 - 2
client/components/Footer.tsx

@@ -1,4 +1,5 @@
 import React, { FC, useEffect } from "react";
+import getConfig from "next/config";
 
 import showRecaptcha from "../helpers/recaptcha";
 import { useStoreState } from "../store";
@@ -7,6 +8,8 @@ import ReCaptcha from "./ReCaptcha";
 import ALink from "./ALink";
 import Text from "./Text";
 
+const { publicRuntimeConfig } = getConfig();
+
 const Footer: FC = () => {
   const { isAuthenticated } = useStoreState(s => s.auth);
 
@@ -43,11 +46,11 @@ const Footer: FC = () => {
         <ALink href="/report" title="Report abuse">
           Report Abuse
         </ALink>
-        {process.env.CONTACT_EMAIL && (
+        {publicRuntimeConfig.CONTACT_EMAIL && (
           <>
             {" | "}
             <ALink
-              href={`mailto:${process.env.CONTACT_EMAIL}`}
+              href={`mailto:${publicRuntimeConfig.CONTACT_EMAIL}`}
               title="Contact us"
             >
               Contact us

+ 4 - 1
client/components/Header.tsx

@@ -1,4 +1,5 @@
 import { Flex } from "reflexbox/styled-components";
+import getConfig from "next/config";
 import React, { FC } from "react";
 import Router from "next/router";
 import useMedia from "use-media";
@@ -10,6 +11,8 @@ import { RowCenterV } from "./Layout";
 import { Button } from "./Button";
 import ALink from "./ALink";
 
+const { publicRuntimeConfig } = getConfig();
+
 const Li = styled(Flex).attrs({ ml: [12, 24, 32] })`
   a {
     color: inherit;
@@ -101,7 +104,7 @@ const Header: FC = () => {
             }}
           >
             <img src="/images/logo.svg" alt="" />
-            {process.env.SITE_NAME}
+            {publicRuntimeConfig.SITE_NAME}
           </a>
         </LogoImage>
         {!isMobile && (

+ 4 - 1
client/components/LinksTable.tsx

@@ -5,6 +5,7 @@ import { useFormState } from "react-use-form-state";
 import { Flex } from "reflexbox/styled-components";
 import styled, { css } from "styled-components";
 import { ifProp } from "styled-tools";
+import getConfig from "next/config";
 import QRCode from "qrcode.react";
 import Link from "next/link";
 
@@ -24,6 +25,8 @@ import ALink from "./ALink";
 import Modal from "./Modal";
 import Icon from "./Icon";
 
+const { publicRuntimeConfig } = getConfig();
+
 const Tr = styled(Flex).attrs({ as: "tr", px: [12, 12, 2] })``;
 const Th = styled(Flex)``;
 Th.defaultProps = { as: "th", flexBasis: 0, py: [12, 12, 3], px: [12, 12, 3] };
@@ -341,7 +344,7 @@ const Row: FC<RowProps> = ({ index, link, setDeleteModal }) => {
                   fontSize={[14, 15]}
                   bold
                 >
-                  {link.domain || process.env.DEFAULT_DOMAIN}/
+                  {link.domain || publicRuntimeConfig.DEFAULT_DOMAIN}/
                 </Text>
                 <Flex as="form">
                   <TextInput

+ 8 - 5
client/components/ReCaptcha.tsx

@@ -1,9 +1,12 @@
-import React from 'react';
-import styled from 'styled-components';
-import { Flex } from 'reflexbox/styled-components';
+import { Flex } from "reflexbox/styled-components";
+import styled from "styled-components";
+import getConfig from "next/config";
+import React from "react";
+
+const { publicRuntimeConfig } = getConfig();
 
 const ReCaptcha = () => {
-  if (process.env.NODE_ENV !== 'production') {
+  if (process.env.NODE_ENV !== "production") {
     return null;
   }
 
@@ -12,7 +15,7 @@ const ReCaptcha = () => {
       margin="54px 0 16px"
       id="g-recaptcha"
       className="g-recaptcha"
-      data-sitekey={process.env.RECAPTCHA_SITE_KEY}
+      data-sitekey={publicRuntimeConfig.RECAPTCHA_SITE_KEY}
       data-callback="recaptchaCallback"
       data-size="invisible"
       data-badge="inline"

+ 6 - 1
client/components/Settings/SettingsDeleteAccount.tsx

@@ -1,5 +1,6 @@
 import { useFormState } from "react-use-form-state";
 import React, { FC, useState } from "react";
+import getConfig from "next/config";
 import Router from "next/router";
 import axios from "axios";
 
@@ -13,6 +14,8 @@ import { Button } from "../Button";
 import Icon from "../Icon";
 import Modal from "../Modal";
 
+const { publicRuntimeConfig } = getConfig();
+
 const SettingsDeleteAccount: FC = () => {
   const [message, setMessage] = useMessage(1500);
   const [loading, setLoading] = useState(false);
@@ -52,7 +55,9 @@ const SettingsDeleteAccount: FC = () => {
       <H2 mb={4} bold>
         Delete account
       </H2>
-      <Text mb={4}>Delete your account from {process.env.SITE_NAME}.</Text>
+      <Text mb={4}>
+        Delete your account from {publicRuntimeConfig.SITE_NAME}.
+      </Text>
       <Text
         {...label("password")}
         as="label"

+ 53 - 50
client/components/Settings/SettingsDomain.tsx

@@ -2,6 +2,7 @@ import { useFormState } from "react-use-form-state";
 import { Flex } from "reflexbox/styled-components";
 import React, { FC, useState } from "react";
 import styled from "styled-components";
+import getConfig from "next/config";
 
 import { useStoreState, useStoreActions } from "../../store";
 import { Domain } from "../../store/settings";
@@ -16,6 +17,8 @@ import Table from "../Table";
 import Modal from "../Modal";
 import Icon from "../Icon";
 
+const { publicRuntimeConfig } = getConfig();
+
 const Th = styled(Flex).attrs({ as: "th", py: 3, px: 3 })`
   font-size: 15px;
 `;
@@ -71,7 +74,7 @@ const SettingsDomain: FC = () => {
       </H2>
       <Text mb={3}>
         You can set a custom domain for your short URLs, so instead of{" "}
-        <b>{process.env.DEFAULT_DOMAIN}/shorturl</b> you can have{" "}
+        <b>{publicRuntimeConfig.DEFAULT_DOMAIN}/shorturl</b> you can have{" "}
         <b>example.com/shorturl.</b>
       </Text>
       <Text mb={4}>
@@ -92,7 +95,7 @@ const SettingsDomain: FC = () => {
               <tr key={d.address}>
                 <Td width={2 / 5}>{d.address}</Td>
                 <Td width={2 / 5}>
-                  {d.homepage || process.env.DEFAULT_DOMAIN}
+                  {d.homepage || publicRuntimeConfig.DEFAULT_DOMAIN}
                 </Td>
                 <Td width={1 / 5} justifyContent="center">
                   <Icon
@@ -116,54 +119,54 @@ const SettingsDomain: FC = () => {
           </tbody>
         </Table>
       )}
-        <Col
-          alignItems="flex-start"
-          onSubmit={onSubmit}
-          width={1}
-          as="form"
-          my={[3, 4]}
-        >
-          <Flex width={1} flexDirection={["column", "row"]}>
-            <Col mr={[0, 2]} mb={[3, 0]} flex="0 0 auto">
-              <Text
-                {...label("address")}
-                as="label"
-                mb={[2, 3]}
-                fontSize={[15, 16]}
-                bold
-              >
-                Domain
-              </Text>
-              <TextInput
-                {...text("address")}
-                placeholder="example.com"
-                maxWidth="240px"
-                required
-              />
-            </Col>
-            <Col ml={[0, 2]} flex="0 0 auto">
-              <Text
-                {...label("homepage")}
-                as="label"
-                mb={[2, 3]}
-                fontSize={[15, 16]}
-                bold
-              >
-                Homepage (optional)
-              </Text>
-              <TextInput
-                {...text("homepage")}
-                placeholder="Homepage URL"
-                flex="1 1 auto"
-                maxWidth="240px"
-              />
-            </Col>
-          </Flex>
-          <Button type="submit" color="purple" mt={[24, 3]} disabled={loading}>
-            <Icon name={loading ? "spinner" : "plus"} mr={2} stroke="white" />
-            {loading ? "Setting..." : "Set domain"}
-          </Button>
-        </Col>
+      <Col
+        alignItems="flex-start"
+        onSubmit={onSubmit}
+        width={1}
+        as="form"
+        my={[3, 4]}
+      >
+        <Flex width={1} flexDirection={["column", "row"]}>
+          <Col mr={[0, 2]} mb={[3, 0]} flex="0 0 auto">
+            <Text
+              {...label("address")}
+              as="label"
+              mb={[2, 3]}
+              fontSize={[15, 16]}
+              bold
+            >
+              Domain
+            </Text>
+            <TextInput
+              {...text("address")}
+              placeholder="example.com"
+              maxWidth="240px"
+              required
+            />
+          </Col>
+          <Col ml={[0, 2]} flex="0 0 auto">
+            <Text
+              {...label("homepage")}
+              as="label"
+              mb={[2, 3]}
+              fontSize={[15, 16]}
+              bold
+            >
+              Homepage (optional)
+            </Text>
+            <TextInput
+              {...text("homepage")}
+              placeholder="Homepage URL"
+              flex="1 1 auto"
+              maxWidth="240px"
+            />
+          </Col>
+        </Flex>
+        <Button type="submit" color="purple" mt={[24, 3]} disabled={loading}>
+          <Icon name={loading ? "spinner" : "plus"} mr={2} stroke="white" />
+          {loading ? "Setting..." : "Set domain"}
+        </Button>
+      </Col>
       <Text color={message.color}>{message.text}</Text>
       <Modal id="delete-custom-domain" show={modal} closeHandler={closeModal}>
         <H2 mb={24} textAlign="center" bold>

+ 4 - 1
client/components/Shortener.tsx

@@ -3,6 +3,7 @@ import { useFormState } from "react-use-form-state";
 import { Flex } from "reflexbox/styled-components";
 import React, { FC, useState } from "react";
 import styled from "styled-components";
+import getConfig from "next/config";
 
 import { useStoreActions, useStoreState } from "../store";
 import { Checkbox, Select, TextInput } from "./Input";
@@ -15,6 +16,8 @@ import Animation from "./Animation";
 import { Colors } from "../consts";
 import Icon from "./Icon";
 
+const { publicRuntimeConfig } = getConfig();
+
 const SubmitIconWrapper = styled.div`
   content: "";
   position: absolute;
@@ -55,7 +58,7 @@ interface Form {
   showAdvanced?: boolean;
 }
 
-const defaultDomain = process.env.DEFAULT_DOMAIN;
+const defaultDomain = publicRuntimeConfig.DEFAULT_DOMAIN;
 
 const Shortener = () => {
   const { isAuthenticated } = useStoreState(s => s.auth);

+ 4 - 1
client/helpers/analytics.ts

@@ -1,7 +1,10 @@
+import getConfig from "next/config";
 import ReactGA from "react-ga";
 
+const { publicRuntimeConfig } = getConfig();
+
 export const initGA = () => {
-  ReactGA.initialize(process.env.GOOGLE_ANALYTICS);
+  ReactGA.initialize(publicRuntimeConfig.GOOGLE_ANALYTICS);
 };
 
 export const logPageView = () => {

+ 3 - 2
client/pages/_app.tsx

@@ -1,5 +1,6 @@
 import App, { AppContext } from "next/app";
 import { StoreProvider } from "easy-peasy";
+import getConfig from "next/config";
 import Router from "next/router";
 import decode from "jwt-decode";
 import cookie from "js-cookie";
@@ -9,9 +10,9 @@ import React from "react";
 import { initGA, logPageView } from "../helpers/analytics";
 import { initializeStore } from "../store";
 import { TokenPayload } from "../types";
-import AppWrapper from "../components/AppWrapper";
 
 const isProd = process.env.NODE_ENV === "production";
+const { publicRuntimeConfig } = getConfig();
 
 // TODO: types
 class MyApp extends App<any> {
@@ -74,7 +75,7 @@ class MyApp extends App<any> {
       <>
         <Head>
           <title>
-            {process.env.SITE_NAME} | Modern Open Source URL shortener.
+            {publicRuntimeConfig.SITE_NAME} | Modern Open Source URL shortener.
           </title>
         </Head>
         <StoreProvider store={this.store}>

+ 11 - 8
client/pages/_document.tsx

@@ -1,9 +1,12 @@
-import React from "react";
 import Document, { Head, Main, NextScript } from "next/document";
 import { ServerStyleSheet } from "styled-components";
+import getConfig from "next/config";
+import React from "react";
 
 import { Colors } from "../consts";
 
+const { publicRuntimeConfig } = getConfig();
+
 interface Props {
   styleTags: any;
 }
@@ -29,7 +32,7 @@ class AppDocument extends Document<Props> {
           />
           <meta
             name="description"
-            content={`${process.env.SITE_NAME} is a free and open source URL shortener with custom domains and stats.`}
+            content={`${publicRuntimeConfig.SITE_NAME} is a free and open source URL shortener with custom domains and stats.`}
           />
           <link
             href="https://fonts.googleapis.com/css?family=Nunito:300,400,700"
@@ -46,13 +49,13 @@ class AppDocument extends Document<Props> {
           <meta property="fb:app_id" content="123456789" />
           <meta
             property="og:url"
-            content={`https://${process.env.DEFAULT_DOMAIN}`}
+            content={`https://${publicRuntimeConfig.DEFAULT_DOMAIN}`}
           />
           <meta property="og:type" content="website" />
-          <meta property="og:title" content={process.env.SITE_NAME} />
+          <meta property="og:title" content={publicRuntimeConfig.SITE_NAME} />
           <meta
             property="og:image"
-            content={`https://${process.env.DEFAULT_DOMAIN}/images/card.png`}
+            content={`https://${publicRuntimeConfig.DEFAULT_DOMAIN}/images/card.png`}
           />
           <meta
             property="og:description"
@@ -60,16 +63,16 @@ class AppDocument extends Document<Props> {
           />
           <meta
             name="twitter:url"
-            content={`https://${process.env.DEFAULT_DOMAIN}`}
+            content={`https://${publicRuntimeConfig.DEFAULT_DOMAIN}`}
           />
-          <meta name="twitter:title" content={process.env.SITE_NAME} />
+          <meta name="twitter:title" content={publicRuntimeConfig.SITE_NAME} />
           <meta
             name="twitter:description"
             content="Free & Open Source Modern URL Shortener"
           />
           <meta
             name="twitter:image"
-            content={`https://${process.env.DEFAULT_DOMAIN}/images/card.png`}
+            content={`https://${publicRuntimeConfig.DEFAULT_DOMAIN}/images/card.png`}
           />
 
           {this.props.styleTags}

+ 4 - 2
client/pages/banned.tsx

@@ -1,4 +1,4 @@
-import { Flex } from "reflexbox/styled-components";
+import getConfig from "next/config";
 import Link from "next/link";
 import React from "react";
 
@@ -8,6 +8,8 @@ import Footer from "../components/Footer";
 import ALink from "../components/ALink";
 import { Col } from "../components/Layout";
 
+const { publicRuntimeConfig } = getConfig();
+
 const BannedPage = () => {
   return (
     <AppWrapper>
@@ -21,7 +23,7 @@ const BannedPage = () => {
         </H2>
         <H4 textAlign="center" normal>
           If you noticed a malware/scam link shortened by{" "}
-          {process.env.SITE_NAME},{" "}
+          {publicRuntimeConfig.SITE_NAME},{" "}
           <Link href="/report">
             <ALink title="Send report">send us a report</ALink>
           </Link>

+ 6 - 2
client/pages/report.tsx

@@ -12,6 +12,10 @@ import Icon from "../components/Icon";
 import { useMessage } from "../hooks";
 import { APIv2 } from "../consts";
 
+import getConfig from "next/config";
+
+const { publicRuntimeConfig } = getConfig();
+
 const ReportPage = () => {
   const [formState, { text }] = useFormState<{ url: string }>();
   const [loading, setLoading] = useState(false);
@@ -43,7 +47,7 @@ const ReportPage = () => {
           or use the form. We will take actions shortly.
         </Text>
         <Text mb={4}>
-          {(process.env.REPORT_EMAIL || "").replace("@", "[at]")}
+          {(publicRuntimeConfig.REPORT_EMAIL || "").replace("@", "[at]")}
         </Text>
         <Text mb={3}>
           <Span bold>URL containing malware/scam:</Span>
@@ -57,7 +61,7 @@ const ReportPage = () => {
         >
           <TextInput
             {...text("url")}
-            placeholder={`${process.env.DEFAULT_DOMAIN}/example`}
+            placeholder={`${publicRuntimeConfig.DEFAULT_DOMAIN}/example`}
             height={[44, 54]}
             width={[1, 1 / 2]}
             flex="0 0 auto"

+ 32 - 27
client/pages/terms.tsx

@@ -1,18 +1,20 @@
+import getConfig from "next/config";
 import React from "react";
-import { Flex } from "reflexbox/styled-components";
 
 import AppWrapper from "../components/AppWrapper";
 import { Col } from "../components/Layout";
 
+const { publicRuntimeConfig } = getConfig();
+
 const TermsPage = () => (
   <AppWrapper>
     {/* TODO: better container */}
     <Col width={600} maxWidth="97%" alignItems="flex-start">
-      <h3>{process.env.SITE_NAME} Terms of Service</h3>
+      <h3>{publicRuntimeConfig.SITE_NAME} Terms of Service</h3>
       <p>
         By accessing the website at{" "}
-        <a href={`https://${process.env.DEFAULT_DOMAIN}`}>
-          https://{process.env.DEFAULT_DOMAIN}
+        <a href={`https://${publicRuntimeConfig.DEFAULT_DOMAIN}`}>
+          https://{publicRuntimeConfig.DEFAULT_DOMAIN}
         </a>
         , you are agreeing to be bound by these terms of service, all applicable
         laws and regulations, and agree that you are responsible for compliance
@@ -22,35 +24,38 @@ const TermsPage = () => (
         copyright and trademark law.
       </p>
       <p>
-        In no event shall {process.env.SITE_NAME} or its suppliers be liable for
-        any damages (including, without limitation, damages for loss of data or
-        profit, or due to business interruption) arising out of the use or
-        inability to use the materials on {process.env.DEFAULT_DOMAIN} website,
-        even if {process.env.SITE_NAME} or a {process.env.SITE_NAME} authorized
-        representative has been notified orally or in writing of the possibility
-        of such damage. Because some jurisdictions do not allow limitations on
-        implied warranties, or limitations of liability for consequential or
-        incidental damages, these limitations may not apply to you.
+        In no event shall {publicRuntimeConfig.SITE_NAME} or its suppliers be
+        liable for any damages (including, without limitation, damages for loss
+        of data or profit, or due to business interruption) arising out of the
+        use or inability to use the materials on{" "}
+        {publicRuntimeConfig.DEFAULT_DOMAIN} website, even if{" "}
+        {publicRuntimeConfig.SITE_NAME} or a {publicRuntimeConfig.SITE_NAME}{" "}
+        authorized representative has been notified orally or in writing of the
+        possibility of such damage. Because some jurisdictions do not allow
+        limitations on implied warranties, or limitations of liability for
+        consequential or incidental damages, these limitations may not apply to
+        you.
       </p>
       <p>
-        The materials appearing on {process.env.SITE_NAME} website could include
-        technical, typographical, or photographic errors.{" "}
-        {process.env.SITE_NAME} does not warrant that any of the materials on
-        its website are accurate, complete or current. {process.env.SITE_NAME}{" "}
-        may make changes to the materials contained on its website at any time
-        without notice. However {process.env.SITE_NAME} does not make any
-        commitment to update the materials.
+        The materials appearing on {publicRuntimeConfig.SITE_NAME} website could
+        include technical, typographical, or photographic errors.{" "}
+        {publicRuntimeConfig.SITE_NAME} does not warrant that any of the
+        materials on its website are accurate, complete or current.{" "}
+        {publicRuntimeConfig.SITE_NAME} may make changes to the materials
+        contained on its website at any time without notice. However{" "}
+        {publicRuntimeConfig.SITE_NAME} does not make any commitment to update
+        the materials.
       </p>
       <p>
-        {process.env.SITE_NAME} has not reviewed all of the sites linked to its
-        website and is not responsible for the contents of any such linked site.
-        The inclusion of any link does not imply endorsement by{" "}
-        {process.env.SITE_NAME} of the site. Use of any such linked website is
-        at the {"user's"} own risk.
+        {publicRuntimeConfig.SITE_NAME} has not reviewed all of the sites linked
+        to its website and is not responsible for the contents of any such
+        linked site. The inclusion of any link does not imply endorsement by{" "}
+        {publicRuntimeConfig.SITE_NAME} of the site. Use of any such linked
+        website is at the {"user's"} own risk.
       </p>
       <p>
-        {process.env.SITE_NAME} may revise these terms of service for its
-        website at any time without notice. By using this website you are
+        {publicRuntimeConfig.SITE_NAME} may revise these terms of service for
+        its website at any time without notice. By using this website you are
         agreeing to be bound by the then current version of these terms of
         service.
       </p>

+ 8 - 6
next.config.js

@@ -1,10 +1,12 @@
-const { parsed: localEnv } = require("dotenv").config();
-const webpack = require("webpack");
+const env = require("./production-server/env").default;
 
 module.exports = {
-  webpack(config) {
-    config.plugins.push(new webpack.EnvironmentPlugin(localEnv));
-
-    return config;
+  publicRuntimeConfig: {
+    CONTACT_EMAIL: env.CONTACT_EMAIL,
+    SITE_NAME: env.SITE_NAME,
+    DEFAULT_DOMAIN: env.DEFAULT_DOMAIN,
+    RECAPTCHA_SITE_KEY: env.RECAPTCHA_SITE_KEY,
+    GOOGLE_ANALYTICS: env.GOOGLE_ANALYTICS,
+    REPORT_EMAIL: env.REPORT_EMAIL
   }
 };