Compare commits

..

62 Commits

Author SHA1 Message Date
Szilárd Dóró
809a2d35f8 Merge pull request #1940 from nhost/changeset-release/main
chore: update versions
2023-05-22 11:51:04 +02:00
github-actions[bot]
e17ec7fce7 chore: update versions 2023-05-22 07:26:27 +00:00
Szilárd Dóró
241175b158 Merge pull request #1935 from nhost/renovate/prettier-plugin-tailwindcss-0.x
chore(deps): update dependency prettier-plugin-tailwindcss to ^0.3.0
2023-05-22 09:14:39 +02:00
Szilárd Dóró
51ceaf2696 Merge pull request #1950 from nhost/fix/nextjs-react-error
fix(nextjs): don't break Next.js when using SignedIn/SignedOut
2023-05-22 09:00:26 +02:00
Szilárd Dóró
490b77cde4 Merge pull request #1949 from nhost/fix/local-infinite-loop
fix(dashboard): don't enter an infinite loop in local mode
2023-05-21 18:14:05 +02:00
Szilárd Dóró
7fea29a8b4 chore: add changeset 2023-05-21 18:13:34 +02:00
Szilárd Dóró
1a34e011ad fix: don't break Next.js when using SignedIn/SignedOut 2023-05-21 18:08:42 +02:00
Szilárd Dóró
395839f449 chore: check service URL when creating client 2023-05-21 16:34:00 +02:00
Szilárd Dóró
399009d66a fix(gql): don't enter an infinite loop when fetching remote app data 2023-05-21 16:31:57 +02:00
Szilárd Dóró
12eb236c4a chore: add changeset 2023-05-19 17:14:22 +02:00
Szilárd Dóró
2218e5cd5b Merge branch 'main' into renovate/prettier-plugin-tailwindcss-0.x 2023-05-19 17:12:36 +02:00
Szilárd Dóró
2dcf1b38c6 Merge pull request #1945 from nhost/fix/404-not-found
fix(dashboard): don't redirect to 404 on project creation
2023-05-19 16:16:13 +02:00
Szilárd Dóró
ad49c92879 fix: trigger project list refetch properly 2023-05-19 15:31:17 +02:00
Szilárd Dóró
13dd57eeb4 Merge pull request #1943 from nhost/fix/deployment-sorting
fix(dashboard): sync deployment sorting
2023-05-19 15:27:44 +02:00
Szilárd Dóró
1345741b11 fix: don't redirect to 404 2023-05-19 15:26:20 +02:00
renovate[bot]
511615f176 chore(deps): update dependency prettier-plugin-tailwindcss to ^0.3.0 2023-05-19 09:51:56 +00:00
Szilárd Dóró
1198c201f1 Merge pull request #1922 from nhost/renovate/turbo-1.x
chore(deps): update dependency turbo to v1.9.8
2023-05-19 11:48:43 +02:00
Szilárd Dóró
f9b81a2ae9 chore: bump version in the Dockerfile as well 2023-05-19 10:34:21 +02:00
Szilárd Dóró
dff0894f37 Revert "fix: sync deployment sorting"
This reverts commit 80f3645d57.
2023-05-19 09:38:04 +02:00
Szilárd Dóró
80f3645d57 fix: sync deployment sorting 2023-05-19 09:27:24 +02:00
Szilárd Dóró
7ddb9a654e fix: sync deployment sorting 2023-05-19 09:10:44 +02:00
Szilárd Dóró
71f3be15d8 Merge pull request #1941 from nhost/chore/under-the-hood-improvements
chore(dashboard): under the hood improvements
2023-05-19 09:03:46 +02:00
Szilárd Dóró
96a9070836 chore: loosen eslint rules 2023-05-18 17:07:15 +02:00
Szilárd Dóró
329e5a91b9 fix: use the correct deployment ordering 2023-05-18 16:01:44 +02:00
Szilárd Dóró
6d559d6e23 chore: under the hood improvements 2023-05-18 11:39:22 +02:00
renovate[bot]
f4f1450d06 chore(deps): update dependency turbo to v1.9.8 2023-05-18 08:22:54 +00:00
Szilárd Dóró
a1eea9df7d Merge pull request #1932 from nhost/renovate/docusaurus-monorepo
fix(deps): update docusaurus monorepo to v2.4.1
2023-05-18 10:16:23 +02:00
Szilárd Dóró
26f2b665e6 Merge pull request #1924 from nhost/feat/pat
feat(hasura-auth-js): add support for personal access tokens
2023-05-18 10:14:12 +02:00
renovate[bot]
224a5cc805 fix(deps): update docusaurus monorepo to v2.4.1 2023-05-17 11:05:58 +00:00
David Barroso
59125b3c77 Merge pull request #1937 from nhost/dbarroso/react-apollo-example-update
chore: update react-apollo example's dependencies
2023-05-17 13:02:09 +02:00
Szilárd Dóró
5b69e3efd8 fix: don't break builds 2023-05-17 10:41:27 +02:00
Szilárd Dóró
c9a444d048 chore: sync playwright versions 2023-05-17 10:21:03 +02:00
David Barroso
2eeac45718 asd 2023-05-17 09:54:44 +02:00
David Barroso
fe8ca8aba6 Update examples/react-apollo/package.json
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-05-17 09:32:13 +02:00
David Barroso
086ee46b08 chore: update react-apollo example's dependencies 2023-05-17 09:21:25 +02:00
Szilárd Dóró
203bc97f51 feat: add useSignInPAT to @nhost/vue 2023-05-16 14:42:55 +02:00
Szilárd Dóró
b24af44aac feat: add support for refreshTokenId 2023-05-16 12:54:51 +02:00
Szilárd Dóró
cdaa6d4e73 chore: remove hash function 2023-05-16 12:16:35 +02:00
Szilárd Dóró
09e2c8f5c7 feat: add hash function 2023-05-16 11:24:52 +02:00
Szilárd Dóró
4581677830 chore: update docs 2023-05-16 09:37:51 +02:00
Szilárd Dóró
7bfa6c9f93 fix: use correct hasura-auth image 2023-05-16 08:59:17 +02:00
Szilárd Dóró
80ef430d70 feat: add personal access token docs 2023-05-16 08:51:22 +02:00
Szilárd Dóró
bad8af0fd1 Merge branch 'main' into feat/pat 2023-05-16 08:35:24 +02:00
Szilárd Dóró
a6120bf366 feat: update API
chore: fix tests
2023-05-12 14:59:07 +02:00
Szilárd Dóró
b23dc058a6 Merge branch 'main' into feat/pat 2023-05-12 14:14:09 +02:00
Szilárd Dóró
ad0dda7493 feat: extend nhost.auth.createPAT with id 2023-05-12 14:09:57 +02:00
Szilárd Dóró
4063507d59 chore: improve CLI example 2023-05-12 14:01:11 +02:00
Szilárd Dóró
85439307a9 feat: finalize CLI example 2023-05-12 09:34:22 +02:00
Szilárd Dóró
0d8baa4065 feat: allow PAT creation through the example 2023-05-12 08:56:56 +02:00
Szilárd Dóró
4bca94425e chore: add useful information to the README 2023-05-11 15:26:15 +02:00
Szilárd Dóró
9f948385c0 feat: improve readability, add book relationship 2023-05-11 15:23:59 +02:00
Szilárd Dóró
294c504b61 chore: update pnpm-lock file 2023-05-11 15:10:28 +02:00
Szilárd Dóró
1469ec2969 Merge branch 'main' into feat/pat 2023-05-11 15:09:31 +02:00
Szilárd Dóró
726c33d1b2 feat: finalize CLI example 2023-05-10 16:41:49 +02:00
Szilárd Dóró
11b9cfbc0d feat: add an example CLI tool to showcase PATs 2023-05-10 15:35:54 +02:00
Szilárd Dóró
79aaa91e67 chore: update hasura-auth version 2023-05-09 17:19:40 +02:00
Szilárd Dóró
df4d24320a chore: update example metadata 2023-05-09 14:33:49 +02:00
Szilárd Dóró
baa3ef794e feat(examples): add PAT example 2023-05-08 17:03:49 +02:00
Szilárd Dóró
da7ffbe523 feat: add useSignInPAT hook 2023-05-08 16:04:00 +02:00
Szilárd Dóró
3ca70554c8 feat: add support for PAT sign in 2023-05-08 14:34:12 +02:00
Szilárd Dóró
077b200510 Merge branch 'main' into feat/pat 2023-05-08 14:04:11 +02:00
Szilárd Dóró
2f220db84a extend machine with PAT sign in 2023-04-27 17:06:26 +02:00
193 changed files with 4797 additions and 2252 deletions

View File

@@ -25,6 +25,7 @@ module.exports = {
'error',
{ allowArrowFunctions: true, allowFunctions: true },
],
'import/no-named-as-default': 'off',
'import/prefer-default-export': 'off',
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
curly: ['error', 'all'],

View File

@@ -1,5 +1,19 @@
# @nhost/dashboard
## 0.16.12
### Patch Changes
- 399009d6: fix(gql): don't enter an infinite loop when fetching remote app data
- 329e5a91: fix(deployments): use the same sorting of deployments everywhere
- 6d559d6e: chore(settings): add under the hood improvements to the settings page
- 12eb236c: chore(deps): bump `prettier-plugin-tailwindcss` to `v0.3.0`
- f9b81a2a: chore(deps): bump `turbo` to `v1.9.8`
- 1345741b: fix(projects): don't redirect to 404 on project creation
- Updated dependencies [7fea29a8]
- @nhost/react-apollo@5.0.23
- @nhost/nextjs@1.13.25
## 0.16.11
### Patch Changes

View File

@@ -3,7 +3,7 @@ RUN apk add --no-cache libc6-compat
RUN apk update
WORKDIR /app
RUN yarn global add turbo@1.9.3
RUN yarn global add turbo@1.9.8
COPY . .
RUN turbo prune --scope="@nhost/dashboard" --docker

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "0.16.11",
"version": "0.16.12",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -15,7 +15,7 @@
"format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.",
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook",
"e2e": "npx playwright@1.31.2 install --with-deps && playwright test"
"e2e": "npx playwright@1.33.0 install --with-deps && playwright test"
},
"dependencies": {
"@apollo/client": "^3.7.10",
@@ -88,7 +88,7 @@
"@graphql-codegen/typescript-operations": "^3.0.0",
"@graphql-codegen/typescript-react-apollo": "^3.3.1",
"@next/bundle-analyzer": "^12.3.1",
"@playwright/test": "^1.31.2",
"@playwright/test": "^1.33.0",
"@storybook/addon-actions": "^6.5.14",
"@storybook/addon-essentials": "^6.5.14",
"@storybook/addon-interactions": "^6.5.14",
@@ -137,7 +137,7 @@
"postcss": "^8.4.19",
"prettier": "^2.7.1",
"prettier-plugin-organize-imports": "^3.2.0",
"prettier-plugin-tailwindcss": "^0.2.0",
"prettier-plugin-tailwindcss": "^0.3.0",
"react-date-fns-hooks": "^0.9.4",
"require-from-string": "^2.0.2",
"snake-case": "^3.0.4",

View File

@@ -114,6 +114,9 @@ export default function AppDeployments(props: AppDeploymentsProps) {
const { deployments } = deploymentPageData || { deployments: [] };
const { deployments: scheduledOrPendingDeployments } =
scheduledOrPendingDeploymentsData || { deployments: [] };
const isDeploymentInProgress = deployments?.some((deployment) =>
['PENDING', 'SCHEDULED'].includes(deployment.deploymentStatus),
);
const latestDeployment = latestDeploymentData?.deployments[0];
const latestLiveDeployment = latestLiveDeploymentData?.deployments[0];
@@ -135,7 +138,10 @@ export default function AppDeployments(props: AppDeploymentsProps) {
deployment={deployment}
isLive={liveDeploymentId === deployment.id}
showRedeploy={latestDeployment.id === deployment.id}
disableRedeploy={scheduledOrPendingDeployments?.length > 0}
disableRedeploy={
scheduledOrPendingDeployments?.length > 0 ||
isDeploymentInProgress
}
/>
{index !== deployments.length - 1 && <Divider component="li" />}

View File

@@ -21,8 +21,6 @@ import { discordAnnounce } from '@/utils/discordAnnounce';
import { getPreviousApplicationState } from '@/utils/getPreviousApplicationState';
import { getApplicationStatusString } from '@/utils/helpers';
import { triggerToast } from '@/utils/toast';
import { updateOwnCache } from '@/utils/updateOwnCache';
import { useApolloClient } from '@apollo/client';
import { useUserData } from '@nhost/nextjs';
import Image from 'next/image';
import { useState } from 'react';
@@ -32,7 +30,11 @@ import { RemoveApplicationModal } from './RemoveApplicationModal';
import { StagingMetadata } from './StagingMetadata';
export default function ApplicationErrored() {
const { currentWorkspace, currentProject } = useCurrentWorkspaceAndProject();
const {
currentWorkspace,
currentProject,
refetch: refetchProject,
} = useCurrentWorkspaceAndProject();
const [changingApplicationStateLoading, setChangingApplicationStateLoading] =
useState(false);
@@ -54,7 +56,6 @@ export default function ApplicationErrored() {
const [showRecreateModal, setShowRecreateModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [insertApp] = useInsertApplicationMutation();
const client = useApolloClient();
const { currentDate } = useCurrentDate();
const user = useUserData();
const isOwner = useIsCurrentUserOwner();
@@ -94,7 +95,7 @@ export default function ApplicationErrored() {
});
discordAnnounce(`Recreating: ${currentProject?.name} (${user.email})`);
triggerToast(`Recreating ${currentProject?.name} `);
await updateOwnCache(client);
await refetchProject();
} catch (e) {
triggerToast(`Error trying to recreate: ${currentProject?.name}`);
}

View File

@@ -18,18 +18,14 @@ import { toast } from 'react-hot-toast';
export default function ApplicationInfo() {
const { currentProject } = useCurrentWorkspaceAndProject();
const [deleteApplication] = useDeleteApplicationMutation({
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
const router = useRouter();
async function handleClickRemove() {
try {
await toast.promise(
deleteApplication({
variables: {
appId: currentProject.id,
},
}),
deleteApplication({ variables: { appId: currentProject.id } }),
{
loading: 'Deleting project...',
success: 'The project has been deleted successfully.',

View File

@@ -35,7 +35,7 @@ export default function ApplicationPaused() {
const [showDeletingModal, setShowDeletingModal] = useState(false);
const [unpauseApplication, { loading: changingApplicationStateLoading }] =
useUnpauseApplicationMutation({
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
const { data, loading } = useGetFreeAndActiveProjectsQuery({

View File

@@ -46,7 +46,7 @@ export function RemoveApplicationModal({
className,
}: RemoveApplicationModalProps) {
const [deleteApplication] = useDeleteApplicationMutation({
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
const [loadingRemove, setLoadingRemove] = useState(false);
const { currentProject } = useCurrentWorkspaceAndProject();

View File

@@ -8,8 +8,6 @@ import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text';
import { discordAnnounce } from '@/utils/discordAnnounce';
import { triggerToast } from '@/utils/toast';
import { updateOwnCache } from '@/utils/updateOwnCache';
import { useApolloClient } from '@apollo/client';
import { ErrorBoundary } from 'react-error-boundary';
import { useFormContext } from 'react-hook-form';
import { RepoAndBranch } from './RepoAndBranch';
@@ -27,12 +25,11 @@ export function EditRepositorySettingsModal({
const isNotCompleted = !watch('productionBranch') || !watch('repoBaseFolder');
const { closeAlertDialog } = useDialog();
const { currentProject } = useCurrentWorkspaceAndProject();
const { currentProject, refetch: refetchProject } =
useCurrentWorkspaceAndProject();
const [updateApp, { loading }] = useUpdateApplicationMutation();
const client = useApolloClient();
const handleEditGitHubIntegration = async (
data: EditRepositorySettingsFormData,
) => {
@@ -60,7 +57,8 @@ export function EditRepositorySettingsModal({
});
}
await updateOwnCache(client);
await refetchProject();
if (close) {
close();
} else {

View File

@@ -5,16 +5,20 @@ import { Avatar } from '@/ui/Avatar';
import type { DeploymentStatus } from '@/ui/StatusCircle';
import { StatusCircle } from '@/ui/StatusCircle';
import Button from '@/ui/v2/Button';
import Chip from '@/ui/v2/Chip';
import { Chip } from '@/ui/v2/Chip';
import { ListItem } from '@/ui/v2/ListItem';
import Tooltip from '@/ui/v2/Tooltip';
import { Tooltip } from '@/ui/v2/Tooltip';
import ArrowCounterclockwiseIcon from '@/ui/v2/icons/ArrowCounterclockwiseIcon';
import ChevronRightIcon from '@/ui/v2/icons/ChevronRightIcon';
import type { DeploymentRowFragment } from '@/utils/__generated__/graphql';
import { useInsertDeploymentMutation } from '@/utils/__generated__/graphql';
import {
GetAllWorkspacesAndProjectsDocument,
useInsertDeploymentMutation,
} from '@/utils/__generated__/graphql';
import getServerError from '@/utils/settings/getServerError';
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
import { formatDistanceToNowStrict, parseISO } from 'date-fns';
import type { MouseEvent } from 'react';
import { toast } from 'react-hot-toast';
import { twMerge } from 'tailwind-merge';
@@ -52,9 +56,39 @@ export default function DeploymentListItem({
})
: '';
const [insertDeployment, { loading }] = useInsertDeploymentMutation();
const [insertDeployment, { loading }] = useInsertDeploymentMutation({
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
const { commitMessage } = deployment;
async function redeployDeployment(event: MouseEvent<HTMLButtonElement>) {
event.stopPropagation();
event.preventDefault();
const insertDeploymentPromise = insertDeployment({
variables: {
object: {
appId: currentProject?.id,
commitMessage: deployment.commitMessage,
commitSHA: deployment.commitSHA,
commitUserAvatarUrl: deployment.commitUserAvatarUrl,
commitUserName: deployment.commitUserName,
deploymentStatus: 'SCHEDULED',
},
},
});
await toast.promise(
insertDeploymentPromise,
{
loading: 'Scheduling deployment...',
success: 'Deployment has been scheduled successfully.',
error: getServerError('An error occurred when scheduling deployment.'),
},
getToastStyleProps(),
);
}
return (
<ListItem.Root>
<ListItem.Button
@@ -88,7 +122,7 @@ export default function DeploymentListItem({
{showRedeploy && (
<Tooltip
title={
!disableRedeploy && !loading
disableRedeploy || loading
? 'Deployments cannot be re-triggered when a deployment is in progress.'
: ''
}
@@ -100,35 +134,7 @@ export default function DeploymentListItem({
size="small"
color="secondary"
variant="outlined"
onClick={async (event) => {
event.stopPropagation();
event.preventDefault();
const insertDeploymentPromise = insertDeployment({
variables: {
object: {
appId: currentProject?.id,
commitMessage: deployment.commitMessage,
commitSHA: deployment.commitSHA,
commitUserAvatarUrl: deployment.commitUserAvatarUrl,
commitUserName: deployment.commitUserName,
deploymentStatus: 'SCHEDULED',
},
},
});
await toast.promise(
insertDeploymentPromise,
{
loading: 'Scheduling deployment...',
success: 'Deployment has been scheduled successfully.',
error: getServerError(
'An error occurred when scheduling deployment.',
),
},
getToastStyleProps(),
);
}}
onClick={redeployDeployment}
startIcon={
<ArrowCounterclockwiseIcon className={twMerge('h-4 w-4')} />
}

View File

@@ -10,7 +10,6 @@ import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text';
import { nhost } from '@/utils/nhost';
import { triggerToast } from '@/utils/toast';
import { updateOwnCache } from '@/utils/updateOwnCache';
import { useApolloClient } from '@apollo/client';
import { alpha } from '@mui/system';
import { useUserData } from '@nhost/nextjs';
@@ -28,13 +27,18 @@ export function InviteAnnounce() {
useSubmitState();
// @FIX: We probably don't want to poll every ten seconds for possible invites. (We can change later depending on how it works in production.) Maybe just on the workspace page?
const { data, loading, error, refetch, startPolling } =
useGetWorkspaceMemberInvitesToManageQuery({
variables: {
userId: user?.id,
},
skip: !isPlatform || !user,
});
const {
data,
loading,
error,
refetch: refetchInvitations,
startPolling,
} = useGetWorkspaceMemberInvitesToManageQuery({
variables: {
userId: user?.id,
},
skip: !isPlatform || !user,
});
useEffect(() => {
startPolling(15000);
@@ -79,9 +83,14 @@ export function InviteAnnounce() {
});
}
await updateOwnCache(client);
await client.refetchQueries({
include: [
GetAllWorkspacesAndProjectsDocument,
GetWorkspaceMemberInvitesToManageDocument,
],
});
await router.push(`/${invite.workspace.slug}`);
await refetch();
await refetchInvitations();
triggerToast('Workspace invite accepted');
return setSubmitState({
error: null,

View File

@@ -207,19 +207,6 @@ export default function WorkspaceAndProjectList({
</div>
))}
</Box>
<Text className="font-medium" color="secondary">
Looking for your old apps? They&apos;re still on{' '}
<Link
href="https://console.nhost.io"
target="_blank"
rel="noreferrer"
underline="always"
>
console.nhost.io
</Link>{' '}
during this beta.
</Text>
</Box>
);
}

View File

@@ -119,6 +119,9 @@ function OverviewDeploymentList() {
const liveDeploymentId = getLastLiveDeployment(deployments);
const { deployments: scheduledOrPendingDeployments } =
scheduledOrPendingDeploymentsData || { deployments: [] };
const isDeploymentInProgress = deployments?.some((deployment) =>
['PENDING', 'SCHEDULED'].includes(deployment.deploymentStatus),
);
return (
<List
@@ -131,7 +134,10 @@ function OverviewDeploymentList() {
deployment={deployment}
isLive={deployment.id === liveDeploymentId}
showRedeploy={index === 0}
disableRedeploy={scheduledOrPendingDeployments?.length > 0}
disableRedeploy={
scheduledOrPendingDeployments?.length > 0 ||
isDeploymentInProgress
}
/>
{index !== deployments.length - 1 && <Divider component="li" />}

View File

@@ -1,2 +0,0 @@
export * from './BaseDirectorySettings';
export { default } from './BaseDirectorySettings';

View File

@@ -1,2 +0,0 @@
export * from './DeploymentBranchSettings';
export { default } from './DeploymentBranchSettings';

View File

@@ -1,2 +1,2 @@
export * from './Chip';
export { default } from './Chip';
export { default as Chip, default } from './Chip';

View File

@@ -1,4 +1,4 @@
export * from './Tooltip';
export { default } from './Tooltip';
export { default as Tooltip, default } from './Tooltip';
export * from './useTooltip';
export { default as useTooltip } from './useTooltip';

View File

@@ -50,7 +50,7 @@ export default function useCurrentWorkspaceAndProject(): UseCurrentWorkspaceAndP
isFetching,
refetch,
} = useQuery(
['currentWorkspaceAndProject', workspaceSlug],
['currentWorkspaceAndProject', workspaceSlug, appSlug],
() =>
client.graphql.request<{
workspaces: Workspace[];

View File

@@ -0,0 +1,2 @@
export * from './BaseDirectorySettings';
export { default as BaseDirectorySettings } from './BaseDirectorySettings';

View File

@@ -0,0 +1,2 @@
export * from './DeploymentBranchSettings';
export { default as DeploymentBranchSettings } from './DeploymentBranchSettings';

View File

@@ -0,0 +1,86 @@
import useGitHubModal from '@/components/applications/github/useGitHubModal';
import { useDialog } from '@/components/common/DialogProvider';
import GithubIcon from '@/components/icons/GithubIcon';
import SettingsContainer from '@/components/settings/SettingsContainer';
import { useUI } from '@/context/UIContext';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import Box from '@/ui/v2/Box';
import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text/Text';
import { useUpdateApplicationMutation } from '@/utils/__generated__/graphql';
import { triggerToast } from '@/utils/toast';
export default function GitConnectionSettings() {
const { maintenanceActive } = useUI();
const { currentProject, refetch } = useCurrentWorkspaceAndProject();
const [updateApp] = useUpdateApplicationMutation();
const { openAlertDialog } = useDialog();
const { openGitHubModal } = useGitHubModal();
function handleConnect() {
openAlertDialog({
title: 'Disconnect GitHub Repository',
payload: (
<p>
Are you sure you want to disconnect{' '}
<b>{currentProject.githubRepository.fullName}</b>?
</p>
),
props: {
primaryButtonText: 'Disconnect GitHub Repository',
primaryButtonColor: 'error',
onPrimaryAction: async () => {
await updateApp({
variables: {
appId: currentProject.id,
app: {
githubRepositoryId: null,
},
},
});
triggerToast(
`Successfully disconnected GitHub repository from ${currentProject.name}.`,
);
await refetch();
},
},
});
}
return (
<SettingsContainer
title="Git Repository"
description="Create Deployments for commits pushed to your Git repository."
docsLink="https://docs.nhost.io/platform/github-integration"
slotProps={{ submitButton: { className: 'hidden' } }}
className="grid grid-cols-5"
>
{!currentProject.githubRepository ? (
<Button
onClick={openGitHubModal}
className="col-span-5 grid grid-flow-col gap-1.5 xs:col-span-3 lg:col-span-2"
startIcon={<GithubIcon className="h-4 w-4 self-center" />}
disabled={maintenanceActive}
>
Connect to GitHub
</Button>
) : (
<Box className="col-span-5 flex flex-row place-content-between items-center rounded-lg border px-4 py-4">
<div className="ml-2 flex flex-row">
<GithubIcon className="mr-1.5 h-7 w-7 self-center" />
<Text className="self-center font-normal">
{currentProject.githubRepository.fullName}
</Text>
</div>
<Button
disabled={maintenanceActive}
variant="borderless"
onClick={handleConnect}
>
Disconnect
</Button>
</Box>
)}
</SettingsContainer>
);
}

View File

@@ -0,0 +1 @@
export { default as GitConnectionSettings } from './GitConnectionSettings';

View File

@@ -20,7 +20,7 @@ subscription ScheduledOrPendingDeploymentsSub($appId: uuid!) {
subscription LatestLiveDeploymentSub($appId: uuid!) {
deployments(
where: { deploymentStatus: { _eq: "DEPLOYED" }, appId: { _eq: $appId } }
order_by: { deploymentEndedAt: desc }
order_by: { deploymentStartedAt: desc }
limit: 1
offset: 0
) {

View File

@@ -48,7 +48,7 @@ fragment Project on apps {
githubRepository {
fullName
}
deployments(limit: 4, order_by: { deploymentEndedAt: desc }) {
deployments(limit: 4, order_by: { deploymentStartedAt: desc }) {
id
commitSHA
commitMessage

View File

@@ -10,20 +10,21 @@ import { useMemo } from 'react';
*/
export function useRemoteApplicationGQLClient() {
const { currentProject, loading } = useCurrentWorkspaceAndProject();
const serviceUrl = generateAppServiceUrl(
currentProject?.subdomain,
currentProject?.region,
'graphql',
);
const userApplicationClient = useMemo(() => {
if (loading) {
if (loading || !serviceUrl) {
return new ApolloClient({ cache: new InMemoryCache() });
}
return new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: generateAppServiceUrl(
currentProject?.subdomain,
currentProject?.region,
'graphql',
),
uri: serviceUrl,
headers: {
'x-hasura-admin-secret':
process.env.NEXT_PUBLIC_ENV === 'dev'
@@ -32,12 +33,7 @@ export function useRemoteApplicationGQLClient() {
},
}),
});
}, [
loading,
currentProject?.subdomain,
currentProject?.region,
currentProject?.config?.hasura.adminSecret,
]);
}, [loading, serviceUrl, currentProject?.config?.hasura.adminSecret]);
return userApplicationClient;
}

View File

@@ -49,11 +49,11 @@ export default function SettingsGeneralPage() {
const [updateApp] = useUpdateApplicationMutation();
const [pauseApplication] = usePauseApplicationMutation({
variables: { appId: currentProject?.id },
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
const [deleteApplication] = useDeleteApplicationMutation({
variables: { appId: currentProject?.id },
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
const router = useRouter();
const { maintenanceActive } = useUI();

View File

@@ -1,104 +1,23 @@
import useGitHubModal from '@/components/applications/github/useGitHubModal';
import { useDialog } from '@/components/common/DialogProvider';
import GithubIcon from '@/components/icons/GithubIcon';
import Container from '@/components/layout/Container';
import SettingsContainer from '@/components/settings/SettingsContainer';
import SettingsLayout from '@/components/settings/SettingsLayout';
import BaseDirectorySettings from '@/components/settings/git/BaseDirectorySettings';
import DeploymentBranchSettings from '@/components/settings/git/DeploymentBranchSettings';
import { useUI } from '@/context/UIContext';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useUpdateApplicationMutation } from '@/generated/graphql';
import Box from '@/ui/v2/Box';
import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text';
import { triggerToast } from '@/utils/toast';
import { updateOwnCache } from '@/utils/updateOwnCache';
import { useApolloClient } from '@apollo/client';
import { BaseDirectorySettings } from '@/features/projects/settings/git/components/BaseDirectorySettings';
import { DeploymentBranchSettings } from '@/features/projects/settings/git/components/DeploymentBranchSettings';
import { GitConnectionSettings } from '@/features/projects/settings/git/components/GitConnectionSettings';
import type { ReactElement } from 'react';
export default function SettingsGitPage() {
const { maintenanceActive } = useUI();
const { currentProject } = useCurrentWorkspaceAndProject();
const { openGitHubModal } = useGitHubModal();
const { openAlertDialog } = useDialog();
const client = useApolloClient();
const [updateApp] = useUpdateApplicationMutation();
export default function GitSettingsPage() {
return (
<Container
className="grid max-w-5xl grid-flow-row gap-y-6 bg-transparent"
rootClassName="bg-transparent"
>
<SettingsContainer
title="Git Repository"
description="Create Deployments for commits pushed to your Git repository."
docsLink="https://docs.nhost.io/platform/github-integration"
slotProps={{ submitButton: { className: 'hidden' } }}
className="grid grid-cols-5"
>
{!currentProject.githubRepository ? (
<Button
onClick={openGitHubModal}
className="col-span-5 grid grid-flow-col gap-1.5 xs:col-span-3 lg:col-span-2"
startIcon={<GithubIcon className="h-4 w-4 self-center" />}
disabled={maintenanceActive}
>
Connect to GitHub
</Button>
) : (
<Box className="col-span-5 flex flex-row place-content-between items-center rounded-lg border px-4 py-4">
<div className="ml-2 flex flex-row">
<GithubIcon className="mr-1.5 h-7 w-7 self-center" />
<Text className="self-center font-normal">
{currentProject.githubRepository.fullName}
</Text>
</div>
<Button
disabled={maintenanceActive}
variant="borderless"
onClick={() => {
openAlertDialog({
title: 'Disconnect GitHub Repository',
payload: (
<p>
Are you sure you want to disconnect{' '}
<b>{currentProject.githubRepository.fullName}</b>?
</p>
),
props: {
primaryButtonText: 'Disconnect GitHub Repository',
primaryButtonColor: 'error',
onPrimaryAction: async () => {
await updateApp({
variables: {
appId: currentProject.id,
app: {
githubRepositoryId: null,
},
},
});
triggerToast(
`Successfully disconnected GitHub repository from ${currentProject.name}.`,
);
await updateOwnCache(client);
},
},
});
}}
>
Disconnect
</Button>
</Box>
)}
</SettingsContainer>
<GitConnectionSettings />
<DeploymentBranchSettings />
<BaseDirectorySettings />
</Container>
);
}
SettingsGitPage.getLayout = function getLayout(page: ReactElement) {
GitSettingsPage.getLayout = function getLayout(page: ReactElement) {
return <SettingsLayout>{page}</SettingsLayout>;
};

View File

@@ -81,12 +81,6 @@ export default function IndexPage() {
</Button>
</NavLink>
</div>
<div>
<Text className="mt-9 opacity-70" sx={{ color: 'common.white' }}>
Looking for your old apps? They&apos;re still on
console.nhost.io during this beta.
</Text>
</div>
</div>
</Box>

View File

@@ -106,7 +106,7 @@ export function NewProjectPageContent({
const { submitState, setSubmitState } = useSubmitState();
const [insertApp] = useInsertApplicationMutation({
refetchQueries: [GetAllWorkspacesAndProjectsDocument],
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
});
// options

View File

@@ -3563,6 +3563,183 @@ export type AuthProviders_Updates = {
where: AuthProviders_Bool_Exp;
};
/** columns and relationships of "auth.refresh_token_types" */
export type AuthRefreshTokenTypes = {
__typename?: 'authRefreshTokenTypes';
comment?: Maybe<Scalars['String']>;
/** An array relationship */
refreshTokens: Array<AuthRefreshTokens>;
/** An aggregate relationship */
refreshTokens_aggregate: AuthRefreshTokens_Aggregate;
value: Scalars['String'];
};
/** columns and relationships of "auth.refresh_token_types" */
export type AuthRefreshTokenTypesRefreshTokensArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokens_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<AuthRefreshTokens_Order_By>>;
where?: InputMaybe<AuthRefreshTokens_Bool_Exp>;
};
/** columns and relationships of "auth.refresh_token_types" */
export type AuthRefreshTokenTypesRefreshTokens_AggregateArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokens_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<AuthRefreshTokens_Order_By>>;
where?: InputMaybe<AuthRefreshTokens_Bool_Exp>;
};
/** aggregated selection of "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_Aggregate = {
__typename?: 'authRefreshTokenTypes_aggregate';
aggregate?: Maybe<AuthRefreshTokenTypes_Aggregate_Fields>;
nodes: Array<AuthRefreshTokenTypes>;
};
/** aggregate fields of "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_Aggregate_Fields = {
__typename?: 'authRefreshTokenTypes_aggregate_fields';
count: Scalars['Int'];
max?: Maybe<AuthRefreshTokenTypes_Max_Fields>;
min?: Maybe<AuthRefreshTokenTypes_Min_Fields>;
};
/** aggregate fields of "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_Aggregate_FieldsCountArgs = {
columns?: InputMaybe<Array<AuthRefreshTokenTypes_Select_Column>>;
distinct?: InputMaybe<Scalars['Boolean']>;
};
/** Boolean expression to filter rows from the table "auth.refresh_token_types". All fields are combined with a logical 'AND'. */
export type AuthRefreshTokenTypes_Bool_Exp = {
_and?: InputMaybe<Array<AuthRefreshTokenTypes_Bool_Exp>>;
_not?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
_or?: InputMaybe<Array<AuthRefreshTokenTypes_Bool_Exp>>;
comment?: InputMaybe<String_Comparison_Exp>;
refreshTokens?: InputMaybe<AuthRefreshTokens_Bool_Exp>;
refreshTokens_aggregate?: InputMaybe<AuthRefreshTokens_Aggregate_Bool_Exp>;
value?: InputMaybe<String_Comparison_Exp>;
};
/** unique or primary key constraints on table "auth.refresh_token_types" */
export enum AuthRefreshTokenTypes_Constraint {
/** unique or primary key constraint on columns "value" */
RefreshTokenTypesPkey = 'refresh_token_types_pkey'
}
export enum AuthRefreshTokenTypes_Enum {
/** Personal access token */
Pat = 'pat',
/** Regular refresh token */
Regular = 'regular'
}
/** Boolean expression to compare columns of type "authRefreshTokenTypes_enum". All fields are combined with logical 'AND'. */
export type AuthRefreshTokenTypes_Enum_Comparison_Exp = {
_eq?: InputMaybe<AuthRefreshTokenTypes_Enum>;
_in?: InputMaybe<Array<AuthRefreshTokenTypes_Enum>>;
_is_null?: InputMaybe<Scalars['Boolean']>;
_neq?: InputMaybe<AuthRefreshTokenTypes_Enum>;
_nin?: InputMaybe<Array<AuthRefreshTokenTypes_Enum>>;
};
/** input type for inserting data into table "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_Insert_Input = {
comment?: InputMaybe<Scalars['String']>;
refreshTokens?: InputMaybe<AuthRefreshTokens_Arr_Rel_Insert_Input>;
value?: InputMaybe<Scalars['String']>;
};
/** aggregate max on columns */
export type AuthRefreshTokenTypes_Max_Fields = {
__typename?: 'authRefreshTokenTypes_max_fields';
comment?: Maybe<Scalars['String']>;
value?: Maybe<Scalars['String']>;
};
/** aggregate min on columns */
export type AuthRefreshTokenTypes_Min_Fields = {
__typename?: 'authRefreshTokenTypes_min_fields';
comment?: Maybe<Scalars['String']>;
value?: Maybe<Scalars['String']>;
};
/** response of any mutation on the table "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_Mutation_Response = {
__typename?: 'authRefreshTokenTypes_mutation_response';
/** number of rows affected by the mutation */
affected_rows: Scalars['Int'];
/** data from the rows affected by the mutation */
returning: Array<AuthRefreshTokenTypes>;
};
/** on_conflict condition type for table "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_On_Conflict = {
constraint: AuthRefreshTokenTypes_Constraint;
update_columns?: Array<AuthRefreshTokenTypes_Update_Column>;
where?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
};
/** Ordering options when selecting data from "auth.refresh_token_types". */
export type AuthRefreshTokenTypes_Order_By = {
comment?: InputMaybe<Order_By>;
refreshTokens_aggregate?: InputMaybe<AuthRefreshTokens_Aggregate_Order_By>;
value?: InputMaybe<Order_By>;
};
/** primary key columns input for table: auth.refresh_token_types */
export type AuthRefreshTokenTypes_Pk_Columns_Input = {
value: Scalars['String'];
};
/** select columns of table "auth.refresh_token_types" */
export enum AuthRefreshTokenTypes_Select_Column {
/** column name */
Comment = 'comment',
/** column name */
Value = 'value'
}
/** input type for updating data in table "auth.refresh_token_types" */
export type AuthRefreshTokenTypes_Set_Input = {
comment?: InputMaybe<Scalars['String']>;
value?: InputMaybe<Scalars['String']>;
};
/** Streaming cursor of the table "authRefreshTokenTypes" */
export type AuthRefreshTokenTypes_Stream_Cursor_Input = {
/** Stream column input with initial value */
initial_value: AuthRefreshTokenTypes_Stream_Cursor_Value_Input;
/** cursor ordering */
ordering?: InputMaybe<Cursor_Ordering>;
};
/** Initial value of the column from where the streaming should start */
export type AuthRefreshTokenTypes_Stream_Cursor_Value_Input = {
comment?: InputMaybe<Scalars['String']>;
value?: InputMaybe<Scalars['String']>;
};
/** update columns of table "auth.refresh_token_types" */
export enum AuthRefreshTokenTypes_Update_Column {
/** column name */
Comment = 'comment',
/** column name */
Value = 'value'
}
export type AuthRefreshTokenTypes_Updates = {
/** sets the columns of the filtered rows to the given values */
_set?: InputMaybe<AuthRefreshTokenTypes_Set_Input>;
where: AuthRefreshTokenTypes_Bool_Exp;
};
/** User refresh tokens. Hasura auth uses them to rotate new access tokens as long as the refresh token is not expired. Don't modify its structure as Hasura Auth relies on it to function properly. */
export type AuthRefreshTokens = {
__typename?: 'authRefreshTokens';
@@ -3570,9 +3747,9 @@ export type AuthRefreshTokens = {
expiresAt: Scalars['timestamptz'];
id: Scalars['uuid'];
metadata?: Maybe<Scalars['jsonb']>;
refreshToken?: Maybe<Scalars['uuid']>;
refreshTokenHash?: Maybe<Scalars['String']>;
type: Scalars['String'];
refresh_token?: Maybe<Scalars['uuid']>;
type: AuthRefreshTokenTypes_Enum;
/** An object relationship */
user: Users;
userId: Scalars['uuid'];
@@ -3645,9 +3822,9 @@ export type AuthRefreshTokens_Bool_Exp = {
expiresAt?: InputMaybe<Timestamptz_Comparison_Exp>;
id?: InputMaybe<Uuid_Comparison_Exp>;
metadata?: InputMaybe<Jsonb_Comparison_Exp>;
refreshToken?: InputMaybe<Uuid_Comparison_Exp>;
refreshTokenHash?: InputMaybe<String_Comparison_Exp>;
type?: InputMaybe<String_Comparison_Exp>;
refresh_token?: InputMaybe<Uuid_Comparison_Exp>;
type?: InputMaybe<AuthRefreshTokenTypes_Enum_Comparison_Exp>;
user?: InputMaybe<Users_Bool_Exp>;
userId?: InputMaybe<Uuid_Comparison_Exp>;
};
@@ -3679,9 +3856,9 @@ export type AuthRefreshTokens_Insert_Input = {
expiresAt?: InputMaybe<Scalars['timestamptz']>;
id?: InputMaybe<Scalars['uuid']>;
metadata?: InputMaybe<Scalars['jsonb']>;
refreshToken?: InputMaybe<Scalars['uuid']>;
refreshTokenHash?: InputMaybe<Scalars['String']>;
type?: InputMaybe<Scalars['String']>;
refresh_token?: InputMaybe<Scalars['uuid']>;
type?: InputMaybe<AuthRefreshTokenTypes_Enum>;
user?: InputMaybe<Users_Obj_Rel_Insert_Input>;
userId?: InputMaybe<Scalars['uuid']>;
};
@@ -3692,9 +3869,8 @@ export type AuthRefreshTokens_Max_Fields = {
createdAt?: Maybe<Scalars['timestamptz']>;
expiresAt?: Maybe<Scalars['timestamptz']>;
id?: Maybe<Scalars['uuid']>;
refreshToken?: Maybe<Scalars['uuid']>;
refreshTokenHash?: Maybe<Scalars['String']>;
type?: Maybe<Scalars['String']>;
refresh_token?: Maybe<Scalars['uuid']>;
userId?: Maybe<Scalars['uuid']>;
};
@@ -3703,9 +3879,8 @@ export type AuthRefreshTokens_Max_Order_By = {
createdAt?: InputMaybe<Order_By>;
expiresAt?: InputMaybe<Order_By>;
id?: InputMaybe<Order_By>;
refreshToken?: InputMaybe<Order_By>;
refreshTokenHash?: InputMaybe<Order_By>;
type?: InputMaybe<Order_By>;
refresh_token?: InputMaybe<Order_By>;
userId?: InputMaybe<Order_By>;
};
@@ -3715,9 +3890,8 @@ export type AuthRefreshTokens_Min_Fields = {
createdAt?: Maybe<Scalars['timestamptz']>;
expiresAt?: Maybe<Scalars['timestamptz']>;
id?: Maybe<Scalars['uuid']>;
refreshToken?: Maybe<Scalars['uuid']>;
refreshTokenHash?: Maybe<Scalars['String']>;
type?: Maybe<Scalars['String']>;
refresh_token?: Maybe<Scalars['uuid']>;
userId?: Maybe<Scalars['uuid']>;
};
@@ -3726,9 +3900,8 @@ export type AuthRefreshTokens_Min_Order_By = {
createdAt?: InputMaybe<Order_By>;
expiresAt?: InputMaybe<Order_By>;
id?: InputMaybe<Order_By>;
refreshToken?: InputMaybe<Order_By>;
refreshTokenHash?: InputMaybe<Order_By>;
type?: InputMaybe<Order_By>;
refresh_token?: InputMaybe<Order_By>;
userId?: InputMaybe<Order_By>;
};
@@ -3754,8 +3927,8 @@ export type AuthRefreshTokens_Order_By = {
expiresAt?: InputMaybe<Order_By>;
id?: InputMaybe<Order_By>;
metadata?: InputMaybe<Order_By>;
refreshToken?: InputMaybe<Order_By>;
refreshTokenHash?: InputMaybe<Order_By>;
refresh_token?: InputMaybe<Order_By>;
type?: InputMaybe<Order_By>;
user?: InputMaybe<Users_Order_By>;
userId?: InputMaybe<Order_By>;
@@ -3782,10 +3955,10 @@ export enum AuthRefreshTokens_Select_Column {
/** column name */
Metadata = 'metadata',
/** column name */
RefreshToken = 'refreshToken',
/** column name */
RefreshTokenHash = 'refreshTokenHash',
/** column name */
RefreshToken = 'refresh_token',
/** column name */
Type = 'type',
/** column name */
UserId = 'userId'
@@ -3797,9 +3970,9 @@ export type AuthRefreshTokens_Set_Input = {
expiresAt?: InputMaybe<Scalars['timestamptz']>;
id?: InputMaybe<Scalars['uuid']>;
metadata?: InputMaybe<Scalars['jsonb']>;
refreshToken?: InputMaybe<Scalars['uuid']>;
refreshTokenHash?: InputMaybe<Scalars['String']>;
type?: InputMaybe<Scalars['String']>;
refresh_token?: InputMaybe<Scalars['uuid']>;
type?: InputMaybe<AuthRefreshTokenTypes_Enum>;
userId?: InputMaybe<Scalars['uuid']>;
};
@@ -3817,9 +3990,9 @@ export type AuthRefreshTokens_Stream_Cursor_Value_Input = {
expiresAt?: InputMaybe<Scalars['timestamptz']>;
id?: InputMaybe<Scalars['uuid']>;
metadata?: InputMaybe<Scalars['jsonb']>;
refreshToken?: InputMaybe<Scalars['uuid']>;
refreshTokenHash?: InputMaybe<Scalars['String']>;
type?: InputMaybe<Scalars['String']>;
refresh_token?: InputMaybe<Scalars['uuid']>;
type?: InputMaybe<AuthRefreshTokenTypes_Enum>;
userId?: InputMaybe<Scalars['uuid']>;
};
@@ -3834,10 +4007,10 @@ export enum AuthRefreshTokens_Update_Column {
/** column name */
Metadata = 'metadata',
/** column name */
RefreshToken = 'refreshToken',
/** column name */
RefreshTokenHash = 'refreshTokenHash',
/** column name */
RefreshToken = 'refresh_token',
/** column name */
Type = 'type',
/** column name */
UserId = 'userId'
@@ -9883,6 +10056,10 @@ export type Mutation_Root = {
deleteAuthProviders?: Maybe<AuthProviders_Mutation_Response>;
/** delete single row from the table: "auth.refresh_tokens" */
deleteAuthRefreshToken?: Maybe<AuthRefreshTokens>;
/** delete single row from the table: "auth.refresh_token_types" */
deleteAuthRefreshTokenType?: Maybe<AuthRefreshTokenTypes>;
/** delete data from the table: "auth.refresh_token_types" */
deleteAuthRefreshTokenTypes?: Maybe<AuthRefreshTokenTypes_Mutation_Response>;
/** delete data from the table: "auth.refresh_tokens" */
deleteAuthRefreshTokens?: Maybe<AuthRefreshTokens_Mutation_Response>;
/** delete single row from the table: "auth.roles" */
@@ -10013,6 +10190,10 @@ export type Mutation_Root = {
insertAuthProviders?: Maybe<AuthProviders_Mutation_Response>;
/** insert a single row into the table: "auth.refresh_tokens" */
insertAuthRefreshToken?: Maybe<AuthRefreshTokens>;
/** insert a single row into the table: "auth.refresh_token_types" */
insertAuthRefreshTokenType?: Maybe<AuthRefreshTokenTypes>;
/** insert data into the table: "auth.refresh_token_types" */
insertAuthRefreshTokenTypes?: Maybe<AuthRefreshTokenTypes_Mutation_Response>;
/** insert data into the table: "auth.refresh_tokens" */
insertAuthRefreshTokens?: Maybe<AuthRefreshTokens_Mutation_Response>;
/** insert a single row into the table: "auth.roles" */
@@ -10148,6 +10329,10 @@ export type Mutation_Root = {
updateAuthProviders?: Maybe<AuthProviders_Mutation_Response>;
/** update single row of the table: "auth.refresh_tokens" */
updateAuthRefreshToken?: Maybe<AuthRefreshTokens>;
/** update single row of the table: "auth.refresh_token_types" */
updateAuthRefreshTokenType?: Maybe<AuthRefreshTokenTypes>;
/** update data of the table: "auth.refresh_token_types" */
updateAuthRefreshTokenTypes?: Maybe<AuthRefreshTokenTypes_Mutation_Response>;
/** update data of the table: "auth.refresh_tokens" */
updateAuthRefreshTokens?: Maybe<AuthRefreshTokens_Mutation_Response>;
/** update single row of the table: "auth.roles" */
@@ -10247,6 +10432,8 @@ export type Mutation_Root = {
update_authProviderRequests_many?: Maybe<Array<Maybe<AuthProviderRequests_Mutation_Response>>>;
/** update multiples rows of table: "auth.providers" */
update_authProviders_many?: Maybe<Array<Maybe<AuthProviders_Mutation_Response>>>;
/** update multiples rows of table: "auth.refresh_token_types" */
update_authRefreshTokenTypes_many?: Maybe<Array<Maybe<AuthRefreshTokenTypes_Mutation_Response>>>;
/** update multiples rows of table: "auth.refresh_tokens" */
update_authRefreshTokens_many?: Maybe<Array<Maybe<AuthRefreshTokens_Mutation_Response>>>;
/** update multiples rows of table: "auth.roles" */
@@ -10479,6 +10666,18 @@ export type Mutation_RootDeleteAuthRefreshTokenArgs = {
};
/** mutation root */
export type Mutation_RootDeleteAuthRefreshTokenTypeArgs = {
value: Scalars['String'];
};
/** mutation root */
export type Mutation_RootDeleteAuthRefreshTokenTypesArgs = {
where: AuthRefreshTokenTypes_Bool_Exp;
};
/** mutation root */
export type Mutation_RootDeleteAuthRefreshTokensArgs = {
where: AuthRefreshTokens_Bool_Exp;
@@ -10887,6 +11086,20 @@ export type Mutation_RootInsertAuthRefreshTokenArgs = {
};
/** mutation root */
export type Mutation_RootInsertAuthRefreshTokenTypeArgs = {
object: AuthRefreshTokenTypes_Insert_Input;
on_conflict?: InputMaybe<AuthRefreshTokenTypes_On_Conflict>;
};
/** mutation root */
export type Mutation_RootInsertAuthRefreshTokenTypesArgs = {
objects: Array<AuthRefreshTokenTypes_Insert_Input>;
on_conflict?: InputMaybe<AuthRefreshTokenTypes_On_Conflict>;
};
/** mutation root */
export type Mutation_RootInsertAuthRefreshTokensArgs = {
objects: Array<AuthRefreshTokens_Insert_Input>;
@@ -11410,6 +11623,20 @@ export type Mutation_RootUpdateAuthRefreshTokenArgs = {
};
/** mutation root */
export type Mutation_RootUpdateAuthRefreshTokenTypeArgs = {
_set?: InputMaybe<AuthRefreshTokenTypes_Set_Input>;
pk_columns: AuthRefreshTokenTypes_Pk_Columns_Input;
};
/** mutation root */
export type Mutation_RootUpdateAuthRefreshTokenTypesArgs = {
_set?: InputMaybe<AuthRefreshTokenTypes_Set_Input>;
where: AuthRefreshTokenTypes_Bool_Exp;
};
/** mutation root */
export type Mutation_RootUpdateAuthRefreshTokensArgs = {
_append?: InputMaybe<AuthRefreshTokens_Append_Input>;
@@ -11803,6 +12030,12 @@ export type Mutation_RootUpdate_AuthProviders_ManyArgs = {
};
/** mutation root */
export type Mutation_RootUpdate_AuthRefreshTokenTypes_ManyArgs = {
updates: Array<AuthRefreshTokenTypes_Updates>;
};
/** mutation root */
export type Mutation_RootUpdate_AuthRefreshTokens_ManyArgs = {
updates: Array<AuthRefreshTokens_Updates>;
@@ -13181,6 +13414,12 @@ export type Query_Root = {
authProvidersAggregate: AuthProviders_Aggregate;
/** fetch data from the table: "auth.refresh_tokens" using primary key columns */
authRefreshToken?: Maybe<AuthRefreshTokens>;
/** fetch data from the table: "auth.refresh_token_types" using primary key columns */
authRefreshTokenType?: Maybe<AuthRefreshTokenTypes>;
/** fetch data from the table: "auth.refresh_token_types" */
authRefreshTokenTypes: Array<AuthRefreshTokenTypes>;
/** fetch aggregated fields from the table: "auth.refresh_token_types" */
authRefreshTokenTypesAggregate: AuthRefreshTokenTypes_Aggregate;
/** fetch data from the table: "auth.refresh_tokens" */
authRefreshTokens: Array<AuthRefreshTokens>;
/** fetch aggregated fields from the table: "auth.refresh_tokens" */
@@ -13513,6 +13752,29 @@ export type Query_RootAuthRefreshTokenArgs = {
};
export type Query_RootAuthRefreshTokenTypeArgs = {
value: Scalars['String'];
};
export type Query_RootAuthRefreshTokenTypesArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokenTypes_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<AuthRefreshTokenTypes_Order_By>>;
where?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
};
export type Query_RootAuthRefreshTokenTypesAggregateArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokenTypes_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<AuthRefreshTokenTypes_Order_By>>;
where?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
};
export type Query_RootAuthRefreshTokensArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokens_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
@@ -15174,6 +15436,14 @@ export type Subscription_Root = {
authProviders_stream: Array<AuthProviders>;
/** fetch data from the table: "auth.refresh_tokens" using primary key columns */
authRefreshToken?: Maybe<AuthRefreshTokens>;
/** fetch data from the table: "auth.refresh_token_types" using primary key columns */
authRefreshTokenType?: Maybe<AuthRefreshTokenTypes>;
/** fetch data from the table: "auth.refresh_token_types" */
authRefreshTokenTypes: Array<AuthRefreshTokenTypes>;
/** fetch aggregated fields from the table: "auth.refresh_token_types" */
authRefreshTokenTypesAggregate: AuthRefreshTokenTypes_Aggregate;
/** fetch data from the table in a streaming manner: "auth.refresh_token_types" */
authRefreshTokenTypes_stream: Array<AuthRefreshTokenTypes>;
/** fetch data from the table: "auth.refresh_tokens" */
authRefreshTokens: Array<AuthRefreshTokens>;
/** fetch aggregated fields from the table: "auth.refresh_tokens" */
@@ -15575,6 +15845,36 @@ export type Subscription_RootAuthRefreshTokenArgs = {
};
export type Subscription_RootAuthRefreshTokenTypeArgs = {
value: Scalars['String'];
};
export type Subscription_RootAuthRefreshTokenTypesArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokenTypes_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<AuthRefreshTokenTypes_Order_By>>;
where?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
};
export type Subscription_RootAuthRefreshTokenTypesAggregateArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokenTypes_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<AuthRefreshTokenTypes_Order_By>>;
where?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
};
export type Subscription_RootAuthRefreshTokenTypes_StreamArgs = {
batch_size: Scalars['Int'];
cursor: Array<InputMaybe<AuthRefreshTokenTypes_Stream_Cursor_Input>>;
where?: InputMaybe<AuthRefreshTokenTypes_Bool_Exp>;
};
export type Subscription_RootAuthRefreshTokensArgs = {
distinct_on?: InputMaybe<Array<AuthRefreshTokens_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
@@ -19324,7 +19624,7 @@ export const ProjectFragmentDoc = gql`
githubRepository {
fullName
}
deployments(limit: 4, order_by: {deploymentEndedAt: desc}) {
deployments(limit: 4, order_by: {deploymentStartedAt: desc}) {
id
commitSHA
commitMessage
@@ -20708,7 +21008,7 @@ export const LatestLiveDeploymentSubDocument = gql`
subscription LatestLiveDeploymentSub($appId: uuid!) {
deployments(
where: {deploymentStatus: {_eq: "DEPLOYED"}, appId: {_eq: $appId}}
order_by: {deploymentEndedAt: desc}
order_by: {deploymentStartedAt: desc}
limit: 1
offset: 0
) {

View File

@@ -1,17 +0,0 @@
import type { ApolloClient, ApolloQueryResult } from '@apollo/client';
import { GetAllWorkspacesAndProjectsDocument } from './__generated__/graphql';
/**
* This function will refetch the main query we use for the cache
* of the user's workspaces and applications.
* @param client The apollo client instance.
*/
export async function updateOwnCache(
client: ApolloClient<any>,
): Promise<ApolloQueryResult<any>[]> {
return client.refetchQueries({
include: [GetAllWorkspacesAndProjectsDocument],
});
}
export default updateOwnCache;

View File

@@ -1,5 +1,11 @@
# @nhost/docs
## 0.2.1
### Patch Changes
- 203bc97f: feat(pat): add support for personal access tokens
## 0.2.0
### Minor Changes

View File

@@ -0,0 +1,82 @@
---
title: Sign In with Personal Access Tokens
sidebar_label: Personal Access Tokens
slug: /authentication/sign-in-with-personal-access-tokens
---
Nhost allows you to sign in users with personal access tokens (PAT) which is a way to sign in users without an email address or password.
## Configuration
:::info
Personal Access Tokens can only be created through Hasura Auth or the [Nhost JavaScript SDK](/reference/javascript) at the moment.
:::
## Create a Personal Access Token
Users must be signed in to create a personal access token. Once a user is signed in, they can create a personal access token.
**Example:** Create a personal access token:
```tsx
const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30) // 30 days
const metadata = { name: 'Example PAT' } // Optional metadata
const { data, error } = await nhost.auth.createPAT(expiresAt, metadata)
// Something unexpected happened
if (error) {
console.error(error)
return
}
console.log(data.id) // The personal access token ID (can be used to delete the token later)
console.log(data.personalAccessToken) // The personal access token
```
Users can create multiple personal access tokens. Each token can have a different expiration date and metadata.
## Sign In
Once a user has created a personal access token, they can use it to sign in.
**Example:** Sign in with a personal access token:
```tsx
const { error, session } = await nhost.auth.signInPAT('<personal-access-token>')
// Something unexpected happened
if (error) {
console.log(error)
return
}
// User is signed in
console.log(session.user)
```
## List or Remove Personal Access Tokens
To list and remove personal access tokens, use GraphQL and set permissions on the `auth.refresh_tokens` table:
**Example:** Get all personal access tokens for a user:
```graphql
query personalAccessTokens($userId: uuid!) {
authRefreshTokens(where: { _and: [{ userId: { _eq: $userId } }, { type: { _eq: pat } }] }) {
id
expiresAt
metadata
}
}
```
**Example:** Remove a personal access token:
```graphql
mutation removePersonalAccessToken($id: uuid!) {
deleteAuthRefreshToken(id: $id) {
id
}
}
```

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "0.2.0",
"version": "0.2.1",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -16,9 +16,9 @@
},
"dependencies": {
"@algolia/client-search": "^4.9.1",
"@docusaurus/core": "2.4.0",
"@docusaurus/plugin-sitemap": "2.4.0",
"@docusaurus/preset-classic": "2.4.0",
"@docusaurus/core": "2.4.1",
"@docusaurus/plugin-sitemap": "2.4.1",
"@docusaurus/preset-classic": "2.4.1",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.2.1",
"docusaurus-plugin-image-zoom": "^0.1.1",
@@ -30,7 +30,7 @@
"unist-util-visit": "^2.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.4.0",
"@docusaurus/module-type-aliases": "2.4.1",
"@tsconfig/docusaurus": "^1.0.6",
"typescript": "^4.8.4"
},

View File

@@ -0,0 +1,3 @@
NHOST_ACCOUNT_PAT=34f74930-09c0-4af5-a8d5-28fad78e3414
NHOST_ACCOUNT_EMAIL=cli@nhost.io
NHOST_ACCOUNT_PASSWORD=Admin1234!

5
examples/cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.nhost
web/node_modules
node_modules
functions/node_modules
.env

View File

@@ -0,0 +1,9 @@
# @nhost-examples/cli
## 0.1.1
### Patch Changes
- 203bc97f: feat(pat): add support for personal access tokens
- Updated dependencies [7fea29a8]
- @nhost/nhost-js@2.2.5

109
examples/cli/README.md Normal file
View File

@@ -0,0 +1,109 @@
# Node.js CLI tool example with Personal Access Tokens
Todo app that shows how to use:
- [Nhost](https://nhost.io/)
- [Node.js](https://nodejs.org/en/)
- Personal Access Tokens
## Get Started
There is a migration script that creates a service account. The credentials of
this account are used to authenticate the CLI tool.
By default these credentials are used:
Email address: `cli@nhost.io`
Password: `Admin1234!`
1. Clone the repository
```sh
git clone https://github.com/nhost/nhost
cd nhost
```
2. Install dependencies
```sh
pnpm install
```
3. Start the Nhost backend
```sh
nhost up
```
4. Run the help command to see the available commands
```sh
pnpm start --help
```
## Environment Variables
Credentials can be provided through the command line or by using environment
variables. Create a `.env` file in the root folder of the example. See `.env.example`
for an example configuration.
You can specify the following environment variables:
- `NHOST_ACCOUNT_PAT` - The personal access token of the service account. If provided, the email address and password will be ignored.
- `NHOST_ACCOUNT_EMAIL` - The email address of the service account.
- `NHOST_ACCOUNT_PASSWORD` - The password of the service account.
> If email and password are provided, the CLI tool will sign in first to create a personal access token for the account. This token will then be used to authenticate and make requests to the Nhost backend as the service account without having to provide the email address and password. Make sure to copy the token from the output and use it in the `NHOST_ACCOUNT_PAT` environment variable later.
## Commands
Environment variables should be placed in a `.env` file in the root folder of
the example. See `.env.example` for an example configuration.
### Use an existing personal access token to authenticate
```sh
pnpm start --token <personal-access-token>
```
or using environment variables:
```sh
NHOST_ACCOUNT_PAT=<personal-access-token> pnpm start
```
### Create a new personal access token
```sh
pnpm start --email cli@nhost.io --password Admin1234! --create-token --expires-at 2040-01-01 --token-name "CLI Token"
```
or using environment variables:
```sh
NHOST_ACCOUNT_EMAIL=cli@nhost.io NHOST_ACCOUNT_PASSWORD=Admin1234! pnpm start --create-token --expires-at 2040-01-01 --token-name "CLI Token"
```
### Create a new book
```sh
pnpm start --token <personal-access-token> --create-book <title>
```
or using environment variables:
```sh
NHOST_ACCOUNT_PAT=<personal-access-token> pnpm start --create-book <title>
```
### Delete a book
```sh
pnpm start --token <personal-access-token> --delete-book <id>
```
or using environment variables:
```sh
NHOST_ACCOUNT_PAT=<personal-access-token> pnpm start --delete-book <id>
```

View File

@@ -0,0 +1,139 @@
metadata_directory: metadata
services:
auth:
image: nhost/hasura-auth:0.20.0
hasura:
environment:
hasura_graphql_enable_remote_schema_permissions: true
minio:
environment:
minio_root_password: minioaccesskey123123
minio_root_user: minioaccesskey123123
postgres:
environment:
postgres_password: postgres
postgres_user: postgres
auth:
access_control:
email:
allowed_email_domains: ''
allowed_emails: ''
blocked_email_domains: ''
blocked_emails: ''
url:
allowed_redirect_urls: ''
anonymous_users_enabled: false
client_url: http://localhost:3000
disable_new_users: false
email:
enabled: false
passwordless:
enabled: false
signin_email_verified_required: false
template_fetch_url: ''
gravatar:
default: ''
enabled: true
rating: ''
locale:
allowed: en
default: en
password:
hibp_enabled: false
min_length: 3
provider:
apple:
client_id: ''
enabled: false
key_id: ''
private_key: ''
scope: name,email
team_id: ''
bitbucket:
client_id: ''
client_secret: ''
enabled: false
facebook:
client_id: ''
client_secret: ''
enabled: false
scope: email,photos,displayName
github:
client_id: ''
client_secret: ''
enabled: false
scope: user:email
token_url: ''
user_profile_url: ''
gitlab:
base_url: ''
client_id: ''
client_secret: ''
enabled: false
scope: read_user
google:
client_id: ''
client_secret: ''
enabled: false
scope: email,profile
linkedin:
client_id: ''
client_secret: ''
enabled: false
scope: r_emailaddress,r_liteprofile
spotify:
client_id: ''
client_secret: ''
enabled: false
scope: user-read-email,user-read-private
strava:
client_id: ''
client_secret: ''
enabled: false
twilio:
account_sid: ''
auth_token: ''
enabled: false
messaging_service_id: ''
twitter:
consumer_key: ''
consumer_secret: ''
enabled: false
windows_live:
client_id: ''
client_secret: ''
enabled: false
scope: wl.basic,wl.emails,wl.contacts_emails
sms:
enabled: false
passwordless:
enabled: false
provider:
twilio:
account_sid: ''
auth_token: ''
from: ''
messaging_service_id: ''
smtp:
host: mailhog
method: ''
pass: password
port: 1025
secure: false
sender: hasura-auth@example.com
user: user
token:
access:
expires_in: 900
refresh:
expires_in: 43200
user:
allowed_roles: user,me
default_allowed_roles: user,me
default_role: user
mfa:
enabled: false
issuer: nhost
storage:
force_download_for_content_types: text/html,application/javascript
version: 3

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Потвърдете смяната на вашия имейл</h2>
<p>Използвайте посочения линк, за да повърдите смяната на имейл:</p>
<p>
<a href="${link}">
Смени имейл
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Потвърждение за смяна на имейл

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Потвърдете вашия имейл</h2>
<p>Използвайте посочения линк, за да потвърдите вашия имейл:</p>
<p>
<a href="${link}">
Потвърдете имейл
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Потвърждаване на имейл

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Смяна на парола</h2>
<p>Използвайте посочения линк, за да смените вашата парола:</p>
<p>
<a href="${link}">
Смяна на парола
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Смяна на парола

View File

@@ -0,0 +1 @@
Вашият код е ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Магически линк за вход</h2>
<p>Използвайте посочения линк за защитен и бърз вход:</p>
<p>
<a href="${link}">
Вход
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Магически линк за вход

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Potvrzení změny emailové adresy</h2>
<p>Použijte tento odkaz k potvrzení změny emailové adresy:</p>
<p>
<a href="${link}">
Změnit email
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Změna vaší emailové adresy

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Ověření emailové adresy</h2>
<p>Použijte tento odkaz k ověření vaší emailové adresy:</p>
<p>
<a href="${link}">
Ověřit emailovou adresu
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Ověření vaší emailové adresy

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Obnova hesla</h2>
<p>Použijte tento odkaz k obnovení vašeho hesla:</p>
<p>
<a href="${link}">
Obnova hesla
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Obnova hesla

View File

@@ -0,0 +1 @@
Váš kód je ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Magický odkaz</h2>
<p>Použijte tento odkaz k bezpečnému přihlášení:</p>
<p>
<a href="${link}">
Přihlášení
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Bezpečný odkaz k přihlášení

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Confirm Email Change</h2>
<p>Use this link to confirm changing email:</p>
<p>
<a href="${link}">
Change email
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Change your email address

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Verify Email</h2>
<p>Use this link to verify your email:</p>
<p>
<a href="${link}">
Verify Email
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Verify your email

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Reset Password</h2>
<p>Use this link to reset your password:</p>
<p>
<a href="${link}">
Reset password
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Reset your password

View File

@@ -0,0 +1 @@
Your code is ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Magic Link</h2>
<p>Use this link to securely sign in:</p>
<p>
<a href="${link}">
Sign In
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Secure sign-in link

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Confirmar cambio de correo electrónico</h2>
<p>Utiliza el siguiente enlace para confirmar el cambio de correo:</p>
<p>
<a href="${link}">
Cambiar correo electrónico
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Cambiar dirección de correo electrónico

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Verificar correo electrónico</h2>
<p>Utilza el siguiente enlace para verificar tu correo:</p>
<p>
<a href="${link}">
Verificar correo electrónico
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Verifica tu correo electrónico

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Recuperar contraseña</h2>
<p>Utiliza el siguiente enlace para recuperar tu contraseña:</p>
<p>
<a href="${link}">
Recuperar contraseña
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Recuperar contraseña

View File

@@ -0,0 +1 @@
Tu código es ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Enlace mágico</h2>
<p>Utiliza este enlace para iniciar sesión de forma segura:</p>
<p>
<a href="${link}">
Iniciar sesión
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Enlace de acceso seguro

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Confirmer changement de courriel</h2>
<p>Utilisez ce lien pour confirmer le changement de courriel:</p>
<p>
<a href="${link}">
Changer courriel
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Changez votre adresse courriel

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>V&eacute;rifiez votre courriel</h2>
<p>Utilisez ce lien pour v&eacute;rifier votre courriel:</p>
<p>
<a href="${link}">
V&eacute;rifier courriel
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Vérifier votre courriel

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>R&eacute;initializer votre mot de passe</h2>
<p>Utilisez ce lien pour r&eacute;initializer votre mot de passe:</p>
<p>
<a href="${link}">
R&eacute;initializer mot de passe
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Réinitialiser votre mot de passe

View File

@@ -0,0 +1 @@
Votre code est ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Lien magique</h2>
<p>Utilisez ce lien pour vous connecter de fa&ccedil;on s&eacute;curitaire:</p>
<p>
<a href="${link}">
Connexion
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Lien de connexion sécurisé

View File

@@ -0,0 +1,6 @@
actions: []
custom_types:
enums: []
input_objects: []
objects: []
scalars: []

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,14 @@
- name: default
kind: postgres
configuration:
connection_info:
database_url:
from_env: HASURA_GRAPHQL_DATABASE_URL
isolation_level: read-committed
pool_settings:
connection_lifetime: 600
idle_timeout: 180
max_connections: 50
retries: 20
use_prepared_statements: true
tables: "!include default/tables/tables.yaml"

View File

@@ -0,0 +1,23 @@
table:
name: provider_requests
schema: auth
configuration:
column_config:
id:
custom_name: id
options:
custom_name: options
custom_column_names:
id: id
options: options
custom_name: authProviderRequests
custom_root_fields:
delete: deleteAuthProviderRequests
delete_by_pk: deleteAuthProviderRequest
insert: insertAuthProviderRequests
insert_one: insertAuthProviderRequest
select: authProviderRequests
select_aggregate: authProviderRequestsAggregate
select_by_pk: authProviderRequest
update: updateAuthProviderRequests
update_by_pk: updateAuthProviderRequest

View File

@@ -0,0 +1,28 @@
table:
name: providers
schema: auth
configuration:
column_config:
id:
custom_name: id
custom_column_names:
id: id
custom_name: authProviders
custom_root_fields:
delete: deleteAuthProviders
delete_by_pk: deleteAuthProvider
insert: insertAuthProviders
insert_one: insertAuthProvider
select: authProviders
select_aggregate: authProvidersAggregate
select_by_pk: authProvider
update: updateAuthProviders
update_by_pk: updateAuthProvider
array_relationships:
- name: userProviders
using:
foreign_key_constraint_on:
column: provider_id
table:
name: user_providers
schema: auth

View File

@@ -0,0 +1,26 @@
table:
name: refresh_token_types
schema: auth
is_enum: true
configuration:
column_config: {}
custom_column_names: {}
custom_name: authRefreshTokenTypes
custom_root_fields:
delete: deleteAuthRefreshTokenTypes
delete_by_pk: deleteAuthRefreshTokenType
insert: insertAuthRefreshTokenTypes
insert_one: insertAuthRefreshTokenType
select: authRefreshTokenTypes
select_aggregate: authRefreshTokenTypesAggregate
select_by_pk: authRefreshTokenType
update: updateAuthRefreshTokenTypes
update_by_pk: updateAuthRefreshTokenType
array_relationships:
- name: refreshTokens
using:
foreign_key_constraint_on:
column: type
table:
name: refresh_tokens
schema: auth

View File

@@ -0,0 +1,61 @@
table:
name: refresh_tokens
schema: auth
configuration:
column_config:
created_at:
custom_name: createdAt
expires_at:
custom_name: expiresAt
refresh_token_hash:
custom_name: refreshTokenHash
type:
custom_name: type
user_id:
custom_name: userId
custom_column_names:
created_at: createdAt
expires_at: expiresAt
refresh_token_hash: refreshTokenHash
type: type
user_id: userId
custom_name: authRefreshTokens
custom_root_fields:
delete: deleteAuthRefreshTokens
delete_by_pk: deleteAuthRefreshToken
insert: insertAuthRefreshTokens
insert_one: insertAuthRefreshToken
select: authRefreshTokens
select_aggregate: authRefreshTokensAggregate
select_by_pk: authRefreshToken
update: updateAuthRefreshTokens
update_by_pk: updateAuthRefreshToken
object_relationships:
- name: refreshTokenType
using:
foreign_key_constraint_on: type
- name: user
using:
foreign_key_constraint_on: user_id
select_permissions:
- role: user
permission:
columns:
- id
- created_at
- expires_at
- metadata
- type
- user_id
filter:
user_id:
_eq: X-Hasura-User-Id
delete_permissions:
- role: user
permission:
filter:
_and:
- user_id:
_eq: X-Hasura-User-Id
- type:
_eq: pat

View File

@@ -0,0 +1,35 @@
table:
name: roles
schema: auth
configuration:
column_config:
role:
custom_name: role
custom_column_names:
role: role
custom_name: authRoles
custom_root_fields:
delete: deleteAuthRoles
delete_by_pk: deleteAuthRole
insert: insertAuthRoles
insert_one: insertAuthRole
select: authRoles
select_aggregate: authRolesAggregate
select_by_pk: authRole
update: updateAuthRoles
update_by_pk: updateAuthRole
array_relationships:
- name: userRoles
using:
foreign_key_constraint_on:
column: role
table:
name: user_roles
schema: auth
- name: usersByDefaultRole
using:
foreign_key_constraint_on:
column: default_role
table:
name: users
schema: auth

View File

@@ -0,0 +1,48 @@
table:
name: user_providers
schema: auth
configuration:
column_config:
access_token:
custom_name: accessToken
created_at:
custom_name: createdAt
id:
custom_name: id
provider_id:
custom_name: providerId
provider_user_id:
custom_name: providerUserId
refresh_token:
custom_name: refreshToken
updated_at:
custom_name: updatedAt
user_id:
custom_name: userId
custom_column_names:
access_token: accessToken
created_at: createdAt
id: id
provider_id: providerId
provider_user_id: providerUserId
refresh_token: refreshToken
updated_at: updatedAt
user_id: userId
custom_name: authUserProviders
custom_root_fields:
delete: deleteAuthUserProviders
delete_by_pk: deleteAuthUserProvider
insert: insertAuthUserProviders
insert_one: insertAuthUserProvider
select: authUserProviders
select_aggregate: authUserProvidersAggregate
select_by_pk: authUserProvider
update: updateAuthUserProviders
update_by_pk: updateAuthUserProvider
object_relationships:
- name: provider
using:
foreign_key_constraint_on: provider_id
- name: user
using:
foreign_key_constraint_on: user_id

View File

@@ -0,0 +1,36 @@
table:
name: user_roles
schema: auth
configuration:
column_config:
created_at:
custom_name: createdAt
id:
custom_name: id
role:
custom_name: role
user_id:
custom_name: userId
custom_column_names:
created_at: createdAt
id: id
role: role
user_id: userId
custom_name: authUserRoles
custom_root_fields:
delete: deleteAuthUserRoles
delete_by_pk: deleteAuthUserRole
insert: insertAuthUserRoles
insert_one: insertAuthUserRole
select: authUserRoles
select_aggregate: authUserRolesAggregate
select_by_pk: authUserRole
update: updateAuthUserRoles
update_by_pk: updateAuthUserRole
object_relationships:
- name: roleByRole
using:
foreign_key_constraint_on: role
- name: user
using:
foreign_key_constraint_on: user_id

Some files were not shown because too many files have changed in this diff Show More