mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
feat: remove app next.js built-time environment variable requirement (#382)
* feat: retreive stripe public key via runtime environment variable instead of a build-time environment variable * feat: retreive google and github enabled environment variable via runtime environment variable instead of a build-time environment variable * feat: retreive app base url environment variable via runtime environment variable instead of a build-time environment variable * feat: retreive mixpanel token environment variable via runtime environment variable instead of a build-time environment variable * lazy initialize app info * feat: provide ga tarcking id and crisp website id via environment variable * make docs url optional * feat: load sentry config from environment variables * document hive app environment variables * add application dockerfile * add docker build instructions * lul * set working directory * pls fix * lol this is apollo-router not graphql-hive * feat: only show sentry stuff if sentry environment variables are set * use LTS node version * No mixpanel * Fallback Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com>
This commit is contained in:
parent
2931441473
commit
a03cc58e5b
34 changed files with 380 additions and 125 deletions
2
.github/workflows/cd.yaml
vendored
2
.github/workflows/cd.yaml
vendored
|
|
@ -111,7 +111,7 @@ jobs:
|
|||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
IMAGE_NAME: ${{ github.repository }}/apollo-router
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
|
|||
62
.github/workflows/ci.yaml
vendored
62
.github/workflows/ci.yaml
vendored
|
|
@ -109,8 +109,6 @@ jobs:
|
|||
|
||||
- name: Build
|
||||
run: yarn workspace integration-tests run build-and-pack
|
||||
env:
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.TEST_STRIPE_PUBLIC_KEY }}
|
||||
|
||||
- name: Pull images
|
||||
run: docker-compose -f integration-tests/docker-compose.yml pull
|
||||
|
|
@ -119,6 +117,7 @@ jobs:
|
|||
run: yarn workspace integration-tests run dockest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
STRIPE_PUBLIC_KEY: ${{ secrets.TEST_STRIPE_PUBLIC_KEY }}
|
||||
STRIPE_SECRET_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY }}
|
||||
SUPERTOKENS_CONNECTION_URI: http://127.0.0.1:3567
|
||||
SUPERTOKENS_API_KEY: bubatzbieber6942096420
|
||||
|
|
@ -177,8 +176,6 @@ jobs:
|
|||
- name: Build
|
||||
if: steps.feature_flags.outputs.detected == 'true'
|
||||
run: yarn workspace integration-tests run build-and-pack
|
||||
env:
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.TEST_STRIPE_PUBLIC_KEY }}
|
||||
|
||||
- name: Pull images
|
||||
if: steps.feature_flags.outputs.detected == 'true'
|
||||
|
|
@ -189,6 +186,7 @@ jobs:
|
|||
run: yarn workspace integration-tests run dockest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
STRIPE_PUBLIC_KEY: ${{ secrets.TEST_STRIPE_PUBLIC_KEY }}
|
||||
STRIPE_SECRET_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY }}
|
||||
SUPERTOKENS_CONNECTION_URI: http://127.0.0.1:3567
|
||||
SUPERTOKENS_API_KEY: bubatzbieber6942096420
|
||||
|
|
@ -404,3 +402,59 @@ jobs:
|
|||
|
||||
- name: Lint Prettier
|
||||
run: yarn lint:prettier
|
||||
|
||||
publish_app:
|
||||
needs: setup
|
||||
name: Publish for Docker
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
|
||||
- name: Pull node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ github.sha }}
|
||||
|
||||
- name: Setup Turbo
|
||||
run: node ./scripts/turborepo-setup.js
|
||||
|
||||
- name: Generate Types
|
||||
run: yarn graphql:generate
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
file: packages/web/app/Dockerfile
|
||||
context: ./packages/web/app
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ github.repository }}/app:${{ github.sha }}
|
||||
|
|
|
|||
|
|
@ -62,28 +62,22 @@ export function deployApp({
|
|||
env: [
|
||||
{ name: 'DEPLOYED_DNS', value: deploymentEnv.DEPLOYED_DNS },
|
||||
{ name: 'NODE_ENV', value: 'production' },
|
||||
{ name: 'ENVIRONMENT', value: deploymentEnv.ENVIRONMENT },
|
||||
{
|
||||
name: 'NEXT_PUBLIC_ENVIRONMENT',
|
||||
name: 'ENVIRONMENT',
|
||||
value: deploymentEnv.ENVIRONMENT,
|
||||
},
|
||||
{
|
||||
name: 'RELEASE',
|
||||
value: appRelease,
|
||||
},
|
||||
{
|
||||
name: 'NEXT_PUBLIC_RELEASE',
|
||||
value: appRelease,
|
||||
},
|
||||
{ name: 'SENTRY_DSN', value: commonEnv.SENTRY_DSN },
|
||||
{ name: 'NEXT_PUBLIC_SENTRY_DSN', value: commonEnv.SENTRY_DSN },
|
||||
{ name: 'SENTRY_ENABLED', value: commonEnv.SENTRY_ENABLED },
|
||||
{
|
||||
name: 'GRAPHQL_ENDPOINT',
|
||||
value: serviceLocalEndpoint(graphql.service).apply(s => `${s}/graphql`),
|
||||
},
|
||||
{
|
||||
name: 'NEXT_PUBLIC_APP_BASE_URL',
|
||||
name: 'APP_BASE_URL',
|
||||
value: `https://${deploymentEnv.DEPLOYED_DNS}/`,
|
||||
},
|
||||
{
|
||||
|
|
@ -99,6 +93,26 @@ export function deployApp({
|
|||
value: githubAppConfig.require('name'),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'STRIPE_PUBLIC_KEY',
|
||||
value: appEnv.STRIPE_PUBLIC_KEY,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'GA_TRACKING_ID',
|
||||
value: appEnv.GA_TRACKING_ID,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'CRISP_WEBSITE_ID',
|
||||
value: appEnv.CRISP_WEBSITE_ID,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'DOCS_URL',
|
||||
value: appEnv.DOCS_URL,
|
||||
},
|
||||
|
||||
//
|
||||
// AUTH
|
||||
//
|
||||
|
|
@ -146,7 +160,7 @@ export function deployApp({
|
|||
},
|
||||
// GitHub
|
||||
{
|
||||
name: 'NEXT_PUBLIC_AUTH_GITHUB',
|
||||
name: 'AUTH_GITHUB',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
|
|
@ -159,7 +173,7 @@ export function deployApp({
|
|||
},
|
||||
// Google
|
||||
{
|
||||
name: 'NEXT_PUBLIC_AUTH_GOOGLE',
|
||||
name: 'AUTH_GOOGLE',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
GRAPHQL_ENDPOINT="http://localhost:4000/graphql"
|
||||
APP_BASE_URL="http://localhost:3000"
|
||||
NEXT_PUBLIC_APP_BASE_URL="http://localhost:3000"
|
||||
|
||||
# Supertokens
|
||||
|
||||
|
|
@ -10,12 +9,12 @@ SUPERTOKENS_API_KEY="bubatzbieber6942096420"
|
|||
# Auth Provider
|
||||
|
||||
## Enable GitHub login
|
||||
NEXT_PUBLIC_AUTH_GITHUB=1
|
||||
AUTH_GITHUB=1
|
||||
AUTH_GITHUB_CLIENT_ID="<sync>"
|
||||
AUTH_GITHUB_CLIENT_SECRET="<sync>"
|
||||
|
||||
## Enable Google login
|
||||
NEXT_PUBLIC_AUTH_GOOGLE=1
|
||||
AUTH_GOOGLE=1
|
||||
AUTH_GOOGLE_CLIENT_ID="<sync>"
|
||||
AUTH_GOOGLE_CLIENT_SECRET="<sync>"
|
||||
|
||||
|
|
@ -38,7 +37,7 @@ SLACK_CLIENT_SECRET="<sync>"
|
|||
GITHUB_APP_NAME="<sync>"
|
||||
|
||||
# Stripe
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY="<sync>"
|
||||
STRIPE_PUBLIC_KEY="<sync>"
|
||||
|
||||
# Emails Service
|
||||
|
||||
|
|
|
|||
24
packages/web/app/Dockerfile
Normal file
24
packages/web/app/Dockerfile
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
FROM node:16-slim as install
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY dist/ /usr/src/app/
|
||||
|
||||
ENV NODE_ENV production
|
||||
|
||||
# DANGER: there is no lockfile :)
|
||||
# in the future this should be improved...
|
||||
|
||||
RUN npm install --legacy-peer-deps
|
||||
|
||||
FROM node:16-slim as app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY --from=install /usr/src/app/ /usr/src/app/
|
||||
|
||||
ENV ENVIRONMENT production
|
||||
ENV RELEASE ${RELEASE}
|
||||
ENV PORT 3000
|
||||
|
||||
CMD ["node", "index.js"]
|
||||
63
packages/web/app/README.md
Normal file
63
packages/web/app/README.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# `@hive/app`
|
||||
|
||||
The Hive application as seen on https://app.graphql-hive.com/.
|
||||
|
||||
## Configuration
|
||||
|
||||
The following environment variables configure the application.
|
||||
|
||||
| Name | Required | Description | Example Value |
|
||||
| ---------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `APP_BASE_URL` | **Yes** | The base url of the app, | `https://app.graphql-hive.com` |
|
||||
| `GRAPHQL_ENDPOINT` | **Yes** | The endpoint of the Hive GraphQL API. | `http://127.0.0.1:4000/graphql` |
|
||||
| `EMAILS_ENDPOINT` | **Yes** | The endpoint of the GraphQL Hive Email service. | `http://127.0.0.1:6260` |
|
||||
| `SUPERTOKENS_CONNECTION_URI` | **Yes** | The URI of the SuperTokens instance. | `http://127.0.0.1:3567` |
|
||||
| `SUPERTOKENS_API_KEY` | **Yes** | The API KEY of the SuperTokens instance. | `iliketurtlesandicannotlie` |
|
||||
| `SLACK_CLIENT_ID` | **Yes** | The Slack client ID. | `g6aff8102efda5e1d12e` |
|
||||
| `SLACK_CLIENT_SECRET` | **Yes** | The Slack client secret. | `g12e552xx54xx2b127821dc4abc4491dxxxa6b187` |
|
||||
| `GITHUB_APP_NAME` | **Yes** | The GitHub App name. | `graphql-hive-self-hosted` |
|
||||
| `AUTH_GITHUB` | No | Whether login via GitHub should be allowed | `1` (enabled) or `0` (disabled) |
|
||||
| `AUTH_GITHUB_CLIENT_ID` | No (**Yes** if `AUTH_GITHUB` is set) | The GitHub client ID. | `g6aff8102efda5e1d12e` |
|
||||
| `AUTH_GITHUB_CLIENT_SECRET` | No (**Yes** if `AUTH_GITHUB` is set) | The GitHub client secret. | `g12e552xx54xx2b127821dc4abc4491dxxxa6b187` |
|
||||
| `AUTH_GOOGLE` | No | Whether login via GitHub should be allowed | `1` (enabled) or `0` (disabled) |
|
||||
| `AUTH_GOOGLE_CLIENT_ID` | No (**Yes** if `AUTH_GOOGLE` is set) | The Google client ID. | `g6aff8102efda5e1d12e` |
|
||||
| `AUTH_GOOGLE_CLIENT_SECRET` | No (**Yes** if `AUTH_GOOGLE` is set) | The Google client secret. | `g12e552xx54xx2b127821dc4abc4491dxxxa6b187` |
|
||||
| `ENVIRONMENT` | No | The environment of your Hive app. (**Note:** This will be used for Sentry reporting.) | `staging` |
|
||||
| `SENTRY_DSN` | No | The DSN for reporting errors to Sentry. | `https://dooobars@o557896.ingest.sentry.io/12121212` |
|
||||
| `SENTRY_ENABLED` | No | Whether Sentry error reporting should be enabled. | `1` (enabled) or `0` (disabled) |
|
||||
| `DOCS_URL` | No | The URL of the Hive Docs | `https://docs.graphql-hive.com` |
|
||||
| `NODE_ENV` | No | The `NODE_ENV` value. | `production` |
|
||||
| `GA_TRACKING_ID` | No | The token for Google Analytics in order to track user actions. | `g6aff8102efda5e1d12e` |
|
||||
| `CRISP_WEBSITE_ID` | No | The Crisp Website ID | `g6aff8102efda5e1d12e` |
|
||||
|
||||
## Hive Hosted Configuration
|
||||
|
||||
This is only important if you are hosting Hive for getting 💰.
|
||||
|
||||
### Payments
|
||||
|
||||
| Name | Required | Description | Example Value |
|
||||
| ------------------- | -------- | ---------------------- | ---------------------- |
|
||||
| `STRIPE_PUBLIC_KEY` | No | The Stripe Public Key. | `g6aff8102efda5e1d12e` |
|
||||
|
||||
### Legacy Auth0 Configuration
|
||||
|
||||
If you are not self-hosting GraphQL Hive, you can ignore this section. It is only required for the legacy Auth0 compatibility layer.
|
||||
|
||||
| Name | Required | Description | Example Value |
|
||||
| ----------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
|
||||
| `AUTH_LEGACY_AUTH0` | No | Whether the legacy Auth0 import is enabled. | `1` (enabled) or `0` (disabled) |
|
||||
| `AUTH_LEGACY_AUTH0_CLIENT_ID` | No (**Yes** if `AUTH_LEGACY_AUTH0` is set) | The Auth0 client ID. | `rDSpExxD8sfqlpF1kbxxLkMNYI2Sxxx` |
|
||||
| `AUTH_LEGACY_AUTH0_CLIENT_SECRET` | No (**Yes** if `AUTH_LEGACY_AUTH0` is set) | The Auth0 client secret. | `e43f156xx54en2b56117dc4abc4491dxxbb6b187` |
|
||||
| `AUTH_LEGACY_AUTH0_ISSUER_BASE_URL` | No (**Yes** if `AUTH_LEGACY_AUTH0` is set) | The Auth0 issuer base url. | `https://your-project.us.auth0.com` |
|
||||
| `AUTH_LEGACY_AUTH0_AUDIENCE` | No (**Yes** if `AUTH_LEGACY_AUTH0` is set) | The Auth0 audience | `https://your-project.us.auth0.com/api/v2/` |
|
||||
| `AUTH_LEGACY_AUTH0_INTERNAL_API_ENDPOINT` | No (**Yes** if `AUTH_LEGACY_AUTH0` is set) | The internal endpoint for importing Auth0 accounts. (**Note:** This route is within the GraphQL service.) | `http://127.0.0.1:4000/__legacy` |
|
||||
| `AUTH_LEGACY_AUTH0_INTERNAL_API_KEY` | No (**Yes** if `AUTH_LEGACY_AUTH0` is set) | The internal endpoint key. | `iliketurtles` |
|
||||
|
||||
### Building the Docker Image
|
||||
|
||||
**Prerequisites:** Make sure you built the mono-repository using `yarn build`.
|
||||
|
||||
```bash
|
||||
docker build . --build-arg RELEASE=stable-main -t graphql-hive/app
|
||||
```
|
||||
19
packages/web/app/modules.d.ts
vendored
19
packages/web/app/modules.d.ts
vendored
|
|
@ -10,9 +10,26 @@ declare module '@n1ru4l/react-time-ago' {
|
|||
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NEXT_PUBLIC_APP_BASE_URL: string;
|
||||
APP_BASE_URL: string;
|
||||
GITHUB_APP_NAME: string;
|
||||
GRAPHQL_ENDPOINT: string;
|
||||
SUPERTOKENS_CONNECTION_URI: string;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
declare var __ENV__:
|
||||
| undefined
|
||||
| {
|
||||
APP_BASE_URL: string;
|
||||
DOCS_URL: string | undefined;
|
||||
STRIPE_PUBLIC_KEY: string | undefined;
|
||||
AUTH_GITHUB: string | undefined;
|
||||
AUTH_GOOGLE: string | undefined;
|
||||
GA_TRACKING_ID: string | undefined;
|
||||
CRISP_WEBSITE_ID: string | undefined;
|
||||
SENTRY_DSN: string | undefined;
|
||||
RELEASE: string | undefined;
|
||||
ENVIRONMENT: string | undefined;
|
||||
SENTRY_ENABLED: string | undefined;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
ProjectFieldsFragment,
|
||||
TargetFieldsFragment,
|
||||
} from '@/graphql';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
|
||||
function floorDate(date: Date): Date {
|
||||
const time = 1000 * 60;
|
||||
|
|
@ -144,7 +145,7 @@ const OperationsViewGate = ({
|
|||
<EmptyList
|
||||
title="Hive is waiting for your first collected operation"
|
||||
description="You can collect usage of your GraphQL API with Hive Client"
|
||||
docsUrl={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/monitoring`}
|
||||
docsUrl={getDocsUrl(`/features/monitoring`)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { ProjectLayout } from '@/components/layouts';
|
|||
import { Activities, Badge, Button, Card, DropdownMenu, EmptyList, Heading, TimeAgo, Title } from '@/components/v2';
|
||||
import { LinkIcon, MoreIcon, SettingsIcon } from '@/components/v2/icon';
|
||||
import { TargetQuery, TargetsDocument, VersionsDocument } from '@/graphql';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
import { useClipboard } from '@/lib/hooks/use-clipboard';
|
||||
import { useRouteSelector } from '@/lib/hooks/use-route-selector';
|
||||
|
||||
|
|
@ -109,7 +110,7 @@ const Page = () => {
|
|||
<EmptyList
|
||||
title="Hive is waiting for your first target"
|
||||
description='You can create a target by clicking the "New Target" button'
|
||||
docsUrl={`${process.env.NEXT_PUBLIC_DOCS_LINK}/get-started/targets`}
|
||||
docsUrl={getDocsUrl(`/get-started/targets`)}
|
||||
/>
|
||||
) : (
|
||||
targets?.nodes.map(target => <TargetCard key={target.id} target={target} />)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactElement } from 'react';
|
||||
import React, { ReactElement } from 'react';
|
||||
import NextLink from 'next/link';
|
||||
import { onlyText } from 'react-children-utilities';
|
||||
import { useQuery } from 'urql';
|
||||
|
|
@ -9,6 +9,7 @@ import { Activities, Button, Card, DropdownMenu, EmptyList, Heading, Skeleton, T
|
|||
import { getActivity } from '@/components/v2/activities';
|
||||
import { LinkIcon, MoreIcon, SettingsIcon } from '@/components/v2/icon';
|
||||
import { ProjectActivitiesDocument, ProjectsWithTargetsDocument, ProjectsWithTargetsQuery } from '@/graphql';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
import { fixDuplicatedFragments } from '@/lib/graphql';
|
||||
import { useClipboard } from '@/lib/hooks/use-clipboard';
|
||||
import { useRouteSelector } from '@/lib/hooks/use-route-selector';
|
||||
|
|
@ -94,6 +95,7 @@ function ProjectsPage(): ReactElement {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title title="Projects" />
|
||||
|
|
@ -106,7 +108,7 @@ function ProjectsPage(): ReactElement {
|
|||
<EmptyList
|
||||
title="Hive is waiting for your first project"
|
||||
description='You can create a project by clicking the "Create Project" button'
|
||||
docsUrl={`${process.env.NEXT_PUBLIC_DOCS_LINK}/get-started/projects`}
|
||||
docsUrl={getDocsUrl(`/get-started/projects`)}
|
||||
/>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { OrganizationUsageEstimationView } from '@/components/organization/Usage
|
|||
import { Card, Heading, Tabs, Title } from '@/components/v2';
|
||||
import { OrganizationFieldsFragment, OrgBillingInfoFieldsFragment, OrgRateLimitFieldsFragment } from '@/graphql';
|
||||
import { OrganizationAccessScope, useOrganizationAccess } from '@/lib/access/organization';
|
||||
import { getIsStripeEnabled } from '@/lib/billing/stripe-public-key';
|
||||
|
||||
const DateFormatter = Intl.DateTimeFormat('en-US', {
|
||||
month: 'short',
|
||||
|
|
@ -110,6 +111,22 @@ function SubscriptionPage(): ReactElement {
|
|||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = withSessionProtection();
|
||||
export const getServerSideProps = withSessionProtection(async context => {
|
||||
/**
|
||||
* If Strive is not enabled we redirect the user to the organization.
|
||||
*/
|
||||
const isStripeEnabled = getIsStripeEnabled();
|
||||
if (isStripeEnabled === false) {
|
||||
const parts = `${context.resolvedUrl}`.split('/');
|
||||
parts.pop();
|
||||
return {
|
||||
redirect: {
|
||||
destination: `${parts.join('/')}`,
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
return { props: {} };
|
||||
});
|
||||
|
||||
export default authenticated(SubscriptionPage);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,50 @@ import 'regenerator-runtime/runtime';
|
|||
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document';
|
||||
import { extractCritical } from '@emotion/server';
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
type FrontendEnvironment = {
|
||||
APP_BASE_URL: string | undefined;
|
||||
DOCS_URL: string | undefined;
|
||||
STRIPE_PUBLIC_KEY: string | undefined;
|
||||
AUTH_GITHUB: string | undefined;
|
||||
AUTH_GOOGLE: string | undefined;
|
||||
GA_TRACKING_ID: string | undefined;
|
||||
CRISP_WEBSITE_ID: string | undefined;
|
||||
SENTRY_DSN: string | undefined;
|
||||
RELEASE: string | undefined;
|
||||
ENVIRONMENT: string | undefined;
|
||||
SENTRY_ENABLED: string | undefined;
|
||||
};
|
||||
|
||||
export default class MyDocument extends Document<{ ids: Array<string>; css: string; __ENV__: FrontendEnvironment }> {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
const page = await ctx.renderPage();
|
||||
const styles = extractCritical(page.html);
|
||||
return { ...initialProps, ...page, ...styles };
|
||||
|
||||
const __ENV__: FrontendEnvironment = {
|
||||
APP_BASE_URL: process.env['APP_BASE_URL'],
|
||||
DOCS_URL: process.env['DOCS_URL'],
|
||||
STRIPE_PUBLIC_KEY: process.env['STRIPE_PUBLIC_KEY'],
|
||||
AUTH_GITHUB: process.env['AUTH_GITHUB'],
|
||||
AUTH_GOOGLE: process.env['AUTH_GOOGLE'],
|
||||
GA_TRACKING_ID: process.env['GA_TRACKING_ID'],
|
||||
CRISP_WEBSITE_ID: process.env['CRISP_WEBSITE_ID'],
|
||||
SENTRY_DSN: process.env['SENTRY_DSN'],
|
||||
RELEASE: process.env['RELEASE'],
|
||||
ENVIRONMENT: process.env['ENVIRONMENT'],
|
||||
SENTRY_ENABLED: process.env['SENTRY_ENABLED'],
|
||||
};
|
||||
|
||||
return {
|
||||
...initialProps,
|
||||
...page,
|
||||
...styles,
|
||||
__ENV__,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { ids, css } = this.props as any;
|
||||
const { ids, css } = this.props;
|
||||
|
||||
return (
|
||||
<Html className="dark">
|
||||
|
|
@ -36,6 +70,12 @@ export default class MyDocument extends Document {
|
|||
id="force-dark-mode"
|
||||
dangerouslySetInnerHTML={{ __html: "localStorage['chakra-ui-color-mode'] = 'dark';" }}
|
||||
/>
|
||||
<script
|
||||
type="module"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `globalThis["__ENV__"] = ${JSON.stringify((this.props as any).__ENV__)}`,
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<body className="bg-transparent font-sans text-white">
|
||||
<Main />
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export default async function superTokens(req: NextApiRequest & Request, res: Ne
|
|||
// NOTE: We need CORS only if we are querying the APIs from a different origin
|
||||
await NextCors(req, res, {
|
||||
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
|
||||
origin: process.env['NEXT_PUBLIC_APP_BASE_URL'],
|
||||
origin: process.env['APP_BASE_URL'],
|
||||
credentials: true,
|
||||
allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export async function ensureGithubIntegration(
|
|||
) {
|
||||
const { orgId, installationId } = input;
|
||||
await graphql({
|
||||
url: `${process.env['NEXT_PUBLIC_APP_BASE_URL'].replace(/\/$/, '')}/api/proxy`,
|
||||
url: `${process.env['APP_BASE_URL'].replace(/\/$/, '')}/api/proxy`,
|
||||
headers: {
|
||||
...req.headers,
|
||||
'content-type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default async function githubConnectOrg(req: NextApiRequest, res: NextApi
|
|||
|
||||
const url = `https://github.com/apps/${process.env.GITHUB_APP_NAME}/installations/new`;
|
||||
|
||||
const redirectUrl = `${process.env['NEXT_PUBLIC_APP_BASE_URL'].replace(/\/$/, '')}/api/github/callback`;
|
||||
const redirectUrl = `${process.env['APP_BASE_URL'].replace(/\/$/, '')}/api/github/callback`;
|
||||
|
||||
res.redirect(`${url}?state=${orgId}&redirect_url=${redirectUrl}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default async function githubSetupCallback(req: NextApiRequest, res: Next
|
|||
cleanId: string;
|
||||
};
|
||||
}>({
|
||||
url: `${process.env['NEXT_PUBLIC_APP_BASE_URL'].replace(/\/$/, '')}/api/proxy`,
|
||||
url: `${process.env['APP_BASE_URL'].replace(/\/$/, '')}/api/proxy`,
|
||||
headers: {
|
||||
...req.headers,
|
||||
'content-type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default async function slackCallback(req: NextApiRequest, res: NextApiRes
|
|||
const token = slackResponse.access_token;
|
||||
|
||||
await graphql({
|
||||
url: `${process.env['NEXT_PUBLIC_APP_BASE_URL'].replace(/\/$/, '')}/api/proxy`,
|
||||
url: `${process.env['APP_BASE_URL'].replace(/\/$/, '')}/api/proxy`,
|
||||
headers: {
|
||||
...req.headers,
|
||||
'content-type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default async function slackConnectOrg(req: NextApiRequest, res: NextApiR
|
|||
|
||||
const slackUrl = `https://slack.com/oauth/v2/authorize?scope=incoming-webhook,chat:write,chat:write.public,commands&client_id=${process
|
||||
.env.SLACK_CLIENT_ID!}`;
|
||||
const redirectUrl = `${process.env['NEXT_PUBLIC_APP_BASE_URL'].replace(/\/$/, '')}/api/slack/callback`;
|
||||
const redirectUrl = `${process.env['APP_BASE_URL'].replace(/\/$/, '')}/api/slack/callback`;
|
||||
|
||||
res.redirect(`${slackUrl}&state=${orgId}&redirect_uri=${redirectUrl}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { init } from '@sentry/nextjs';
|
||||
|
||||
const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
|
||||
const RELEASE = process.env.RELEASE || process.env.NEXT_PUBLIC_RELEASE;
|
||||
const ENVIRONMENT = process.env.ENVIRONMENT || process.env.NEXT_PUBLIC_ENVIRONMENT;
|
||||
const SENTRY_ENABLED = process.env.SENTRY_ENABLED || process.env.NEXT_PUBLIC_SENTRY_ENABLED;
|
||||
const SENTRY_DSN = globalThis.process?.env['SENTRY_DSN'] ?? globalThis['__ENV__']?.['SENTRY_DSN'];
|
||||
const RELEASE = globalThis.process?.env['RELEASE'] ?? globalThis['__ENV__']?.['RELEASE'];
|
||||
const ENVIRONMENT = globalThis.process?.env['ENVIRONMENT'] ?? globalThis['__ENV__']?.['ENVIRONMENT'];
|
||||
const SENTRY_ENABLED = globalThis.process?.env['SENTRY_ENABLED'] ?? globalThis['__ENV__']?.['SENTRY_ENABLED'];
|
||||
|
||||
export const config: Parameters<typeof init>[0] = {
|
||||
serverName: 'app',
|
||||
enabled: String(SENTRY_ENABLED) === '1',
|
||||
environment: ENVIRONMENT || 'local',
|
||||
release: RELEASE || 'local',
|
||||
enabled: SENTRY_ENABLED === '1',
|
||||
environment: ENVIRONMENT ?? 'local',
|
||||
release: RELEASE ?? 'local',
|
||||
dsn: SENTRY_DSN,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { Logo } from './Logo';
|
|||
import { Feedback } from './Feedback';
|
||||
import { UserSettings } from './UserSettings';
|
||||
import ThemeButton from './ThemeButton';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
|
||||
export interface NavigationItem {
|
||||
label: string;
|
||||
|
|
@ -121,6 +122,8 @@ export function Navigation() {
|
|||
|
||||
const me = meQuery.data?.me;
|
||||
|
||||
const docsUrl = getDocsUrl();
|
||||
|
||||
return (
|
||||
<nav tw="bg-white shadow-md dark:bg-gray-900 z-10">
|
||||
<div tw="mx-auto px-2 sm:px-6 lg:px-8">
|
||||
|
|
@ -149,11 +152,13 @@ export function Navigation() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div tw="flex flex-row items-center space-x-4">
|
||||
<ChakraLink tw="text-sm dark:text-gray-200" href={process.env.NEXT_PUBLIC_DOCS_LINK}>
|
||||
Documentation
|
||||
</ChakraLink>
|
||||
</div>
|
||||
{docsUrl ? (
|
||||
<div tw="flex flex-row items-center space-x-4">
|
||||
<ChakraLink tw="text-sm dark:text-gray-200" href={docsUrl}>
|
||||
Documentation
|
||||
</ChakraLink>
|
||||
</div>
|
||||
) : null}
|
||||
<Divider orientation="vertical" tw="height[20px] ml-8 mr-3" />
|
||||
<div tw="inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:pr-0">
|
||||
<div tw="ml-3 relative">
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
import clsx from 'clsx';
|
||||
import { OrganizationType } from '@/graphql';
|
||||
import { gql, DocumentType } from 'urql';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
|
||||
const GetStartedWizard_GetStartedProgress = gql(/* GraphQL */ `
|
||||
fragment GetStartedWizard_GetStartedProgress on OrganizationGetStarted {
|
||||
|
|
@ -104,37 +105,25 @@ function GetStartedWizard({
|
|||
<DrawerBody>
|
||||
<p>Complete these steps to experience the full power of GraphQL Hive</p>
|
||||
<div className="mt-4 flex flex-col divide-y-2 divide-gray-900">
|
||||
<Task link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/get-started/projects`} completed={tasks.creatingProject}>
|
||||
<Task link={getDocsUrl(`/get-started/projects`)} completed={tasks.creatingProject}>
|
||||
Create a project
|
||||
</Task>
|
||||
<Task
|
||||
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/publish-schema`}
|
||||
completed={tasks.publishingSchema}
|
||||
>
|
||||
<Task link={getDocsUrl(`/features/publish-schema`)} completed={tasks.publishingSchema}>
|
||||
Publish a schema
|
||||
</Task>
|
||||
<Task
|
||||
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/checking-schema`}
|
||||
completed={tasks.checkingSchema}
|
||||
>
|
||||
<Task link={getDocsUrl(`/features/checking-schema`)} completed={tasks.checkingSchema}>
|
||||
Check a schema
|
||||
</Task>
|
||||
{'invitingMembers' in tasks && typeof tasks.invitingMembers === 'boolean' ? (
|
||||
<Task
|
||||
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/get-started/organizations#members`}
|
||||
completed={tasks.invitingMembers}
|
||||
>
|
||||
<Task link={getDocsUrl(`/get-started/organizations#members`)} completed={tasks.invitingMembers}>
|
||||
Invite members
|
||||
</Task>
|
||||
) : null}
|
||||
<Task
|
||||
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/monitoring`}
|
||||
completed={tasks.reportingOperations}
|
||||
>
|
||||
<Task link={getDocsUrl(`/features/monitoring`)} completed={tasks.reportingOperations}>
|
||||
Report operations
|
||||
</Task>
|
||||
<Task
|
||||
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/checking-schema#with-usage-enabled`}
|
||||
link={getDocsUrl(`/features/checking-schema#with-usage-enabled`)}
|
||||
completed={tasks.enablingUsageBasedBreakingChanges}
|
||||
>
|
||||
Enable usage-based breaking changes
|
||||
|
|
@ -152,11 +141,11 @@ function Task({
|
|||
link,
|
||||
}: React.PropsWithChildren<{
|
||||
completed: boolean;
|
||||
link: string;
|
||||
link: string | null;
|
||||
}>) {
|
||||
return (
|
||||
<a
|
||||
href={link}
|
||||
href={link ?? undefined}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={clsx('flex flex-row items-center gap-4 p-3 text-left', completed ? 'opacity-50' : 'hover:opacity-80')}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { useRouteSelector } from '@/lib/hooks/use-route-selector';
|
|||
import { canAccessOrganization, OrganizationAccessScope, useOrganizationAccess } from '@/lib/access/organization';
|
||||
import cookies from 'js-cookie';
|
||||
import { LAST_VISITED_ORG_KEY } from '@/constants';
|
||||
import { getIsStripeEnabled } from '@/lib/billing/stripe-public-key';
|
||||
|
||||
enum TabValue {
|
||||
Overview = 'overview',
|
||||
|
|
@ -146,7 +147,7 @@ export function OrganizationLayout({
|
|||
</Tabs.Trigger>
|
||||
</NextLink>
|
||||
)}
|
||||
{canAccessOrganization(OrganizationAccessScope.Settings, me) && (
|
||||
{getIsStripeEnabled() && canAccessOrganization(OrganizationAccessScope.Settings, me) && (
|
||||
<NextLink passHref href={`/${orgId}/${TabValue.Subscription}`}>
|
||||
<Tabs.Trigger value={TabValue.Subscription} asChild>
|
||||
<a>Subscription</a>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ReactElement } from 'react';
|
|||
import Image from 'next/image';
|
||||
|
||||
import { Card, Heading, Link } from '@/components/v2/index';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
import magnifier from '../../../public/images/figures/magnifier.svg';
|
||||
|
||||
export const EmptyList = ({
|
||||
|
|
@ -11,16 +12,18 @@ export const EmptyList = ({
|
|||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
docsUrl: string;
|
||||
docsUrl: string | null;
|
||||
}): ReactElement => {
|
||||
return (
|
||||
<Card className="flex grow flex-col items-center gap-y-2">
|
||||
<Image src={magnifier} alt="Magnifier illustration" width="200" height="200" className="drag-none" />
|
||||
<Heading>{title}</Heading>
|
||||
<span className="text-center text-sm font-medium text-gray-500">{description}</span>
|
||||
<Link variant="primary" href={docsUrl} target="_blank" rel="noreferrer" className="my-5">
|
||||
Read about it in the documentation
|
||||
</Link>
|
||||
{docsUrl === null ? null : (
|
||||
<Link variant="primary" href={docsUrl} target="_blank" rel="noreferrer" className="my-5">
|
||||
Read about it in the documentation
|
||||
</Link>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
@ -29,6 +32,6 @@ export const noSchema = (
|
|||
<EmptyList
|
||||
title="Hive is waiting for your first schema"
|
||||
description="You can publish a schema with Hive CLI and Hive Client"
|
||||
docsUrl={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/publish-schema`}
|
||||
docsUrl={getDocsUrl(`/features/publish-schema`)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
} from '@/components/v2/icon';
|
||||
import { CreateOrganizationModal } from '@/components/v2/modals';
|
||||
import { MeDocument, OrganizationsDocument, OrganizationsQuery, OrganizationType } from '@/graphql';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
import { useRouteSelector } from '@/lib/hooks/use-route-selector';
|
||||
|
||||
type DropdownOrganization = OrganizationsQuery['organizations']['nodes'];
|
||||
|
|
@ -72,6 +73,7 @@ export const Header = (): ReactElement => {
|
|||
};
|
||||
}, [isOpaque]);
|
||||
|
||||
const docsUrl = getDocsUrl();
|
||||
return (
|
||||
<header
|
||||
className={clsx(
|
||||
|
|
@ -142,12 +144,14 @@ export const Header = (): ReactElement => {
|
|||
</DropdownMenu.Item>
|
||||
</a>
|
||||
</NextLink>
|
||||
<DropdownMenu.Item asChild>
|
||||
<a href={process.env.NEXT_PUBLIC_DOCS_LINK} target="_blank" rel="noreferrer">
|
||||
<FileTextIcon className="h-5 w-5" />
|
||||
Documentation
|
||||
</a>
|
||||
</DropdownMenu.Item>
|
||||
{docsUrl ? (
|
||||
<DropdownMenu.Item asChild>
|
||||
<a href={docsUrl} target="_blank" rel="noreferrer">
|
||||
<FileTextIcon className="h-5 w-5" />
|
||||
Documentation
|
||||
</a>
|
||||
</DropdownMenu.Item>
|
||||
) : null}
|
||||
<DropdownMenu.Item asChild>
|
||||
<a href="https://status.graphql-hive.com" target="_blank" rel="noreferrer">
|
||||
<AlertTriangleIcon className="h-5 w-5" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ReactElement } from 'react';
|
||||
|
||||
import { Button, CopyValue, Heading, Link, Modal, Tag } from '@/components/v2';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
|
||||
export const ConnectLabModal = ({
|
||||
isOpen,
|
||||
|
|
@ -11,6 +12,8 @@ export const ConnectLabModal = ({
|
|||
toggleModalOpen: () => void;
|
||||
endpoint: string;
|
||||
}): ReactElement => {
|
||||
const docsUrl = getDocsUrl('/features/tokens');
|
||||
|
||||
return (
|
||||
<Modal open={isOpen} onOpenChange={toggleModalOpen} className="flex w-[650px] flex-col gap-5">
|
||||
<Heading className="text-center">Connect to Lab</Heading>
|
||||
|
|
@ -22,23 +25,13 @@ export const ConnectLabModal = ({
|
|||
<span className="text-sm text-gray-500">To authenticate, use the following HTTP headers:</span>
|
||||
<Tag>
|
||||
X-Hive-Key:{' '}
|
||||
<Link
|
||||
variant="secondary"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/tokens`}
|
||||
>
|
||||
<Link variant="secondary" target="_blank" rel="noreferrer" href={docsUrl ?? undefined}>
|
||||
YOUR_TOKEN_HERE
|
||||
</Link>
|
||||
</Tag>
|
||||
<p className="text-sm text-gray-500">
|
||||
Read the{' '}
|
||||
<Link
|
||||
variant="primary"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/tokens`}
|
||||
>
|
||||
<Link variant="primary" target="_blank" rel="noreferrer" href={docsUrl ?? undefined}>
|
||||
Managing Tokens
|
||||
</Link>{' '}
|
||||
chapter in our documentation.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useMutation, useQuery } from 'urql';
|
|||
|
||||
import { Button, CopyValue, Heading, Link, Modal, Tag } from '@/components/v2';
|
||||
import { CreateCdnTokenDocument, ProjectDocument, ProjectType } from '@/graphql';
|
||||
import { getDocsUrl } from '@/lib/docs-url';
|
||||
import { useRouteSelector } from '@/lib/hooks/use-route-selector';
|
||||
|
||||
const taxonomy = {
|
||||
|
|
@ -83,7 +84,7 @@ export const ConnectSchemaModal = ({
|
|||
variant="primary"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/registry-usage#apollo-federation`}
|
||||
href={getDocsUrl(`/features/registry-usage#apollo-federation`) ?? undefined}
|
||||
>
|
||||
Using the Registry with a Apollo Gateway
|
||||
</Link>{' '}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
export const appInfo = {
|
||||
// learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo
|
||||
appName: 'GraphQL Hive',
|
||||
apiDomain: process.env['NEXT_PUBLIC_APP_BASE_URL'],
|
||||
websiteDomain: process.env['NEXT_PUBLIC_APP_BASE_URL'],
|
||||
apiBasePath: '/api/auth',
|
||||
websiteBasePath: '/auth',
|
||||
function throwException(msg: string) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
export const appInfo = () => {
|
||||
const appBaseUrl =
|
||||
globalThis.process?.env?.['APP_BASE_URL'] ??
|
||||
globalThis?.['__ENV__']?.['APP_BASE_URL'] ??
|
||||
throwException('APP_BASE_URL is not defined');
|
||||
|
||||
return {
|
||||
// learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo
|
||||
appName: 'GraphQL Hive',
|
||||
apiDomain: appBaseUrl,
|
||||
websiteDomain: appBaseUrl,
|
||||
apiBasePath: '/api/auth',
|
||||
websiteBasePath: '/auth',
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ type LegacyAuth0ConfigEnabled = zod.TypeOf<typeof LegacyAuth0ConfigEnabledModel>
|
|||
|
||||
const GitHubConfigModel = zod.union([
|
||||
zod.object({
|
||||
NEXT_PUBLIC_AUTH_GITHUB: zod.union([zod.void(), zod.literal('0')]),
|
||||
AUTH_GITHUB: zod.union([zod.void(), zod.literal('0')]),
|
||||
}),
|
||||
zod.object({
|
||||
NEXT_PUBLIC_AUTH_GITHUB: zod.literal('1'),
|
||||
AUTH_GITHUB: zod.literal('1'),
|
||||
AUTH_GITHUB_CLIENT_ID: zod.string(),
|
||||
AUTH_GITHUB_CLIENT_SECRET: zod.string(),
|
||||
}),
|
||||
|
|
@ -46,10 +46,10 @@ const GitHubConfigModel = zod.union([
|
|||
|
||||
const GoogleConfigModel = zod.union([
|
||||
zod.object({
|
||||
NEXT_PUBLIC_AUTH_GOOGLE: zod.union([zod.void(), zod.literal('0')]),
|
||||
AUTH_GOOGLE: zod.union([zod.void(), zod.literal('0')]),
|
||||
}),
|
||||
zod.object({
|
||||
NEXT_PUBLIC_AUTH_GOOGLE: zod.literal('1'),
|
||||
AUTH_GOOGLE: zod.literal('1'),
|
||||
AUTH_GOOGLE_CLIENT_ID: zod.string(),
|
||||
AUTH_GOOGLE_CLIENT_SECRET: zod.string(),
|
||||
}),
|
||||
|
|
@ -72,7 +72,7 @@ export const backendConfig = (): TypeInput => {
|
|||
});
|
||||
const providers: Array<TypeProvider> = [];
|
||||
|
||||
if (githubConfig['NEXT_PUBLIC_AUTH_GITHUB'] === '1') {
|
||||
if (githubConfig['AUTH_GITHUB'] === '1') {
|
||||
providers.push(
|
||||
ThirdPartyEmailPasswordNode.Github({
|
||||
clientId: githubConfig['AUTH_GITHUB_CLIENT_ID'],
|
||||
|
|
@ -80,7 +80,7 @@ export const backendConfig = (): TypeInput => {
|
|||
})
|
||||
);
|
||||
}
|
||||
if (googleConfig['NEXT_PUBLIC_AUTH_GOOGLE'] === '1') {
|
||||
if (googleConfig['AUTH_GOOGLE'] === '1') {
|
||||
providers.push(
|
||||
ThirdPartyEmailPasswordNode.Google({
|
||||
clientId: googleConfig['AUTH_GOOGLE_CLIENT_ID'],
|
||||
|
|
@ -94,7 +94,7 @@ export const backendConfig = (): TypeInput => {
|
|||
connectionURI: superTokensConfig['SUPERTOKENS_CONNECTION_URI'],
|
||||
apiKey: superTokensConfig['SUPERTOKENS_API_KEY'],
|
||||
},
|
||||
appInfo,
|
||||
appInfo: appInfo(),
|
||||
recipeList: [
|
||||
ThirdPartyEmailPasswordNode.init({
|
||||
providers,
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import { appInfo } from './app-info';
|
|||
export const frontendConfig = () => {
|
||||
const providers: Array<Provider> = [];
|
||||
|
||||
if (process.env['NEXT_PUBLIC_AUTH_GITHUB'] === '1') {
|
||||
if (globalThis['__ENV__']?.['AUTH_GITHUB'] === '1') {
|
||||
providers.push(ThirdPartyEmailPasswordReact.Github.init());
|
||||
}
|
||||
if (process.env['NEXT_PUBLIC_AUTH_GOOGLE'] === '1') {
|
||||
if (globalThis['__ENV__']?.['AUTH_GOOGLE'] === '1') {
|
||||
providers.push(ThirdPartyEmailPasswordReact.Google.init());
|
||||
}
|
||||
|
||||
return {
|
||||
appInfo,
|
||||
appInfo: appInfo(),
|
||||
recipeList: [
|
||||
ThirdPartyEmailPasswordReact.init({
|
||||
signInAndUpFeature: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export const LAST_VISITED_ORG_KEY = 'lastVisitedOrganization';
|
||||
|
||||
export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID;
|
||||
export const GA_TRACKING_ID = globalThis.process?.env['GA_TRACKING_ID'] ?? globalThis['__ENV__']?.['GA_TRACKING_ID'];
|
||||
|
||||
export const CRISP_WEBSITE_ID = process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID;
|
||||
export const CRISP_WEBSITE_ID =
|
||||
globalThis.process?.env['CRISP_WEBSITE_ID'] ?? globalThis['__ENV__']?.['CRISP_WEBSITE_ID'];
|
||||
|
|
|
|||
9
packages/web/app/src/lib/billing/stripe-public-key.ts
Normal file
9
packages/web/app/src/lib/billing/stripe-public-key.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export const getStripePublicKey = () => {
|
||||
const stripePublicUrl = globalThis.process?.env['STRIPE_PUBLIC_KEY'] ?? globalThis['__ENV__']?.['STRIPE_PUBLIC_KEY'];
|
||||
if (!stripePublicUrl) {
|
||||
return null;
|
||||
}
|
||||
return stripePublicUrl;
|
||||
};
|
||||
|
||||
export const getIsStripeEnabled = () => !!getStripePublicKey();
|
||||
|
|
@ -1,15 +1,20 @@
|
|||
import { Elements as ElementsProvider } from '@stripe/react-stripe-js';
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
import React from 'react';
|
||||
|
||||
const STRIPE_PUBLIC_KEY = process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || null;
|
||||
|
||||
const stripePromise$ = !STRIPE_PUBLIC_KEY ? null : loadStripe(STRIPE_PUBLIC_KEY);
|
||||
import { getStripePublicKey } from './stripe-public-key';
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
|
||||
export const HiveStripeWrapper: React.FC<{}> = ({ children }) => {
|
||||
if (STRIPE_PUBLIC_KEY === null || stripePromise$ === null) {
|
||||
const [stripe] = React.useState(() => {
|
||||
const stripePublicKey = getStripePublicKey();
|
||||
return stripePublicKey ? loadStripe(stripePublicKey) : null;
|
||||
});
|
||||
|
||||
if (stripe === null) {
|
||||
return children as any;
|
||||
}
|
||||
|
||||
return <ElementsProvider stripe={stripePromise$}>{children}</ElementsProvider>;
|
||||
return (
|
||||
<React.Suspense fallback={() => <>{children}</>}>
|
||||
<ElementsProvider stripe={stripe}>{children}</ElementsProvider>
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
7
packages/web/app/src/lib/docs-url.ts
Normal file
7
packages/web/app/src/lib/docs-url.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export const getDocsUrl = (path = '') => {
|
||||
const docsUrl = globalThis.process?.env['DOCS_URL'] ?? globalThis['__ENV__']?.['DOCS_URL'];
|
||||
if (!docsUrl) {
|
||||
return null;
|
||||
}
|
||||
return `${docsUrl}${path}`;
|
||||
};
|
||||
|
|
@ -18,13 +18,7 @@
|
|||
"outputs": ["dist/**"]
|
||||
},
|
||||
"@hive/app#build": {
|
||||
"dependsOn": [
|
||||
"^build",
|
||||
"$NEXT_PUBLIC_DOCS_LINK",
|
||||
"$NEXT_PUBLIC_CRISP_WEBSITE_ID",
|
||||
"$NEXT_PUBLIC_GA_TRACKING_ID",
|
||||
"$NEXT_PUBLIC_STRIPE_PUBLIC_KEY"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"@hive/docs#build": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue