Compare commits
60 Commits
@nhost/nex
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3601de3f85 | ||
|
|
ac9404610b | ||
|
|
63570db57c | ||
|
|
538ed78f5a | ||
|
|
b1a31ecb00 | ||
|
|
bac8ace434 | ||
|
|
a402fc17de | ||
|
|
de0a125e98 | ||
|
|
ea1ad29031 | ||
|
|
3da40e5712 | ||
|
|
b9087a4add | ||
|
|
1b7a6d0252 | ||
|
|
1417d3e794 | ||
|
|
e187923858 | ||
|
|
8a60ed4074 | ||
|
|
d7d11a44a7 | ||
|
|
062e4691cd | ||
|
|
a95d49fa2c | ||
|
|
d14fc96899 | ||
|
|
93db718254 | ||
|
|
c367bd58b9 | ||
|
|
0bfed4d9e1 | ||
|
|
1f3aecd379 | ||
|
|
42306ea3bb | ||
|
|
1b12a175f6 | ||
|
|
32060aaea0 | ||
|
|
f94cace5f2 | ||
|
|
5de965d9a5 | ||
|
|
e10b3adc11 | ||
|
|
457db76b06 | ||
|
|
1e952a026e | ||
|
|
2f4c040789 | ||
|
|
74648752b4 | ||
|
|
09d218a3fe | ||
|
|
2e8938dbb0 | ||
|
|
ec60d03536 | ||
|
|
2f3767552f | ||
|
|
bc401c0dd2 | ||
|
|
2145243b19 | ||
|
|
ca012d790c | ||
|
|
aeda14ef53 | ||
|
|
3fa5e2005a | ||
|
|
beadd84adb | ||
|
|
f8f55d2b99 | ||
|
|
03a98d4f3a | ||
|
|
8ed8e04ab6 | ||
|
|
587efd4551 | ||
|
|
a48dd5bf74 | ||
|
|
ef53df5cb3 | ||
|
|
7055ffc37a | ||
|
|
c68ce6d480 | ||
|
|
98a149c8bf | ||
|
|
ceb558975e | ||
|
|
7a87321a7e | ||
|
|
9349766c0a | ||
|
|
31655191a3 | ||
|
|
e3b91efa84 | ||
|
|
cfe736776a | ||
|
|
481bf237cc | ||
|
|
33ce9bf1b9 |
4
.github/workflows/changesets.yaml
vendored
4
.github/workflows/changesets.yaml
vendored
@@ -18,6 +18,7 @@ env:
|
||||
|
||||
jobs:
|
||||
version:
|
||||
name: Version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
hasChangesets: ${{ steps.changesets.outputs.hasChangesets }}
|
||||
@@ -61,6 +62,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test
|
||||
@@ -110,6 +112,8 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
push: true
|
||||
- name: Trigger a Vercel deployment
|
||||
run: curl -X POST -d {} https://api.vercel.com/v1/integrations/deploy/${{ secrets.DASHBOARD_VERCEL_PROJECT_ID }}/${{ secrets.DASHBOARD_VERCEL_WEBHOOK_ID }}
|
||||
- name: Create GitHub Release
|
||||
uses: taiki-e/create-gh-release-action@v1
|
||||
with:
|
||||
|
||||
37
README.md
37
README.md
@@ -179,14 +179,21 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Grégory D'Angelo</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ejkkan">
|
||||
<img src="https://avatars.githubusercontent.com/u/32518962?v=4" width="100;" alt="ejkkan"/>
|
||||
<br />
|
||||
<sub><b>Erik Magnusson</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/guicurcio">
|
||||
<img src="https://avatars.githubusercontent.com/u/20285232?v=4" width="100;" alt="guicurcio"/>
|
||||
<br />
|
||||
<sub><b>Guido Curcio</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/subatuba21">
|
||||
<img src="https://avatars.githubusercontent.com/u/34824571?v=4" width="100;" alt="subatuba21"/>
|
||||
@@ -221,15 +228,15 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Christopher Möller</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/GavanWilhite">
|
||||
<img src="https://avatars.githubusercontent.com/u/2085119?v=4" width="100;" alt="GavanWilhite"/>
|
||||
<br />
|
||||
<sub><b>Gavan Wilhite</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FuzzyReason">
|
||||
<img src="https://avatars.githubusercontent.com/u/62517920?v=4" width="100;" alt="FuzzyReason"/>
|
||||
@@ -237,13 +244,6 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Vadim Smirnov</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ejkkan">
|
||||
<img src="https://avatars.githubusercontent.com/u/32518962?v=4" width="100;" alt="ejkkan"/>
|
||||
<br />
|
||||
<sub><b>Erik Magnusson</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/macmac49">
|
||||
<img src="https://avatars.githubusercontent.com/u/831190?v=4" width="100;" alt="macmac49"/>
|
||||
@@ -495,6 +495,13 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Quentin Decré</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/elephant3">
|
||||
<img src="https://avatars.githubusercontent.com/u/48279149?v=4" width="100;" alt="elephant3"/>
|
||||
<br />
|
||||
<sub><b>Siarhei Lipchyk</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/altschuler">
|
||||
<img src="https://avatars.githubusercontent.com/u/956928?v=4" width="100;" alt="altschuler"/>
|
||||
@@ -522,15 +529,15 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Vadim</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/TheRedLancer">
|
||||
<img src="https://avatars.githubusercontent.com/u/58493767?v=4" width="100;" alt="TheRedLancer"/>
|
||||
<br />
|
||||
<sub><b>Zach Burnaby</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/komninoschat">
|
||||
<img src="https://avatars.githubusercontent.com/u/29049104?v=4" width="100;" alt="komninoschat"/>
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- a48dd5bf: feat(dashboard): make backend port configurable
|
||||
|
||||
## 0.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5de965d9: fix(dashboard): alphabetic ordering of providers
|
||||
- b9087a4a: fix(dashboard): console -> dashboard terminology
|
||||
- ca012d79: docs(workos): WorkOS Docs
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
FROM node:16-alpine AS pruner
|
||||
RUN apk add --no-cache libc6-compat
|
||||
RUN apk update
|
||||
@@ -17,10 +16,13 @@ RUN apk update
|
||||
WORKDIR /app
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
ENV NEXT_PUBLIC_NHOST_PLATFORM false
|
||||
ENV NEXT_PUBLIC_NHOST_MIGRATIONS_URL http://localhost:9693
|
||||
ENV NEXT_PUBLIC_NHOST_HASURA_URL http://localhost:9695
|
||||
ENV NEXT_PUBLIC_ENV dev
|
||||
ENV NEXT_PUBLIC_NHOST_PLATFORM false
|
||||
|
||||
# placeholders for ports, will be replaced on runtime by entrypoint script
|
||||
ENV NEXT_PUBLIC_NHOST_MIGRATIONS_PORT __NEXT_PUBLIC_NHOST_MIGRATIONS_PORT__
|
||||
ENV NEXT_PUBLIC_NHOST_HASURA_PORT __NEXT_PUBLIC_NHOST_HASURA_PORT__
|
||||
ENV NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT __NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT__
|
||||
|
||||
RUN yarn global add pnpm@7.17.0
|
||||
COPY .gitignore .gitignore
|
||||
@@ -40,11 +42,14 @@ RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
USER nextjs
|
||||
|
||||
COPY --from=builder /app/dashboard/next.config.js .
|
||||
COPY --from=builder /app/dashboard/package.json .
|
||||
COPY --from=builder /app/dashboard/public ./dashboard/public
|
||||
COPY --chown=nextjs:nodejs dashboard/docker-entrypoint.sh .
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/dashboard/next.config.js .
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/dashboard/package.json .
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/dashboard/public ./dashboard/public
|
||||
|
||||
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/dashboard/.next/standalone/app ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/dashboard/.next/static ./dashboard/.next/static
|
||||
|
||||
CMD node dashboard/server.js
|
||||
ENTRYPOINT ["./docker-entrypoint.sh"]
|
||||
CMD ["node", "dashboard/server.js"]
|
||||
|
||||
@@ -30,31 +30,27 @@ First, you need to run the following command to start your backend locally:
|
||||
cd <your_nhost_project> && nhost dev
|
||||
```
|
||||
|
||||
Two environment variables are required to connect the Nhost Dashboard to your local backend:
|
||||
You can connect the Nhost Dashboard to your locally running backend by setting the following environment variables in `.env.development.local`:
|
||||
|
||||
- `NEXT_PUBLIC_NHOST_PLATFORM` should be set to `false`, because otherwise the Nhost Dashboard will try to connect to the Nhost platform.
|
||||
- `NEXT_PUBLIC_NHOST_MIGRATIONS_URL` should be set to `http://localhost:9693` unless Hasura is configured to run on a different port. This is the URL of Hasura's migrations endpoint.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
```bash
|
||||
NEXT_PUBLIC_ENV=dev
|
||||
NEXT_PUBLIC_NHOST_PLATFORM=false
|
||||
NEXT_PUBLIC_NHOST_MIGRATIONS_URL=http://localhost:9693
|
||||
```
|
||||
|
||||
### Full list of environment variables
|
||||
|
||||
| Name | Description |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------ |
|
||||
| `NEXT_PUBLIC_NHOST_PLATFORM` | This should be set to `false` to connect the Nhost Dashboard to a locally running Nhost backend. |
|
||||
| `NEXT_PUBLIC_NHOST_MIGRATIONS_URL` | URL of Hasura's migrations endpoint. Used only if local development is enabled. |
|
||||
| `NEXT_PUBLIC_NHOST_HASURA_URL` | URL of the Hasura Console. Used only when `NEXT_PUBLIC_ENV` is `dev`. |
|
||||
| `NEXT_PUBLIC_ENV` | `dev`, `staging` or `prod`. Should be set to `dev` in most cases. |
|
||||
| `NEXT_PUBLIC_NHOST_BACKEND_URL` | Backend URL. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_STRIPE_PK` | Stripe public key. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_GITHUB_APP_INSTALL_URL` | URL of the GitHub application. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_ANALYTICS_WRITE_KEY` | Analytics key. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_NHOST_BRAGI_WEBSOCKET` | URL of the Bragi websocket. Not necessary for local development. |
|
||||
| Name | Description |
|
||||
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `NEXT_PUBLIC_ENV` | `dev`, `staging` or `prod`. Should be set to `dev` in most cases. |
|
||||
| `NEXT_PUBLIC_NHOST_PLATFORM` | This should be set to `false` to connect the Nhost Dashboard to a locally running Nhost backend. Setting this to `true` turns off local development. |
|
||||
| `NEXT_PUBLIC_NHOST_LOCAL_MIGRATIONS_PORT` | Custom port that was passed to the CLI. Used only if local development is enabled. Default: `9693` |
|
||||
| `NEXT_PUBLIC_NHOST_LOCAL_HASURA_PORT` | Custom port that was passed to the CLI. Used only if local development is enabled and `NEXT_PUBLIC_ENV` is `dev`. Default: `9695` |
|
||||
| `NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT` | Custom port that was passed to the CLI. Used only if local development is enabled. Default: `1337` |
|
||||
| `NEXT_PUBLIC_NHOST_BACKEND_URL` | Backend URL. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_STRIPE_PK` | Stripe public key. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_GITHUB_APP_INSTALL_URL` | URL of the GitHub application. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_ANALYTICS_WRITE_KEY` | Analytics key. Not necessary for local development. |
|
||||
| `NEXT_PUBLIC_NHOST_BRAGI_WEBSOCKET` | URL of the Bragi websocket. Not necessary for local development. |
|
||||
|
||||
## ESLint Rules
|
||||
|
||||
@@ -67,6 +63,7 @@ NEXT_PUBLIC_NHOST_MIGRATIONS_URL=http://localhost:9693
|
||||
| `import/extensions` | JS / TS files should be imported without file extensions. |
|
||||
| `react/jsx-filename-extension` | JSX should only appear in `.jsx` and `.tsx` files. |
|
||||
| `react/jsx-no-bind` | Further investigation must be made on the performance impact of functions directly passed as props to components. |
|
||||
| `import/order` | Until we have a better auto-formatter, we disable this rule. |
|
||||
| `import/no-extraneous-dependencies` | `devDependencies` should be excluded from the list of disallowed imports. |
|
||||
| `curly` | By default it only enforces curly braces for multi-line blocks, but it should be enforced for single-line blocks as well. |
|
||||
| `no-restricted-exports` | `export { default } from './module'` is used heavily in `@/ui/v2` which is a restricted export by default. |
|
||||
|
||||
15
dashboard/docker-entrypoint.sh
Executable file
15
dashboard/docker-entrypoint.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# read ports from env variables or use defaults
|
||||
NEXT_PUBLIC_NHOST_MIGRATIONS_PORT="${NEXT_PUBLIC_NHOST_MIGRATIONS_PORT:=9693}"
|
||||
NEXT_PUBLIC_NHOST_HASURA_PORT="${NEXT_PUBLIC_NHOST_HASURA_PORT:=9695}"
|
||||
NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT="${NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT:=1337}"
|
||||
|
||||
# replace placeholders
|
||||
find dashboard -type f -exec sed -i "s/__NEXT_PUBLIC_NHOST_MIGRATIONS_PORT__/${NEXT_PUBLIC_NHOST_MIGRATIONS_PORT}/g" {} +
|
||||
find dashboard -type f -exec sed -i "s/__NEXT_PUBLIC_NHOST_HASURA_PORT__/${NEXT_PUBLIC_NHOST_HASURA_PORT}/g" {} +
|
||||
find dashboard -type f -exec sed -i "s/__NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT__/${NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT}/g" {} +
|
||||
|
||||
exec "$@"
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
1
dashboard/public/assets/twilio.svg
Normal file
1
dashboard/public/assets/twilio.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="21" height="21" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.08 0c5.578 0 10.08 4.507 10.08 10.09 0 5.584-4.502 10.09-10.08 10.09A10.072 10.072 0 0 1 0 10.09C0 4.507 4.503 0 10.08 0Zm0 2.69a7.375 7.375 0 0 0-7.392 7.4c0 4.104 3.293 7.4 7.392 7.4 4.1 0 7.392-3.296 7.392-7.4 0-4.103-3.293-7.4-7.392-7.4Zm-2.486 7.804c1.142 0 2.083.942 2.083 2.085 0 1.144-.94 2.086-2.083 2.086a2.095 2.095 0 0 1-2.083-2.086c0-1.143.94-2.085 2.083-2.085Zm4.973 0c1.142 0 2.083.942 2.083 2.085 0 1.144-.94 2.086-2.083 2.086a2.095 2.095 0 0 1-2.084-2.086c0-1.143.941-2.085 2.084-2.085Zm0-4.978c1.142 0 2.083.942 2.083 2.085 0 1.144-.94 2.086-2.083 2.086A2.095 2.095 0 0 1 10.483 7.6c0-1.143.941-2.085 2.084-2.085Zm-4.973 0c1.142 0 2.083.942 2.083 2.085 0 1.144-.94 2.086-2.083 2.086A2.095 2.095 0 0 1 5.51 7.6c0-1.143.94-2.085 2.083-2.085Z" fill="#F22F46"/></svg>
|
||||
|
After Width: | Height: | Size: 869 B |
@@ -5,6 +5,7 @@ import Button from '@/ui/v2/Button';
|
||||
import ArrowSquareOutIcon from '@/ui/v2/icons/ArrowSquareOutIcon';
|
||||
import Link from '@/ui/v2/Link';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { LOCAL_HASURA_URL } from '@/utils/env';
|
||||
import { generateRemoteAppUrl } from '@/utils/helpers';
|
||||
import Image from 'next/image';
|
||||
|
||||
@@ -24,7 +25,7 @@ export function HasuraData({ close }: HasuraDataProps) {
|
||||
|
||||
const hasuraUrl =
|
||||
process.env.NEXT_PUBLIC_ENV === 'dev'
|
||||
? process.env.NEXT_PUBLIC_NHOST_HASURA_URL || 'http://localhost:9695'
|
||||
? LOCAL_HASURA_URL
|
||||
: generateRemoteAppUrl(currentApplication.subdomain);
|
||||
|
||||
return (
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function AnonymousSignInSettings() {
|
||||
<Form onSubmit={handlePasswordProtectionSettingsChange}>
|
||||
<SettingsContainer
|
||||
title="Anonymous Users"
|
||||
description="Allow users to sign-in anonymously."
|
||||
description="Allow users to sign in anonymously."
|
||||
primaryActionButtonProps={{
|
||||
disabled:
|
||||
form.formState.isSubmitting ||
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function AppleProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Apple"
|
||||
description="Allows users to sign in with Apple."
|
||||
description="Allow users to sign in with Apple."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function DiscordProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Discord"
|
||||
description="Allows users to sign in with Discord."
|
||||
description="Allow users to sign in with Discord."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface EmailAndPasswordFormValues {
|
||||
authPasswordHibpEnabled: boolean;
|
||||
}
|
||||
|
||||
export default function EmailSettings() {
|
||||
export default function EmailAndPasswordSettings() {
|
||||
const { currentApplication } = useCurrentWorkspaceAndApplication();
|
||||
const [updateApp] = useUpdateAppMutation({
|
||||
refetchQueries: [GetAppLoginDataDocument],
|
||||
@@ -61,7 +61,7 @@ export default function EmailSettings() {
|
||||
|
||||
const { formState } = form;
|
||||
|
||||
const handleEmailSettingsChange = async (
|
||||
const handleEmailAndPasswordSettingsChange = async (
|
||||
values: EmailAndPasswordFormValues,
|
||||
) => {
|
||||
const updateAppMutation = updateApp({
|
||||
@@ -90,10 +90,10 @@ export default function EmailSettings() {
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<Form onSubmit={handleEmailSettingsChange}>
|
||||
<Form onSubmit={handleEmailAndPasswordSettingsChange}>
|
||||
<SettingsContainer
|
||||
title="Email and Password"
|
||||
description="Sign in users using email and password."
|
||||
description="Allow users to sign in with email and password."
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-email-and-password"
|
||||
docsTitle="how to sign in users with email and password"
|
||||
className="grid grid-flow-row"
|
||||
@@ -109,10 +109,8 @@ export default function EmailSettings() {
|
||||
name="authEmailSigninEmailVerifiedRequired"
|
||||
id="authEmailSigninEmailVerifiedRequired"
|
||||
label={
|
||||
<span className="inline-grid grid-flow-row gap-y-[2px] text-[15px]">
|
||||
<span className="text-[15px] font-medium">
|
||||
Require Verified Emails
|
||||
</span>
|
||||
<span className="inline-grid grid-flow-row gap-y-0.5 text-sm+">
|
||||
<span className="font-medium">Require Verified Emails</span>
|
||||
<span className="font-normal text-greyscaleMedium">
|
||||
Users must verify their email to be able to sign in.
|
||||
</span>
|
||||
@@ -124,11 +122,9 @@ export default function EmailSettings() {
|
||||
name="authPasswordHibpEnabled"
|
||||
id="authPasswordHibpEnabled"
|
||||
label={
|
||||
<span className="inline-grid grid-flow-row gap-y-[2px] text-[15px]">
|
||||
<span className="text-[15px] font-medium">
|
||||
Password Protection
|
||||
</span>
|
||||
<span className="text-[12px] font-normal text-greyscaleMedium">
|
||||
<span className="inline-grid grid-flow-row gap-y-0.5 text-sm+">
|
||||
<span className="font-medium">Password Protection</span>
|
||||
<span className="font-normal text-greyscaleMedium">
|
||||
Passwords must pass haveibeenpwned.com during sign-up.
|
||||
</span>
|
||||
</span>
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './EmailAndPasswordSettings';
|
||||
export { default } from './EmailAndPasswordSettings';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './EmailSettings';
|
||||
export { default } from './EmailSettings';
|
||||
@@ -88,7 +88,7 @@ export default function FacebookProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Facebook"
|
||||
description="Allows users to sign in with Facebook."
|
||||
description="Allow users to sign in with Facebook."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function GitHubProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="GitHub"
|
||||
description="Allows users to sign in with GitHub."
|
||||
description="Allow users to sign in with GitHub."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function GoogleProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Google"
|
||||
description="Allows users to sign in with Google."
|
||||
description="Allow users to sign in with Google."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function LinkedInProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="LinkedIn"
|
||||
description="Allows users to sign in with LinkedIn"
|
||||
description="Allow users to sign in with LinkedIn."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -80,7 +80,7 @@ export default function MagicLinkSettings() {
|
||||
<Form onSubmit={handleMagicLinkSettingsUpdate}>
|
||||
<SettingsContainer
|
||||
title="Magic Link"
|
||||
description="Allow users to sign-in with a magic link."
|
||||
description="Allow users to sign in with a magic link."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function SpotifyProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Spotify"
|
||||
description="Allows users to sign in with Spotify."
|
||||
description="Allow users to sign in with Spotify."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function TwitchProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Twitch"
|
||||
description="Allows users to sign in with Twitch."
|
||||
description="Allow users to sign in with Twitch."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function TwitterProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Twitter"
|
||||
description="Allows users to sign in with Twitter."
|
||||
description="Allow users to sign in with Twitter."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function WebAuthnSettings() {
|
||||
<Form onSubmit={handleWebAuthnSettingsUpdate}>
|
||||
<SettingsContainer
|
||||
title="Security Keys"
|
||||
description="Allow users to sign-in with security keys using WebAuthn."
|
||||
description="Allow users to sign in with security keys using WebAuthn."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function WindowsLiveProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="Windows Live"
|
||||
description="Allows users to sign in with Windows Live."
|
||||
description="Allow users to sign in with Windows Live."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
|
||||
@@ -94,11 +94,13 @@ export default function WorkOsProviderSettings() {
|
||||
<Form onSubmit={handleProviderUpdate}>
|
||||
<SettingsContainer
|
||||
title="WorkOS"
|
||||
description="Allows users to sign in with WorkOS."
|
||||
description="Allow users to sign in with WorkOS."
|
||||
primaryActionButtonProps={{
|
||||
disabled: !formState.isValid || !formState.isDirty,
|
||||
loading: formState.isSubmitting,
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-workos"
|
||||
docsTitle="how to sign in users with WorkOS"
|
||||
icon="/logos/WorkOs.svg"
|
||||
switchId="authWorkOsEnabled"
|
||||
showSwitch
|
||||
@@ -112,8 +114,8 @@ export default function WorkOsProviderSettings() {
|
||||
{...register(`authWorkOsClientId`)}
|
||||
name="authWorkOsClientId"
|
||||
id="authWorkOsClientId"
|
||||
label="WorkOS Client ID"
|
||||
placeholder="WorkOS Client ID"
|
||||
label="Client ID"
|
||||
placeholder="Enter your Client ID"
|
||||
className="col-span-3"
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
@@ -122,28 +124,28 @@ export default function WorkOsProviderSettings() {
|
||||
{...register('authWorkOsClientSecret')}
|
||||
name="authWorkOsClientSecret"
|
||||
id="authWorkOsClientSecret"
|
||||
label="WorkOS Client Secret"
|
||||
placeholder="WorkOS Client Secret"
|
||||
label="Client Secret"
|
||||
placeholder="Enter your Client Secret"
|
||||
className="col-span-3"
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
/>
|
||||
<Input
|
||||
{...register('authWorkOsDefaultDomain')}
|
||||
name="authWorkOsDefaultDomain"
|
||||
id="authWorkOsDefaultDomain"
|
||||
label="Default Domain"
|
||||
placeholder="Default Domain"
|
||||
className="col-span-2"
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
/>
|
||||
<Input
|
||||
{...register('authWorkOsDefaultOrganization')}
|
||||
name="authWorkOsDefaultOrganization"
|
||||
id="authWorkOsDefaultOrganization"
|
||||
label="Default Organization"
|
||||
placeholder="Default Organization"
|
||||
label="Default Organization ID (optional)"
|
||||
placeholder="Default Organization ID"
|
||||
className="col-span-2"
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
/>
|
||||
<Input
|
||||
{...register('authWorkOsDefaultDomain')}
|
||||
name="authWorkOsDefaultDomain"
|
||||
id="authWorkOsDefaultDomain"
|
||||
label="Default Domain (optional)"
|
||||
placeholder="Default Domain"
|
||||
className="col-span-2"
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
@@ -152,7 +154,7 @@ export default function WorkOsProviderSettings() {
|
||||
{...register('authWorkOsDefaultConnection')}
|
||||
name="authWorkOsDefaultConnection"
|
||||
id="authWorkOsDefaultConnection"
|
||||
label="Default Connection"
|
||||
label="Default Connection (optional)"
|
||||
placeholder="Default Connection"
|
||||
className="col-span-2"
|
||||
fullWidth
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
} from '@/types/data-browser';
|
||||
import { getPreparedHasuraQuery } from '@/utils/dataBrowser/hasuraQueryHelpers';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
import prepareCreateColumnQuery from './prepareCreateColumnQuery';
|
||||
|
||||
export interface CreateColumnMigrationVariables {
|
||||
@@ -33,30 +34,27 @@ export default async function createColumnMigration({
|
||||
column,
|
||||
});
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${table}_add_column_${column.name}`,
|
||||
down: [
|
||||
getPreparedHasuraQuery(
|
||||
dataSource,
|
||||
'ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I',
|
||||
schema,
|
||||
table,
|
||||
column.name,
|
||||
),
|
||||
],
|
||||
up: args,
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${table}_add_column_${column.name}`,
|
||||
down: [
|
||||
getPreparedHasuraQuery(
|
||||
dataSource,
|
||||
'ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I',
|
||||
schema,
|
||||
table,
|
||||
column.name,
|
||||
),
|
||||
],
|
||||
up: args,
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
} from '@/types/data-browser';
|
||||
import { getPreparedHasuraQuery } from '@/utils/dataBrowser/hasuraQueryHelpers';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
import prepareCreateTableQuery from './prepareCreateTableQuery';
|
||||
|
||||
export interface CreateTableMigrationVariables {
|
||||
@@ -27,29 +28,26 @@ export default async function createTableMigration({
|
||||
}: CreateTableMigrationOptions & CreateTableMigrationVariables) {
|
||||
const args = prepareCreateTableQuery({ dataSource, schema, table });
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `create_table_${schema}_${table.name}`,
|
||||
down: [
|
||||
getPreparedHasuraQuery(
|
||||
dataSource,
|
||||
'DROP TABLE IF EXISTS %I.%I',
|
||||
schema,
|
||||
table.name,
|
||||
),
|
||||
],
|
||||
up: args,
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `create_table_${schema}_${table.name}`,
|
||||
down: [
|
||||
getPreparedHasuraQuery(
|
||||
dataSource,
|
||||
'DROP TABLE IF EXISTS %I.%I',
|
||||
schema,
|
||||
table.name,
|
||||
),
|
||||
],
|
||||
up: args,
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
} from '@/types/data-browser';
|
||||
import { getPreparedHasuraQuery } from '@/utils/dataBrowser/hasuraQueryHelpers';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
|
||||
export interface DeleteColumnMigrationVariables {
|
||||
/**
|
||||
@@ -45,30 +46,27 @@ export default async function deleteColumnMigration({
|
||||
},
|
||||
});
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${table}_drop_column_${column.id}`,
|
||||
down: recreateColumnArgs,
|
||||
up: [
|
||||
getPreparedHasuraQuery(
|
||||
dataSource,
|
||||
'ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I CASCADE',
|
||||
schema,
|
||||
table,
|
||||
column.id,
|
||||
),
|
||||
],
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${table}_drop_column_${column.id}`,
|
||||
down: recreateColumnArgs,
|
||||
up: [
|
||||
getPreparedHasuraQuery(
|
||||
dataSource,
|
||||
'ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I CASCADE',
|
||||
schema,
|
||||
table,
|
||||
column.id,
|
||||
),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getPreparedHasuraQuery,
|
||||
} from '@/utils/dataBrowser/hasuraQueryHelpers';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
|
||||
export interface DeleteTableMigrationVariables {
|
||||
/**
|
||||
@@ -39,32 +40,29 @@ export default async function deleteTable({
|
||||
),
|
||||
];
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `drop_table_${schema}_${table}`,
|
||||
down: [
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
cascade: false,
|
||||
read_only: false,
|
||||
source: '',
|
||||
sql: getEmptyDownMigrationMessage(deleteTableArgs),
|
||||
},
|
||||
},
|
||||
],
|
||||
up: deleteTableArgs,
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `drop_table_${schema}_${table}`,
|
||||
down: [
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
cascade: false,
|
||||
read_only: false,
|
||||
source: '',
|
||||
sql: getEmptyDownMigrationMessage(deleteTableArgs),
|
||||
},
|
||||
},
|
||||
],
|
||||
up: deleteTableArgs,
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
QueryResult,
|
||||
} from '@/types/data-browser';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
import prepareTrackForeignKeyRelationsMetadata from './prepareTrackForeignKeyRelationsMetadata';
|
||||
|
||||
export interface TrackForeignKeyRelationsMigrationVariables {
|
||||
@@ -45,23 +46,20 @@ export default async function trackForeignKeyRelationsMigration({
|
||||
foreignKeyRelations,
|
||||
});
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `track_foreign_key_relations_${schema}_${table}`,
|
||||
down: [],
|
||||
up: creatableRelationships,
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `track_foreign_key_relations_${schema}_${table}`,
|
||||
down: [],
|
||||
up: creatableRelationships,
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
QueryResult,
|
||||
} from '@/types/data-browser';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
|
||||
export interface TrackTableMigrationVariables {
|
||||
/**
|
||||
@@ -23,32 +24,29 @@ export default async function trackTableMigration({
|
||||
adminSecret,
|
||||
table,
|
||||
}: TrackTableMigrationOptions & TrackTableMigrationVariables) {
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `add_existing_table_or_view_${schema}_${table.name}`,
|
||||
down: [
|
||||
{
|
||||
type: 'pg_untrack_table',
|
||||
args: { source: dataSource, table: { schema, name: table.name } },
|
||||
},
|
||||
],
|
||||
up: [
|
||||
{
|
||||
args: { source: dataSource, table: { schema, name: table.name } },
|
||||
type: 'pg_track_table',
|
||||
},
|
||||
],
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `add_existing_table_or_view_${schema}_${table.name}`,
|
||||
down: [
|
||||
{
|
||||
type: 'pg_untrack_table',
|
||||
args: { source: dataSource, table: { schema, name: table.name } },
|
||||
},
|
||||
],
|
||||
up: [
|
||||
{
|
||||
args: { source: dataSource, table: { schema, name: table.name } },
|
||||
type: 'pg_track_table',
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
} from '@/types/data-browser';
|
||||
import { getEmptyDownMigrationMessage } from '@/utils/dataBrowser/hasuraQueryHelpers';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
import prepareUpdateColumnQuery from './prepareUpdateColumnQuery';
|
||||
|
||||
export interface UpdateColumnMigrationVariables {
|
||||
@@ -65,22 +66,19 @@ export default async function updateColumnMigration({
|
||||
];
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${table}_alter_column_${originalColumn.name}`,
|
||||
down: columnUpdateDownMigration,
|
||||
up: columnUpdateUpMigration,
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${table}_alter_column_${originalColumn.name}`,
|
||||
down: columnUpdateDownMigration,
|
||||
up: columnUpdateUpMigration,
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
} from '@/types/data-browser';
|
||||
import { getEmptyDownMigrationMessage } from '@/utils/dataBrowser/hasuraQueryHelpers';
|
||||
import normalizeQueryError from '@/utils/dataBrowser/normalizeQueryError';
|
||||
import { LOCAL_MIGRATIONS_URL } from '@/utils/env';
|
||||
import prepareUpdateTableQuery from './prepareUpdateTableQuery';
|
||||
|
||||
export interface UpdateTableMigrationVariables {
|
||||
@@ -56,32 +57,29 @@ export default async function updateTableMigration({
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_NHOST_MIGRATIONS_URL}/apis/migrate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${originalTable.table_name}`,
|
||||
down: [
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
cascade: false,
|
||||
read_only: false,
|
||||
source: '',
|
||||
sql: getEmptyDownMigrationMessage(args),
|
||||
},
|
||||
},
|
||||
],
|
||||
up: args,
|
||||
}),
|
||||
const response = await fetch(`${LOCAL_MIGRATIONS_URL}/apis/migrate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
dataSource,
|
||||
skip_execution: false,
|
||||
name: `alter_table_${schema}_${originalTable.table_name}`,
|
||||
down: [
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
cascade: false,
|
||||
read_only: false,
|
||||
source: '',
|
||||
sql: getEmptyDownMigrationMessage(args),
|
||||
},
|
||||
},
|
||||
],
|
||||
up: args,
|
||||
}),
|
||||
});
|
||||
|
||||
const responseData: [AffectedRowsResult, QueryResult<string[]>] | QueryError =
|
||||
await response.json();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LOCAL_SUBDOMAIN } from '@/utils/env';
|
||||
import { isDevOrStaging } from '@/utils/helpers';
|
||||
import type { NhostClientConstructorParams } from '@nhost/nhost-js';
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
@@ -20,7 +21,7 @@ export function useAppClient(
|
||||
|
||||
if (process.env.NEXT_PUBLIC_ENV === 'dev') {
|
||||
return new NhostClient({
|
||||
subdomain: 'localhost:1337',
|
||||
subdomain: LOCAL_SUBDOMAIN,
|
||||
start: false,
|
||||
...options,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import SettingsLayout from '@/components/settings/SettingsLayout';
|
||||
import AnonymousSignInSettings from '@/components/settings/signInMethods/AnonymousSignInSettings';
|
||||
import AppleProviderSettings from '@/components/settings/signInMethods/AppleProviderSettings';
|
||||
import DiscordProviderSettings from '@/components/settings/signInMethods/DiscordProviderSettings';
|
||||
import EmailSettings from '@/components/settings/signInMethods/EmailSettings';
|
||||
import EmailAndPasswordSettings from '@/components/settings/signInMethods/EmailAndPasswordSettings';
|
||||
import FacebookProviderSettings from '@/components/settings/signInMethods/FacebookProviderSettings';
|
||||
import GitHubProviderSettings from '@/components/settings/signInMethods/GitHubProviderSettings';
|
||||
import GoogleProviderSettings from '@/components/settings/signInMethods/GoogleProviderSettings';
|
||||
@@ -35,7 +35,7 @@ export default function SettingsSignInMethodsPage() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
delay={1000}
|
||||
label="Loading Sign-In Methods Settings..."
|
||||
label="Loading sign-in method settings..."
|
||||
className="justify-center"
|
||||
/>
|
||||
);
|
||||
@@ -50,21 +50,21 @@ export default function SettingsSignInMethodsPage() {
|
||||
className="max-w-5xl space-y-8 bg-fafafa"
|
||||
wrapperClassName="bg-fafafa"
|
||||
>
|
||||
<EmailSettings />
|
||||
<EmailAndPasswordSettings />
|
||||
<MagicLinkSettings />
|
||||
<WebAuthnSettings />
|
||||
<AnonymousSignInSettings />
|
||||
<SMSSettings />
|
||||
<GoogleProviderSettings />
|
||||
<GitHubProviderSettings />
|
||||
<LinkedInProviderSettings />
|
||||
<AppleProviderSettings />
|
||||
<WindowsLiveProviderSettings />
|
||||
<DiscordProviderSettings />
|
||||
<FacebookProviderSettings />
|
||||
<GitHubProviderSettings />
|
||||
<GoogleProviderSettings />
|
||||
<LinkedInProviderSettings />
|
||||
<SpotifyProviderSettings />
|
||||
<TwitchProviderSettings />
|
||||
<DiscordProviderSettings />
|
||||
<TwitterProviderSettings />
|
||||
<WindowsLiveProviderSettings />
|
||||
<WorkOsProviderSettings />
|
||||
</Container>
|
||||
);
|
||||
@@ -72,11 +72,7 @@ export default function SettingsSignInMethodsPage() {
|
||||
|
||||
SettingsSignInMethodsPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<SettingsLayout
|
||||
mainContainerProps={{
|
||||
className: 'bg-fafafa',
|
||||
}}
|
||||
>
|
||||
<SettingsLayout mainContainerProps={{ className: 'bg-fafafa' }}>
|
||||
{page}
|
||||
</SettingsLayout>
|
||||
);
|
||||
|
||||
@@ -11,8 +11,8 @@ export default class MyDocument extends Document {
|
||||
|
||||
render() {
|
||||
const meta = {
|
||||
title: 'Nhost 2.0 | Console',
|
||||
description: 'Nhost Console 2.0',
|
||||
title: 'Dashboard - Nhost',
|
||||
description: 'Nhost Dashboard',
|
||||
image: '/assets/splash.png',
|
||||
};
|
||||
|
||||
|
||||
@@ -26,5 +26,5 @@ export default function IndexPage() {
|
||||
}
|
||||
|
||||
IndexPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <AuthenticatedLayout title="Console">{page}</AuthenticatedLayout>;
|
||||
return <AuthenticatedLayout title="Dashboard">{page}</AuthenticatedLayout>;
|
||||
};
|
||||
|
||||
36
dashboard/src/utils/env.ts
Normal file
36
dashboard/src/utils/env.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* URL of Hasura's Migration API. This is only used when local development is
|
||||
* enabled.
|
||||
*/
|
||||
export const LOCAL_MIGRATIONS_URL = `http://localhost:${
|
||||
process.env.NEXT_PUBLIC_NHOST_LOCAL_MIGRATIONS_PORT || 9693
|
||||
}`;
|
||||
|
||||
/**
|
||||
* Port of the locally running backend.s
|
||||
*/
|
||||
export const LOCAL_BACKEND_PORT =
|
||||
process.env.NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT;
|
||||
|
||||
/**
|
||||
* Local subdomain. This is only used when local development is enabled.
|
||||
*/
|
||||
export const LOCAL_SUBDOMAIN = LOCAL_BACKEND_PORT
|
||||
? `localhost:${LOCAL_BACKEND_PORT}`
|
||||
: 'localhost';
|
||||
|
||||
/**
|
||||
* URL of Hasura Console. This is only used when running the Nhost Dashboard
|
||||
* locally.
|
||||
*/
|
||||
export const LOCAL_HASURA_URL = `http://localhost:${
|
||||
process.env.NEXT_PUBLIC_NHOST_LOCAL_HASURA_PORT || 9695
|
||||
}`;
|
||||
|
||||
/**
|
||||
* Backend URL for the locally running instance. This is only used when running
|
||||
* the Nhost Dashboard locally.
|
||||
*/
|
||||
export const LOCAL_BACKEND_URL = `http://localhost:${
|
||||
process.env.NEXT_PUBLIC_NHOST_LOCAL_BACKEND_PORT || 1337
|
||||
}`;
|
||||
@@ -6,6 +6,7 @@ import features from '@/data/features.json';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import type { NextRouter } from 'next/router';
|
||||
import slugify from 'slugify';
|
||||
import { LOCAL_BACKEND_URL } from './env';
|
||||
import type { DeploymentRowFragment } from './__generated__/graphql';
|
||||
|
||||
export function getLastLiveDeployment(deployments: DeploymentRowFragment[]) {
|
||||
@@ -57,11 +58,11 @@ export function getCurrentEnvironment(): Environment {
|
||||
|
||||
export function generateRemoteAppUrl(subdomain: string): string {
|
||||
if (process.env.NEXT_PUBLIC_NHOST_PLATFORM !== 'true') {
|
||||
return 'http://localhost:1337';
|
||||
return LOCAL_BACKEND_URL;
|
||||
}
|
||||
|
||||
if (process.env.NEXT_PUBLIC_ENV === 'dev') {
|
||||
return process.env.NEXT_PUBLIC_NHOST_BACKEND_URL || 'http://localhost:1337';
|
||||
return process.env.NEXT_PUBLIC_NHOST_BACKEND_URL || LOCAL_BACKEND_URL;
|
||||
}
|
||||
|
||||
if (process.env.NEXT_PUBLIC_ENV === 'staging') {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { NhostClient } from '@nhost/nextjs';
|
||||
import { LOCAL_SUBDOMAIN } from './env';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: process.env.NEXT_PUBLIC_NHOST_BACKEND_URL as string,
|
||||
});
|
||||
export const nhost =
|
||||
process.env.NEXT_PUBLIC_NHOST_PLATFORM === 'true'
|
||||
? new NhostClient({ backendUrl: process.env.NEXT_PUBLIC_NHOST_BACKEND_URL })
|
||||
: new NhostClient({ subdomain: LOCAL_SUBDOMAIN });
|
||||
|
||||
export { nhost };
|
||||
export default nhost;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ca012d79: docs(workos): WorkOS Docs
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -33,7 +33,7 @@ Follow this guide to sign in users with GitHub.
|
||||
|
||||
- Fill in Application Name.
|
||||
- Fill in Homepage URL.
|
||||
- Fill in **Authorization callback URL** with your OAuth Callbacke URL from Nhost.
|
||||
- Fill in **Authorization callback URL** with your OAuth Callback URL from Nhost.
|
||||
|
||||
## Configure Nhost
|
||||
|
||||
|
||||
67
docs/docs/authentication/sign-in-methods/4-workos.mdx
Normal file
67
docs/docs/authentication/sign-in-methods/4-workos.mdx
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Sign In with WorkOS
|
||||
sidebar_label: WorkOS
|
||||
slug: /authentication/sign-in-with-workos
|
||||
image: /img/og/sign-in-with-workos.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with WorkOS.
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
alt="WorkOS Sign In Preview"
|
||||
src="/img/social-providers/workos-preview.svg"
|
||||
width={480}
|
||||
height={267}
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Create WorkOS Account
|
||||
|
||||
- Go to [WorkOS's website](https://workos.com/).
|
||||
- Click on **Sign In** in the top menu and create a WorkOS account.
|
||||
|
||||
## Get the Client ID
|
||||
|
||||
- In the WorkOS dashboard, click on **Configuration** in the left menu.
|
||||
- Copy the **Client ID**, which starts with `project_`.
|
||||
- Paste the **Client ID** in the **Client ID** field in the WorkOS settings in the Nhost Dashboard.
|
||||
|
||||
## Set correct Redirect URI
|
||||
|
||||
- In the WorkOS dashboard, click on **Configuration** in the left menu.
|
||||
- Click on **Edit Redirect URIs** in the Redirect URIs section.
|
||||
- Add the **Redirect URL** from the Nhost Dashboard to the list of Redirect URIs.
|
||||
- Set the newly added redirect URI as the **Default Redirect URI**.
|
||||
- Click on **Close**.
|
||||
|
||||
## Get the Client Secret
|
||||
|
||||
- In the WorkOS dashboard, click on **API Keys** in the left menu.
|
||||
- Click on the eye icon next to the **Client Secret** to reveal it.
|
||||
- Copy the **Client Secret**, which stars with `sk_`.
|
||||
- Paste the **Client Secret** in the **Client Secret** field in the WorkOS settings in the Nhost Dashboard.
|
||||
|
||||
## Get Organization ID
|
||||
|
||||
- In the WorkOS dashboard, click on **Organizations** in the left menu.
|
||||
- Click on **Create an Organization**.
|
||||
- Fill in the organization details and click on **Create Organization**.
|
||||
- Click on the newly created organization.
|
||||
- Copy the **Organization ID**, which stars with `org_`.
|
||||
- Paste the **Organization ID** in the **Organization ID** field in the WorkOS settings in the Nhost Dashboard.
|
||||
- Click **Save** in the Nhost Dashboard to save all WorkOS settings.
|
||||
|
||||
The WorkOS configuration is now completed with Nhost.
|
||||
|
||||
See the [WorkOS documentation](https://workos.com/docs/) to learn more about how to configure WorkOS.
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'workos'
|
||||
})
|
||||
```
|
||||
@@ -88,6 +88,26 @@ custom_claims: '{"organisation-id":"user.profile.organisation.id"}'
|
||||
|
||||
JSON columns cannot be used in custom claims, with the exception of the `users.metadata` column.
|
||||
|
||||
### Arrays
|
||||
|
||||
When the target value is expected to be an array, it is important to explicitly add a `[]` at the end of the expression. For instance: if `organisationIds` is expected to be an array, you must set the expression to `organisationIds[]`. It will otherwise return a litteral when the array is a singleton.
|
||||
|
||||
✅ Singleton array with `'{"organisation-ids":"organisationIds[]"}'`
|
||||
|
||||
```json
|
||||
{
|
||||
"x-hasura-organisation-ids": "{\"org-id-1\"}"
|
||||
}
|
||||
```
|
||||
|
||||
🛑 Singleton array with `'{"organisation-ids":"organisationIds"}'`
|
||||
|
||||
```json
|
||||
{
|
||||
"x-hasura-organisation-ids": "org-id-1"
|
||||
}
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
Every GraphQL request is resolved based on a **single role**. Roles are added in the Hasura Console when selecting a table and clicking **Permisisons**.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
||||
BIN
docs/static/img/og/sign-in-with-workos.png
vendored
Normal file
BIN
docs/static/img/og/sign-in-with-workos.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
24
docs/static/img/social-providers/workos-preview.svg
vendored
Normal file
24
docs/static/img/social-providers/workos-preview.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 55 KiB |
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
ports:
|
||||
- '5432:5432'
|
||||
graphql-engine:
|
||||
image: hasura/graphql-engine:v2.2.0
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
depends_on:
|
||||
- 'postgres'
|
||||
restart: always
|
||||
@@ -72,7 +72,7 @@ services:
|
||||
- "traefik.http.routers.auth.middlewares=strip-auth@docker"
|
||||
- "traefik.http.routers.auth.entrypoints=web"
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.1
|
||||
image: nhost/hasura-storage:0.3.1
|
||||
depends_on:
|
||||
- postgres
|
||||
- graphql-engine
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
@@ -15,7 +15,7 @@ auth:
|
||||
allowed_emails: ''
|
||||
blocked_email_domains: ''
|
||||
blocked_emails: ''
|
||||
allowed_redirect_urls: 'http://127.0.0.1:3000'
|
||||
allowed_redirect_urls: http://127.0.0.1:3000
|
||||
anonymous_users_enabled: true
|
||||
client_url: http://localhost:3000
|
||||
disable_new_users: false
|
||||
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
hasura:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
minio:
|
||||
environment:
|
||||
minio_root_password: minioaccesskey123123
|
||||
@@ -13,9 +13,9 @@ services:
|
||||
postgres_password: postgres
|
||||
postgres_user: postgres
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
webauthn:
|
||||
enabled: true
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.7
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
hasura:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
minio:
|
||||
environment:
|
||||
minio_root_password: minioaccesskey123123
|
||||
@@ -13,6 +14,8 @@ services:
|
||||
environment:
|
||||
postgres_password: postgres
|
||||
postgres_user: postgres
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -3,6 +3,7 @@ services:
|
||||
hasura:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
minio:
|
||||
environment:
|
||||
minio_root_password: minioaccesskey123123
|
||||
@@ -11,15 +12,19 @@ services:
|
||||
environment:
|
||||
postgres_password: postgres
|
||||
postgres_user: postgres
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
allowed_email_domains: ""
|
||||
allowed_emails: ""
|
||||
blocked_email_domains: ""
|
||||
blocked_emails: ""
|
||||
allowed_email_domains: ''
|
||||
allowed_emails: ''
|
||||
blocked_email_domains: ''
|
||||
blocked_emails: ''
|
||||
url:
|
||||
allowed_redirect_urls: ""
|
||||
allowed_redirect_urls: ''
|
||||
anonymous_users_enabled: false
|
||||
client_url: http://localhost:3000
|
||||
disable_new_users: false
|
||||
@@ -28,11 +33,11 @@ auth:
|
||||
passwordless:
|
||||
enabled: false
|
||||
signin_email_verified_required: true
|
||||
template_fetch_url: ""
|
||||
template_fetch_url: ''
|
||||
gravatar:
|
||||
default: ""
|
||||
default: ''
|
||||
enabled: true
|
||||
rating: ""
|
||||
rating: ''
|
||||
locale:
|
||||
allowed: en
|
||||
default: en
|
||||
@@ -41,65 +46,65 @@ auth:
|
||||
min_length: 3
|
||||
provider:
|
||||
apple:
|
||||
client_id: ""
|
||||
client_id: ''
|
||||
enabled: false
|
||||
key_id: ""
|
||||
private_key: ""
|
||||
key_id: ''
|
||||
private_key: ''
|
||||
scope: name,email
|
||||
team_id: ""
|
||||
team_id: ''
|
||||
bitbucket:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
facebook:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: email,photos,displayName
|
||||
github:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: user:email
|
||||
token_url: ""
|
||||
user_profile_url: ""
|
||||
token_url: ''
|
||||
user_profile_url: ''
|
||||
gitlab:
|
||||
base_url: ""
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
base_url: ''
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: read_user
|
||||
google:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: email,profile
|
||||
linkedin:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: r_emailaddress,r_liteprofile
|
||||
spotify:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: user-read-email,user-read-private
|
||||
strava:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
twilio:
|
||||
account_sid: ""
|
||||
auth_token: ""
|
||||
account_sid: ''
|
||||
auth_token: ''
|
||||
enabled: false
|
||||
messaging_service_id: ""
|
||||
messaging_service_id: ''
|
||||
twitter:
|
||||
consumer_key: ""
|
||||
consumer_secret: ""
|
||||
consumer_key: ''
|
||||
consumer_secret: ''
|
||||
enabled: false
|
||||
windows_live:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: wl.basic,wl.emails,wl.contacts_emails
|
||||
sms:
|
||||
@@ -108,13 +113,13 @@ auth:
|
||||
enabled: false
|
||||
provider:
|
||||
twilio:
|
||||
account_sid: ""
|
||||
auth_token: ""
|
||||
from: ""
|
||||
messaging_service_id: ""
|
||||
account_sid: ''
|
||||
auth_token: ''
|
||||
from: ''
|
||||
messaging_service_id: ''
|
||||
smtp:
|
||||
host: mailhog
|
||||
method: ""
|
||||
method: ''
|
||||
pass: password
|
||||
port: 1025
|
||||
secure: false
|
||||
|
||||
9
examples/serverless-functions/CHANGELOG.md
Normal file
9
examples/serverless-functions/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# @nhost-examples/serverless-functions
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 93db7182: feat(stripe-graphql-js): add charges, payment intents and connected accounts
|
||||
- Updated dependencies [93db7182]
|
||||
- @nhost/stripe-graphql-js@0.0.6
|
||||
25
examples/serverless-functions/functions/async-await/index.ts
Normal file
25
examples/serverless-functions/functions/async-await/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
|
||||
- How to use async/await.
|
||||
|
||||
Test:
|
||||
|
||||
curl http://localhost:1337/v1/functions/async-await
|
||||
*/
|
||||
|
||||
import fetch from 'cross-fetch'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
// using async in the function signature
|
||||
export default async (req: Request, res: Response) => {
|
||||
// using await
|
||||
const result = await fetch('//api.github.com/repos/nhost/nhost')
|
||||
|
||||
if (result.status >= 400) {
|
||||
throw new Error('Bad response from server')
|
||||
}
|
||||
|
||||
const repo = await result.json()
|
||||
|
||||
res.json(repo)
|
||||
}
|
||||
44
examples/serverless-functions/functions/send-email/index.ts
Normal file
44
examples/serverless-functions/functions/send-email/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
- How to send an email via SMTP.
|
||||
|
||||
Test:
|
||||
|
||||
curl http://localhost:1337/v1/functions/send-email
|
||||
*/
|
||||
|
||||
import { Request, Response } from 'express'
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
export default async (req: Request, res: Response) => {
|
||||
// Only needed if you don't have a real mail account for testing
|
||||
let testAccount = await nodemailer.createTestAccount()
|
||||
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: 'smtp.ethereal.email',
|
||||
port: 587,
|
||||
secure: false, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: testAccount.user, // generated ethereal user
|
||||
pass: testAccount.pass // generated ethereal password
|
||||
}
|
||||
})
|
||||
|
||||
// send mail with defined transport object
|
||||
let info = await transporter.sendMail({
|
||||
from: '"Fred Foo 👻" <foo@example.com>', // sender address
|
||||
to: 'bar@example.com, baz@example.com', // list of receivers
|
||||
subject: 'Hello ✔', // Subject line
|
||||
text: 'Hello world?', // plain text body
|
||||
html: '<b>Hello world?</b>' // html body
|
||||
})
|
||||
|
||||
console.log('Message sent: %s', info.messageId)
|
||||
// Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>
|
||||
|
||||
// Preview only available when sending through an Ethereal account
|
||||
console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info))
|
||||
|
||||
res.json('Message sent')
|
||||
}
|
||||
@@ -3,6 +3,7 @@ services:
|
||||
hasura:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
minio:
|
||||
environment:
|
||||
minio_root_password: minioaccesskey123123
|
||||
@@ -11,15 +12,19 @@ services:
|
||||
environment:
|
||||
postgres_password: postgres
|
||||
postgres_user: postgres
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
allowed_email_domains: ""
|
||||
allowed_emails: ""
|
||||
blocked_email_domains: ""
|
||||
blocked_emails: ""
|
||||
allowed_email_domains: ''
|
||||
allowed_emails: ''
|
||||
blocked_email_domains: ''
|
||||
blocked_emails: ''
|
||||
url:
|
||||
allowed_redirect_urls: ""
|
||||
allowed_redirect_urls: ''
|
||||
anonymous_users_enabled: false
|
||||
client_url: http://localhost:3000
|
||||
disable_new_users: false
|
||||
@@ -28,11 +33,11 @@ auth:
|
||||
passwordless:
|
||||
enabled: false
|
||||
signin_email_verified_required: true
|
||||
template_fetch_url: ""
|
||||
template_fetch_url: ''
|
||||
gravatar:
|
||||
default: ""
|
||||
default: ''
|
||||
enabled: true
|
||||
rating: ""
|
||||
rating: ''
|
||||
locale:
|
||||
allowed: en
|
||||
default: en
|
||||
@@ -41,65 +46,65 @@ auth:
|
||||
min_length: 3
|
||||
provider:
|
||||
apple:
|
||||
client_id: ""
|
||||
client_id: ''
|
||||
enabled: false
|
||||
key_id: ""
|
||||
private_key: ""
|
||||
key_id: ''
|
||||
private_key: ''
|
||||
scope: name,email
|
||||
team_id: ""
|
||||
team_id: ''
|
||||
bitbucket:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
facebook:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: email,photos,displayName
|
||||
github:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: user:email
|
||||
token_url: ""
|
||||
user_profile_url: ""
|
||||
token_url: ''
|
||||
user_profile_url: ''
|
||||
gitlab:
|
||||
base_url: ""
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
base_url: ''
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: read_user
|
||||
google:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: email,profile
|
||||
linkedin:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: r_emailaddress,r_liteprofile
|
||||
spotify:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: user-read-email,user-read-private
|
||||
strava:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
twilio:
|
||||
account_sid: ""
|
||||
auth_token: ""
|
||||
account_sid: ''
|
||||
auth_token: ''
|
||||
enabled: false
|
||||
messaging_service_id: ""
|
||||
messaging_service_id: ''
|
||||
twitter:
|
||||
consumer_key: ""
|
||||
consumer_secret: ""
|
||||
consumer_key: ''
|
||||
consumer_secret: ''
|
||||
enabled: false
|
||||
windows_live:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
client_id: ''
|
||||
client_secret: ''
|
||||
enabled: false
|
||||
scope: wl.basic,wl.emails,wl.contacts_emails
|
||||
sms:
|
||||
@@ -108,13 +113,13 @@ auth:
|
||||
enabled: false
|
||||
provider:
|
||||
twilio:
|
||||
account_sid: ""
|
||||
auth_token: ""
|
||||
from: ""
|
||||
messaging_service_id: ""
|
||||
account_sid: ''
|
||||
auth_token: ''
|
||||
from: ''
|
||||
messaging_service_id: ''
|
||||
smtp:
|
||||
host: mailhog
|
||||
method: ""
|
||||
method: ''
|
||||
pass: password
|
||||
port: 1025
|
||||
secure: false
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
{
|
||||
"name": "@nhost-examples/serverless-functions",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@graphql-yoga/node": "^2.13.13",
|
||||
"@nhost/stripe-graphql-js": "^0.0.5",
|
||||
"@nhost/stripe-graphql-js": "^0.0.6",
|
||||
"@pothos/core": "^3.21.0",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"graphql": "15.7.2",
|
||||
"nodemailer": "^6.8.0",
|
||||
"slugify": "^1.6.5"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/stripe-graphql-js
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 93db7182: feat(stripe-graphql-js): add charges, payment intents and connected accounts
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -197,6 +197,14 @@ Start the development server:
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Include the correct admin secret header for admin access
|
||||
|
||||
```js
|
||||
{
|
||||
"x-hasura-admin-secret":"<secret value matching your NHOST_ADMIN_SECRET environment variable>"
|
||||
}
|
||||
```
|
||||
|
||||
The GraphQL Server will reload every time the code changes.
|
||||
|
||||
Open GraphiQL:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/stripe-graphql-js",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"description": "Stripe GraphQL API",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -3,7 +3,13 @@ import Stripe from 'stripe'
|
||||
|
||||
import SchemaBuilder from '@pothos/core'
|
||||
|
||||
import { Context, StripeInvoice, StripePaymentMethod, StripeSubscription } from './types'
|
||||
import {
|
||||
Context,
|
||||
StripeCharge,
|
||||
StripeInvoice,
|
||||
StripePaymentIntent,
|
||||
StripePaymentMethod,
|
||||
StripeSubscription} from './types'
|
||||
|
||||
// TODO: Make sure we either use Type or Types (e.g. StripePaymentMethodTypes or StripePaymentMethodType ) everywhere
|
||||
|
||||
@@ -93,6 +99,18 @@ const builder = new SchemaBuilder<{
|
||||
|
||||
// BILLING PORTAL
|
||||
StripeBillingPortalSession: Stripe.BillingPortal.Session
|
||||
|
||||
// PAYMENT INTENT
|
||||
StripePaymentIntent: StripePaymentIntent
|
||||
StripePaymentIntents: Stripe.ApiList<StripePaymentIntent>
|
||||
|
||||
// CHARGES
|
||||
StripeCharge: StripeCharge
|
||||
StripeCharges: Stripe.ApiList<StripeCharge>
|
||||
|
||||
// CONNECTED ACCOUNTS
|
||||
StripeConnectedAccount: Stripe.Account
|
||||
StripeConnectedAccounts: Stripe.ApiList<Stripe.Account>
|
||||
}
|
||||
Context: Context
|
||||
}>({})
|
||||
|
||||
82
integrations/stripe-graphql-js/src/schema/charge.ts
Normal file
82
integrations/stripe-graphql-js/src/schema/charge.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import { builder } from '../builder'
|
||||
import { StripeInvoice } from '../types'
|
||||
import { stripe } from '../utils'
|
||||
|
||||
builder.objectType('StripeCharge', {
|
||||
description: 'Stripe charge object',
|
||||
fields: (t) => ({
|
||||
id: t.exposeString('id'),
|
||||
customer: t.exposeString('customer'),
|
||||
amount: t.exposeInt('amount'),
|
||||
amountCaptured: t.exposeInt('amount_captured'),
|
||||
amountRefunded: t.exposeInt('amount_refunded'),
|
||||
applicationFeeAmount: t.exposeInt('application_fee_amount', { nullable: true }),
|
||||
calculatedStatementDescriptor: t.exposeString('calculated_statement_descriptor', {
|
||||
nullable: true
|
||||
}),
|
||||
billingDetails: t.expose('billing_details', { type: 'JSON', nullable: true }),
|
||||
captured: t.exposeBoolean('captured'),
|
||||
created: t.exposeInt('created', {
|
||||
nullable: true
|
||||
}),
|
||||
currency: t.exposeString('currency'),
|
||||
description: t.exposeString('description', { nullable: true }),
|
||||
disputed: t.exposeBoolean('disputed'),
|
||||
failureCode: t.exposeString('failure_code', { nullable: true }),
|
||||
invoice: t.field({
|
||||
type: 'StripeInvoice',
|
||||
nullable: true,
|
||||
resolve: async (charge) => {
|
||||
const { invoice } = charge
|
||||
|
||||
if (!invoice) {
|
||||
return null
|
||||
}
|
||||
|
||||
const invoiceData = await stripe.invoices.retrieve(invoice as string)
|
||||
|
||||
return invoiceData as Stripe.Response<StripeInvoice>
|
||||
}
|
||||
}),
|
||||
application: t.field({
|
||||
type: 'StripeConnectedAccount',
|
||||
nullable: true,
|
||||
resolve: async (charge) => {
|
||||
const { application } = charge
|
||||
if (!application) return null
|
||||
|
||||
const connectedAccount = await stripe.accounts.retrieve(application as string)
|
||||
|
||||
return connectedAccount
|
||||
}
|
||||
}),
|
||||
livemode: t.exposeBoolean('livemode'),
|
||||
metadata: t.expose('metadata', { nullable: true, type: 'JSON' }),
|
||||
outcome: t.expose('outcome', { nullable: true, type: 'JSON' }),
|
||||
fraudDetails: t.expose('fraud_details', { nullable: true, type: 'JSON' }),
|
||||
paid: t.exposeBoolean('paid'),
|
||||
receiptEmail: t.exposeString('receipt_email', { nullable: true }),
|
||||
receiptNumber: t.exposeString('receipt_number', { nullable: true }),
|
||||
receiptUrl: t.exposeString('receipt_url', { nullable: true }),
|
||||
refunded: t.exposeBoolean('refunded'),
|
||||
shipping: t.expose('shipping', { nullable: true, type: 'JSON' }),
|
||||
statementDescriptor: t.exposeString('statement_descriptor', { nullable: true }),
|
||||
statementDescriptorSuffix: t.exposeString('statement_descriptor_suffix', { nullable: true }),
|
||||
status: t.exposeString('status'),
|
||||
transferData: t.expose('transfer_data', { nullable: true, type: 'JSON' }),
|
||||
transferGroup: t.exposeString('transfer_group', { nullable: true }),
|
||||
refunds: t.expose('refunds', { nullable: true, type: 'JSON' }),
|
||||
paymentMethodDetails: t.expose('payment_method_details', { nullable: true, type: 'JSON' }),
|
||||
paymentIntent: t.exposeString('payment_intent', { nullable: true }),
|
||||
paymentMethod: t.exposeString('payment_method', { nullable: true })
|
||||
|
||||
// todo: add missing fields
|
||||
// application_fee
|
||||
// balance_transaction
|
||||
// on_behalf_of
|
||||
// failure_balance_transaction
|
||||
// source_transfer
|
||||
})
|
||||
})
|
||||
14
integrations/stripe-graphql-js/src/schema/charges.ts
Normal file
14
integrations/stripe-graphql-js/src/schema/charges.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { builder } from '../builder'
|
||||
|
||||
builder.objectType('StripeCharges', {
|
||||
description: 'List of Stripe charge objects',
|
||||
fields: (t) => ({
|
||||
object: t.exposeString('object'),
|
||||
url: t.exposeString('url'),
|
||||
hasMore: t.exposeBoolean('has_more'),
|
||||
data: t.expose('data', {
|
||||
type: ['StripeCharge'],
|
||||
nullable: false
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
import { builder } from '../builder'
|
||||
|
||||
builder.objectType('StripeConnectedAccount', {
|
||||
description: 'Stripe charge object',
|
||||
fields: (t) => ({
|
||||
id: t.exposeString('id'),
|
||||
object: t.exposeString('object'),
|
||||
country: t.exposeString('country', { nullable: true }),
|
||||
businessType: t.exposeString('business_type', { nullable: true }),
|
||||
capabilities: t.expose('capabilities', { type: 'JSON' }),
|
||||
company: t.expose('company', { type: 'JSON' }),
|
||||
email: t.exposeString('email', { nullable: true }),
|
||||
individual: t.expose('individual', { type: 'JSON' }),
|
||||
metadata: t.expose('metadata', { type: 'JSON' }),
|
||||
requirements: t.expose('requirements', { type: 'JSON' }),
|
||||
tosAcceptance: t.expose('tos_acceptance', { type: 'JSON' }),
|
||||
businessProfile: t.expose('business_profile', { type: 'JSON' }),
|
||||
chargesEnabled: t.exposeBoolean('charges_enabled'),
|
||||
controller: t.expose('controller', { nullable: true, type: 'JSON' }),
|
||||
created: t.exposeInt('created', { nullable: true }),
|
||||
defaultCurrency: t.exposeString('default_currency', { nullable: true }),
|
||||
detailsSubmitted: t.exposeBoolean('details_submitted'),
|
||||
externalAccounts: t.expose('external_accounts', { type: 'JSON' }),
|
||||
futureRequirements: t.expose('future_requirements', { type: 'JSON' }),
|
||||
payoutsEnabled: t.exposeBoolean('payouts_enabled'),
|
||||
settings: t.expose('settings', { type: 'JSON' })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import { builder } from '../builder'
|
||||
|
||||
builder.objectType('StripeConnectedAccounts', {
|
||||
description: 'List of Stripe Connected Account objects',
|
||||
fields: (t) => ({
|
||||
object: t.exposeString('object'),
|
||||
url: t.exposeString('url'),
|
||||
hasMore: t.exposeBoolean('has_more'),
|
||||
data: t.expose('data', {
|
||||
type: ['StripeConnectedAccount'],
|
||||
nullable: false
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,12 @@
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import { builder } from '../builder'
|
||||
import { StripeInvoice, StripePaymentMethod, StripeSubscription } from '../types'
|
||||
import {
|
||||
StripeCharge,
|
||||
StripeInvoice,
|
||||
StripePaymentIntent,
|
||||
StripePaymentMethod,
|
||||
StripeSubscription} from '../types'
|
||||
import { stripe } from '../utils'
|
||||
|
||||
import { StripePaymentMethodTypes } from './payment-methods'
|
||||
@@ -110,6 +115,16 @@ builder.objectType('StripeCustomer', {
|
||||
return invoices as Stripe.Response<Stripe.ApiList<StripeInvoice>>
|
||||
}
|
||||
}),
|
||||
paymentIntents: t.field({
|
||||
type: 'StripePaymentIntents',
|
||||
nullable: false,
|
||||
resolve: async (customer) => {
|
||||
const paymentIntents = await stripe.paymentIntents.list({
|
||||
customer: customer.id
|
||||
})
|
||||
return paymentIntents as Stripe.Response<Stripe.ApiList<StripePaymentIntent>>
|
||||
}
|
||||
}),
|
||||
paymentMethods: t.field({
|
||||
type: 'StripePaymentMethods',
|
||||
args: {
|
||||
@@ -138,6 +153,16 @@ builder.objectType('StripeCustomer', {
|
||||
})
|
||||
return paymentMethods as Stripe.Response<Stripe.ApiList<StripePaymentMethod>>
|
||||
}
|
||||
}),
|
||||
charges: t.field({
|
||||
type: 'StripeCharges',
|
||||
nullable: false,
|
||||
resolve: async (customer) => {
|
||||
const charges = await stripe.charges.list({
|
||||
customer: customer.id
|
||||
})
|
||||
return charges as Stripe.Response<Stripe.ApiList<StripeCharge>>
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -42,6 +42,12 @@ import './product'
|
||||
import './tax-rate'
|
||||
import './test-clock'
|
||||
import './billing-portal-session'
|
||||
import './payment-intent'
|
||||
import './payment-intents'
|
||||
import './charges'
|
||||
import './charge'
|
||||
import './connectedAccount'
|
||||
import './connectedAccounts'
|
||||
|
||||
import { builder } from '../builder'
|
||||
|
||||
|
||||
@@ -32,7 +32,18 @@ builder.objectType('StripeInvoice', {
|
||||
amountRemaining: t.exposeInt('amount_remaining', {
|
||||
description: `The difference between amount_due and amount_paid, in %s.`
|
||||
}),
|
||||
// todo: application
|
||||
application: t.field({
|
||||
type: 'StripeConnectedAccount',
|
||||
nullable: true,
|
||||
resolve: async (invoice) => {
|
||||
const { application } = invoice
|
||||
if (!application) return null
|
||||
|
||||
const connectedAccount = await stripe.accounts.retrieve(application as string)
|
||||
|
||||
return connectedAccount
|
||||
}
|
||||
}),
|
||||
applicationFeeAmount: t.exposeInt('application_fee_amount', {
|
||||
description: `The fee in %s that will be applied to the invoice and transferred to the application owner's Stripe account when the invoice is paid.`,
|
||||
nullable: true
|
||||
|
||||
75
integrations/stripe-graphql-js/src/schema/payment-intent.ts
Normal file
75
integrations/stripe-graphql-js/src/schema/payment-intent.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import { builder } from '../builder'
|
||||
import { StripeInvoice } from '../types'
|
||||
import { stripe } from '../utils'
|
||||
|
||||
builder.objectType('StripePaymentIntent', {
|
||||
description: 'Payment intents',
|
||||
fields: (t) => ({
|
||||
id: t.exposeString('id'),
|
||||
object: t.exposeString('object'),
|
||||
amount: t.exposeInt('amount'),
|
||||
currency: t.exposeString('currency'),
|
||||
description: t.exposeString('description', {
|
||||
nullable: true
|
||||
}),
|
||||
metadata: t.expose('metadata', {
|
||||
type: 'JSON',
|
||||
nullable: true
|
||||
}),
|
||||
paymentMethodTypes: t.exposeStringList('payment_method_types'),
|
||||
statementDescriptor: t.exposeString('statement_descriptor', {
|
||||
nullable: true
|
||||
}),
|
||||
statementDescriptorSuffix: t.exposeString('statement_descriptor_suffix', {
|
||||
nullable: true
|
||||
}),
|
||||
receiptEmail: t.exposeString('receipt_email', {
|
||||
nullable: true
|
||||
}),
|
||||
customer: t.exposeString('customer'),
|
||||
amountCapturable: t.exposeInt('amount_capturable'),
|
||||
amountDetails: t.expose('amount_details', {
|
||||
nullable: true,
|
||||
type: 'JSON'
|
||||
}),
|
||||
amountReceived: t.exposeInt('amount_received'),
|
||||
|
||||
applicationFeeAmount: t.exposeInt('application_fee_amount', {
|
||||
nullable: true
|
||||
}),
|
||||
canceledAt: t.exposeInt('canceled_at', {
|
||||
nullable: true
|
||||
}),
|
||||
transferGroup: t.exposeString('transfer_group', {
|
||||
nullable: true
|
||||
}),
|
||||
cancellationReason: t.exposeString('cancellation_reason', {
|
||||
nullable: true
|
||||
}),
|
||||
created: t.exposeInt('created', {
|
||||
nullable: true
|
||||
}),
|
||||
status: t.exposeString('status'),
|
||||
invoice: t.field({
|
||||
type: 'StripeInvoice',
|
||||
nullable: true,
|
||||
resolve: async (paymentIntent) => {
|
||||
const { invoice } = paymentIntent
|
||||
|
||||
if (!invoice) {
|
||||
return null
|
||||
}
|
||||
|
||||
const invoiceData = await stripe.invoices.retrieve(invoice as string)
|
||||
|
||||
return invoiceData as Stripe.Response<StripeInvoice>
|
||||
}
|
||||
})
|
||||
// todo: missing fields
|
||||
// capture_method
|
||||
// add charges
|
||||
// application
|
||||
})
|
||||
})
|
||||
13
integrations/stripe-graphql-js/src/schema/payment-intents.ts
Normal file
13
integrations/stripe-graphql-js/src/schema/payment-intents.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { builder } from '../builder'
|
||||
|
||||
builder.objectType('StripePaymentIntents', {
|
||||
fields: (t) => ({
|
||||
object: t.exposeString('object'),
|
||||
url: t.exposeString('url'),
|
||||
hasMore: t.exposeBoolean('has_more'),
|
||||
data: t.expose('data', {
|
||||
type: ['StripePaymentIntent'],
|
||||
nullable: false
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -7,6 +7,35 @@ import { stripe } from '../utils'
|
||||
|
||||
builder.objectType('Stripe', {
|
||||
fields: (t) => ({
|
||||
connectedAccounts: t.field({
|
||||
type: 'StripeConnectedAccounts',
|
||||
resolve: async (_parent, _, context) => {
|
||||
const { isAdmin } = context
|
||||
|
||||
if (!isAdmin) throw new GraphQLYogaError('Not allowed')
|
||||
|
||||
const connectedAccounts = await stripe.accounts.list()
|
||||
|
||||
return connectedAccounts
|
||||
}
|
||||
}),
|
||||
connectedAccount: t.field({
|
||||
type: 'StripeConnectedAccount',
|
||||
args: {
|
||||
id: t.arg.string({
|
||||
required: true
|
||||
})
|
||||
},
|
||||
resolve: async (_parent, { id }, context) => {
|
||||
const { isAdmin } = context
|
||||
|
||||
if (!isAdmin) throw new GraphQLYogaError('Not allowed')
|
||||
|
||||
const connectedAccount = await stripe.accounts.retrieve(id)
|
||||
|
||||
return connectedAccount
|
||||
}
|
||||
}),
|
||||
customer: t.field({
|
||||
type: 'StripeCustomer',
|
||||
args: {
|
||||
|
||||
@@ -31,6 +31,16 @@ export type StripeInvoice = Stripe.Invoice & {
|
||||
id: string
|
||||
customer: string
|
||||
default_payment_method: StripePaymentMethod | null
|
||||
payment_intent: any
|
||||
}
|
||||
|
||||
export type StripePaymentIntent = Stripe.PaymentIntent & {
|
||||
customer: string
|
||||
}
|
||||
|
||||
export type StripeCharge = Stripe.Charge & {
|
||||
customer: string
|
||||
payment_intent: string | null
|
||||
}
|
||||
|
||||
export type UserHasuraClaims = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Docker image versions used in the cloud
|
||||
hasura: v2.10.1
|
||||
auth: 0.15.0
|
||||
storage: 0.2.5
|
||||
hasura: v2.15.2
|
||||
auth: 0.16.1
|
||||
storage: 0.3.0
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
metadata_directory: metadata
|
||||
services:
|
||||
hasura:
|
||||
image: hasura/graphql-engine:v2.10.1
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.5
|
||||
image: nhost/hasura-storage:0.3.0
|
||||
auth:
|
||||
access_control:
|
||||
email:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NhostClientConstructorParams } from './types'
|
||||
|
||||
const LOCALHOST_REGEX = /^((?<protocol>http[s]?):\/\/)?(?<host>localhost)(:(?<port>\d+))?$/
|
||||
// a port can be a number or a placeholder string with leading and trailing double underscores, f.e. "8080" or "__PLACEHOLDER_NAME__"
|
||||
const LOCALHOST_REGEX = /^((?<protocol>http[s]?):\/\/)?(?<host>localhost)(:(?<port>(\d+|__\w+__)))?$/
|
||||
|
||||
/**
|
||||
* `backendUrl` should now be used only when self-hosting
|
||||
|
||||
@@ -62,5 +62,21 @@ describe('urlFromParams', () => {
|
||||
expect(url).toBe('http://localhost:2001/v1/auth')
|
||||
})
|
||||
})
|
||||
|
||||
describe('"localhost" with a placeholder for custom port', () => {
|
||||
it('should use the specified placeholder and return "http://localhost:__FOO_BAR__/v1/auth"', async () => {
|
||||
const url = urlFromSubdomain({ subdomain: 'localhost:__FOO_BAR__' }, 'auth')
|
||||
|
||||
expect(url).toBe('http://localhost:__FOO_BAR__/v1/auth')
|
||||
})
|
||||
})
|
||||
|
||||
describe('"localhost" with invalid custom port', () => {
|
||||
it('should throw an error"', async () => {
|
||||
expect(() => {
|
||||
urlFromSubdomain({ subdomain: 'localhost:_invalid_FOO_BAR__' }, 'auth')
|
||||
}).toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@@ -638,16 +638,20 @@ importers:
|
||||
examples/serverless-functions:
|
||||
specifiers:
|
||||
'@graphql-yoga/node': ^2.13.13
|
||||
'@nhost/stripe-graphql-js': ^0.0.5
|
||||
'@nhost/stripe-graphql-js': ^0.0.6
|
||||
'@pothos/core': ^3.21.0
|
||||
'@types/express': ^4.17.13
|
||||
cross-fetch: ^3.1.5
|
||||
graphql: 15.7.2
|
||||
nodemailer: ^6.8.0
|
||||
slugify: ^1.6.5
|
||||
dependencies:
|
||||
'@graphql-yoga/node': 2.13.13_graphql@15.7.2
|
||||
'@nhost/stripe-graphql-js': link:../../integrations/stripe-graphql-js
|
||||
'@pothos/core': 3.21.0_graphql@15.7.2
|
||||
cross-fetch: 3.1.5
|
||||
graphql: 15.7.2
|
||||
nodemailer: 6.8.0
|
||||
slugify: 1.6.5
|
||||
devDependencies:
|
||||
'@types/express': 4.17.13
|
||||
@@ -24109,6 +24113,11 @@ packages:
|
||||
/node-releases/2.0.6:
|
||||
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
||||
|
||||
/nodemailer/6.8.0:
|
||||
resolution: {integrity: sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dev: false
|
||||
|
||||
/normalize-package-data/2.5.0:
|
||||
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user