Compare commits
26 Commits
@nhost/das
...
@nhost/vue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9fd1a54a5 | ||
|
|
a19b85c8ac | ||
|
|
4e1aaca0ee | ||
|
|
34ef37cdce | ||
|
|
5d6b655cb1 | ||
|
|
074a0fa111 | ||
|
|
403d839fca | ||
|
|
4e3098240b | ||
|
|
07fda9bbb3 | ||
|
|
4a7ede11e9 | ||
|
|
482ae4c4f1 | ||
|
|
08fe4cd65f | ||
|
|
5781721bca | ||
|
|
39de0063bf | ||
|
|
202b647234 | ||
|
|
51c163a268 | ||
|
|
6e802c9938 | ||
|
|
9a46104e37 | ||
|
|
655b317c39 | ||
|
|
d3ad7c9d4a | ||
|
|
ece08d3efd | ||
|
|
3493442c2d | ||
|
|
632a79b9e4 | ||
|
|
4a4d85757a | ||
|
|
88a01004b7 | ||
|
|
73230eb35a |
@@ -1,5 +1,15 @@
|
|||||||
# @nhost/dashboard
|
# @nhost/dashboard
|
||||||
|
|
||||||
|
## 0.20.7
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 4a7ede11e: fix: distinguish files that were not uploaded
|
||||||
|
- 202b64723: feat(nhost-run): add support for one-click-install run services
|
||||||
|
- 074a0fa11: feat(dashboard): add settings toggle to enable/disable antivirus
|
||||||
|
- @nhost/react-apollo@5.0.33
|
||||||
|
- @nhost/nextjs@1.13.35
|
||||||
|
|
||||||
## 0.20.6
|
## 0.20.6
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/dashboard",
|
"name": "@nhost/dashboard",
|
||||||
"version": "0.20.6",
|
"version": "0.20.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Button } from '@/components/ui/v2/Button';
|
|||||||
import { ArrowRightIcon } from '@/components/ui/v2/icons/ArrowRightIcon';
|
import { ArrowRightIcon } from '@/components/ui/v2/icons/ArrowRightIcon';
|
||||||
import { XIcon } from '@/components/ui/v2/icons/XIcon';
|
import { XIcon } from '@/components/ui/v2/icons/XIcon';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import Link from 'next/link';
|
|
||||||
import { forwardRef, type ForwardedRef } from 'react';
|
import { forwardRef, type ForwardedRef } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import AnnouncementContainer, {
|
import AnnouncementContainer, {
|
||||||
@@ -28,7 +27,7 @@ function Announcement(
|
|||||||
<AnnouncementContainer
|
<AnnouncementContainer
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="grid justify-between grid-flow-col gap-4"
|
className="grid grid-flow-col justify-between gap-4"
|
||||||
slotProps={{
|
slotProps={{
|
||||||
root: {
|
root: {
|
||||||
...(slotProps?.root || {}),
|
...(slotProps?.root || {}),
|
||||||
@@ -39,12 +38,12 @@ function Announcement(
|
|||||||
<span />
|
<span />
|
||||||
|
|
||||||
<div className="flex items-center self-center truncate">
|
<div className="flex items-center self-center truncate">
|
||||||
<Link href={href}>
|
<a href={href}>
|
||||||
<Text className="truncate cursor-pointer hover:underline">
|
<Text className="cursor-pointer truncate hover:underline">
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
</Link>
|
</a>
|
||||||
<ArrowRightIcon className="w-4 h-4 ml-1 text-white" />
|
<ArrowRightIcon className="ml-1 h-4 w-4 text-white" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -52,9 +51,9 @@ function Announcement(
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
aria-label="Close announcement"
|
aria-label="Close announcement"
|
||||||
size="small"
|
size="small"
|
||||||
className="p-1 rounded-sm"
|
className="rounded-sm p-1"
|
||||||
>
|
>
|
||||||
<XIcon className="w-4 h-4 opacity-65" />
|
<XIcon className="opacity-65 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</AnnouncementContainer>
|
</AnnouncementContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -178,6 +178,22 @@ export default function DataGridBody<T extends object>({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getBackgroundCellColor = (
|
||||||
|
row: Row<T>,
|
||||||
|
column: DataBrowserGridColumn<T>,
|
||||||
|
) => {
|
||||||
|
// Grey out files not uploaded
|
||||||
|
if (!row.values.isUploaded) {
|
||||||
|
return 'grey.200';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.isDisabled) {
|
||||||
|
return 'grey.100';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'background.paper';
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...getTableBodyProps()} ref={bodyRef} {...props}>
|
<div {...getTableBodyProps()} ref={bodyRef} {...props}>
|
||||||
{rows.length === 0 && !loading && (
|
{rows.length === 0 && !loading && (
|
||||||
@@ -260,9 +276,7 @@ export default function DataGridBody<T extends object>({
|
|||||||
})}
|
})}
|
||||||
cell={cell}
|
cell={cell}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: column.isDisabled
|
backgroundColor: getBackgroundCellColor(row, column),
|
||||||
? 'grey.100'
|
|
||||||
: 'background.paper',
|
|
||||||
color: isCellDisabled ? 'text.secondary' : 'text.primary',
|
color: isCellDisabled ? 'text.secondary' : 'text.primary',
|
||||||
}}
|
}}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as useHostName } from './useHostName';
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function useHostName() {
|
||||||
|
const [hostName, setHostName] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { port, hostname, protocol } = window.location;
|
||||||
|
setHostName(`${protocol}//${hostname}:${port}`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return hostName;
|
||||||
|
}
|
||||||
@@ -3,11 +3,14 @@ import { Form } from '@/components/form/Form';
|
|||||||
import { Alert } from '@/components/ui/v2/Alert';
|
import { Alert } from '@/components/ui/v2/Alert';
|
||||||
import { Box } from '@/components/ui/v2/Box';
|
import { Box } from '@/components/ui/v2/Box';
|
||||||
import { Button } from '@/components/ui/v2/Button';
|
import { Button } from '@/components/ui/v2/Button';
|
||||||
|
import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
|
||||||
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
|
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
|
||||||
|
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
|
||||||
import { Input } from '@/components/ui/v2/Input';
|
import { Input } from '@/components/ui/v2/Input';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
|
import { useHostName } from '@/features/projects/common/hooks/useHostName';
|
||||||
import { InfoCard } from '@/features/projects/overview/components/InfoCard';
|
import { InfoCard } from '@/features/projects/overview/components/InfoCard';
|
||||||
import {
|
import {
|
||||||
COST_PER_VCPU,
|
COST_PER_VCPU,
|
||||||
@@ -25,6 +28,7 @@ import { StorageFormSection } from '@/features/services/components/ServiceForm/c
|
|||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
import { RESOURCE_VCPU_MULTIPLIER } from '@/utils/constants/common';
|
import { RESOURCE_VCPU_MULTIPLIER } from '@/utils/constants/common';
|
||||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||||
|
import { copy } from '@/utils/copy';
|
||||||
import {
|
import {
|
||||||
useInsertRunServiceConfigMutation,
|
useInsertRunServiceConfigMutation,
|
||||||
useInsertRunServiceMutation,
|
useInsertRunServiceMutation,
|
||||||
@@ -109,6 +113,7 @@ export default function ServiceForm({
|
|||||||
onCancel,
|
onCancel,
|
||||||
location,
|
location,
|
||||||
}: ServiceFormProps) {
|
}: ServiceFormProps) {
|
||||||
|
const hostName = useHostName();
|
||||||
const { onDirtyStateChange, openDialog, closeDialog } = useDialog();
|
const { onDirtyStateChange, openDialog, closeDialog } = useDialog();
|
||||||
const [insertRunService] = useInsertRunServiceMutation();
|
const [insertRunService] = useInsertRunServiceMutation();
|
||||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||||
@@ -146,7 +151,7 @@ export default function ServiceForm({
|
|||||||
onDirtyStateChange(isDirty, location);
|
onDirtyStateChange(isDirty, location);
|
||||||
}, [isDirty, location, onDirtyStateChange]);
|
}, [isDirty, location, onDirtyStateChange]);
|
||||||
|
|
||||||
const createOrUpdateService = async (values: ServiceFormValues) => {
|
const getFormattedConfig = (values: ServiceFormValues) => {
|
||||||
const config: ConfigRunServiceConfigInsertInput = {
|
const config: ConfigRunServiceConfigInsertInput = {
|
||||||
name: values.name,
|
name: values.name,
|
||||||
image: {
|
image: {
|
||||||
@@ -176,7 +181,13 @@ export default function ServiceForm({
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (initialData) {
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createOrUpdateService = async (values: ServiceFormValues) => {
|
||||||
|
const config = getFormattedConfig(values);
|
||||||
|
|
||||||
|
if (serviceID) {
|
||||||
// Update service config
|
// Update service config
|
||||||
await replaceRunServiceConfig({
|
await replaceRunServiceConfig({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -278,6 +289,16 @@ export default function ServiceForm({
|
|||||||
return `Approximate cost for ${details}`;
|
return `Approximate cost for ${details}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const copyConfig = () => {
|
||||||
|
const config = getFormattedConfig(formValues);
|
||||||
|
|
||||||
|
const base64Config = btoa(JSON.stringify(config));
|
||||||
|
|
||||||
|
const link = `${hostName}/run-one-click-install?config=${base64Config}`;
|
||||||
|
|
||||||
|
copy(link, 'Service Config');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<Form
|
<Form
|
||||||
@@ -427,9 +448,24 @@ export default function ServiceForm({
|
|||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<div className="grid grid-flow-row gap-2">
|
<div className="grid grid-flow-row gap-2">
|
||||||
<Button type="submit" disabled={isSubmitting}>
|
<div className="grid grid-cols-2 gap-2">
|
||||||
{initialData ? 'Update' : 'Create'}
|
<Button
|
||||||
</Button>
|
type="submit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
startIcon={<PlusIcon />}
|
||||||
|
>
|
||||||
|
{serviceID ? 'Update' : 'Create'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
variant="outlined"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={copyConfig}
|
||||||
|
startIcon={<CopyIcon />}
|
||||||
|
>
|
||||||
|
Copy one-click install link
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button variant="outlined" color="secondary" onClick={onCancel}>
|
<Button variant="outlined" color="secondary" onClick={onCancel}>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default function PortsFormSection() {
|
|||||||
const port = Number(_port) > 0 ? Number(_port) : '[port]';
|
const port = Number(_port) > 0 ? Number(_port) : '[port]';
|
||||||
const name = _name && _name.length > 0 ? _name : '[name]';
|
const name = _name && _name.length > 0 ? _name : '[name]';
|
||||||
|
|
||||||
return `https://${currentProject.subdomain}-${name}-${port}.svc.${currentProject.region.awsName}.${currentProject.region.domain}`;
|
return `https://${currentProject?.subdomain}-${name}-${port}.svc.${currentProject?.region.awsName}.${currentProject?.region.domain}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import { useUI } from '@/components/common/UIProvider';
|
||||||
|
import { Form } from '@/components/form/Form';
|
||||||
|
import { SettingsContainer } from '@/components/layout/SettingsContainer';
|
||||||
|
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||||
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
|
import {
|
||||||
|
GetHasuraSettingsDocument,
|
||||||
|
useGetStorageSettingsQuery,
|
||||||
|
useUpdateConfigMutation,
|
||||||
|
} from '@/generated/graphql';
|
||||||
|
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||||
|
import { getServerError } from '@/utils/getServerError';
|
||||||
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
import { toast } from 'react-hot-toast';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
|
const validationSchema = Yup.object({
|
||||||
|
enabled: Yup.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type HasuraStorageAVFormValues = Yup.InferType<typeof validationSchema>;
|
||||||
|
|
||||||
|
export default function HasuraStorageAVSettings() {
|
||||||
|
const { maintenanceActive } = useUI();
|
||||||
|
const { currentProject, refetch: refetchWorkspaceAndProject } =
|
||||||
|
useCurrentWorkspaceAndProject();
|
||||||
|
const [updateConfig] = useUpdateConfigMutation({
|
||||||
|
refetchQueries: [GetHasuraSettingsDocument],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data, loading, error } = useGetStorageSettingsQuery({
|
||||||
|
variables: { appId: currentProject?.id },
|
||||||
|
fetchPolicy: 'cache-first',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { server } = data?.config?.storage?.antivirus || {};
|
||||||
|
|
||||||
|
const form = useForm<HasuraStorageAVFormValues>({
|
||||||
|
reValidateMode: 'onSubmit',
|
||||||
|
defaultValues: {
|
||||||
|
enabled: !!server,
|
||||||
|
},
|
||||||
|
resolver: yupResolver(validationSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<ActivityIndicator
|
||||||
|
delay={1000}
|
||||||
|
label="Loading AV settings..."
|
||||||
|
className="justify-center"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit(formValues: HasuraStorageAVFormValues) {
|
||||||
|
let antivirus = null;
|
||||||
|
|
||||||
|
if (formValues.enabled) {
|
||||||
|
antivirus = {
|
||||||
|
server: 'tcp://run-clamav:3310',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateConfigPromise = updateConfig({
|
||||||
|
variables: {
|
||||||
|
appId: currentProject.id,
|
||||||
|
config: {
|
||||||
|
storage: {
|
||||||
|
antivirus,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await toast.promise(
|
||||||
|
updateConfigPromise,
|
||||||
|
{
|
||||||
|
loading: `Antivirus settings are being updated...`,
|
||||||
|
success: `Antivirus settings have been updated successfully.`,
|
||||||
|
error: getServerError(
|
||||||
|
`An error occurred while trying to update Antivirus settings.`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
getToastStyleProps(),
|
||||||
|
);
|
||||||
|
|
||||||
|
form.reset(formValues);
|
||||||
|
await refetchWorkspaceAndProject();
|
||||||
|
} catch {
|
||||||
|
// Note: The toast will handle the error.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormProvider {...form}>
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<SettingsContainer
|
||||||
|
title="Antivirus"
|
||||||
|
description="Enable or disable Antivirus."
|
||||||
|
slotProps={{
|
||||||
|
submitButton: {
|
||||||
|
disabled: !form.formState.isDirty || maintenanceActive,
|
||||||
|
loading: form.formState.isSubmitting,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
switchId="enabled"
|
||||||
|
docsTitle="enabling or disabling Antivirus"
|
||||||
|
showSwitch
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './HasuraStorageAVSettings';
|
||||||
|
export { default as HasuraStorageAVSettings } from './HasuraStorageAVSettings';
|
||||||
@@ -4,6 +4,9 @@ query GetStorageSettings($appId: uuid!) {
|
|||||||
__typename
|
__typename
|
||||||
storage {
|
storage {
|
||||||
version
|
version
|
||||||
|
antivirus {
|
||||||
|
server
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,20 +13,35 @@ import type { GetRunServicesQuery } from '@/utils/__generated__/graphql';
|
|||||||
import { useGetRunServicesQuery } from '@/utils/__generated__/graphql';
|
import { useGetRunServicesQuery } from '@/utils/__generated__/graphql';
|
||||||
|
|
||||||
import { UpgradeNotification } from '@/features/projects/common/components/UpgradeNotification';
|
import { UpgradeNotification } from '@/features/projects/common/components/UpgradeNotification';
|
||||||
import { ServiceForm } from '@/features/services/components/ServiceForm';
|
import {
|
||||||
|
ServiceForm,
|
||||||
|
type PortTypes,
|
||||||
|
} from '@/features/services/components/ServiceForm';
|
||||||
import ServicesList from '@/features/services/components/ServicesList/ServicesList';
|
import ServicesList from '@/features/services/components/ServicesList/ServicesList';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useMemo, useRef, useState, type ReactElement } from 'react';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
type ReactElement,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
export type RunService = Omit<
|
export type RunService = Omit<
|
||||||
GetRunServicesQuery['app']['runServices'][0],
|
GetRunServicesQuery['app']['runServices'][0],
|
||||||
'__typename'
|
'__typename'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type RunServiceConfig = Omit<
|
||||||
|
GetRunServicesQuery['app']['runServices'][0]['config'],
|
||||||
|
'__typename'
|
||||||
|
>;
|
||||||
|
|
||||||
export default function ServicesPage() {
|
export default function ServicesPage() {
|
||||||
const limit = useRef(25);
|
const limit = useRef(25);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { openDrawer } = useDialog();
|
const { openDrawer, openAlertDialog } = useDialog();
|
||||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||||
const isPlanFree = currentProject.plan.isFree;
|
const isPlanFree = currentProject.plan.isFree;
|
||||||
|
|
||||||
@@ -66,6 +81,63 @@ export default function ServicesPage() {
|
|||||||
[data],
|
[data],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const checkConfigFromQuery = useCallback(
|
||||||
|
(base64Config: string) => {
|
||||||
|
if (router.query?.config) {
|
||||||
|
try {
|
||||||
|
const decodedConfig = atob(base64Config);
|
||||||
|
const parsedConfig: RunServiceConfig = JSON.parse(decodedConfig);
|
||||||
|
|
||||||
|
openDrawer({
|
||||||
|
title: (
|
||||||
|
<Box className="flex flex-row items-center space-x-2">
|
||||||
|
<CubeIcon className="h-5 w-5" />
|
||||||
|
<Text>Create a new run service</Text>
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
component: (
|
||||||
|
<ServiceForm
|
||||||
|
initialData={{
|
||||||
|
...parsedConfig,
|
||||||
|
compute: parsedConfig?.resources?.compute ?? {
|
||||||
|
cpu: 62,
|
||||||
|
memory: 128,
|
||||||
|
},
|
||||||
|
image: parsedConfig?.image?.image,
|
||||||
|
command: parsedConfig?.command?.join(' '),
|
||||||
|
ports: parsedConfig?.ports.map((item) => ({
|
||||||
|
port: item.port,
|
||||||
|
type: item.type as PortTypes,
|
||||||
|
publish: item.publish,
|
||||||
|
})),
|
||||||
|
replicas: parsedConfig?.resources?.replicas,
|
||||||
|
storage: parsedConfig?.resources?.storage,
|
||||||
|
}}
|
||||||
|
onSubmit={refetchServices}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
openAlertDialog({
|
||||||
|
title: 'Configuration not set properly',
|
||||||
|
payload: 'The service configuration was not properly encoded',
|
||||||
|
props: {
|
||||||
|
primaryButtonText: 'Ok',
|
||||||
|
hideSecondaryAction: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[router.query.config, openDrawer, refetchServices, openAlertDialog],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (router.query?.config) {
|
||||||
|
checkConfigFromQuery(router.query?.config as string);
|
||||||
|
}
|
||||||
|
}, [checkConfigFromQuery, router.query]);
|
||||||
|
|
||||||
const openCreateServiceDialog = () => {
|
const openCreateServiceDialog = () => {
|
||||||
openDrawer({
|
openDrawer({
|
||||||
title: (
|
title: (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { SettingsLayout } from '@/components/layout/SettingsLayout';
|
|||||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { StorageServiceVersionSettings } from '@/features/storage/settings/components/HasuraServiceVersionSettings';
|
import { StorageServiceVersionSettings } from '@/features/storage/settings/components/HasuraServiceVersionSettings';
|
||||||
|
import { HasuraStorageAVSettings } from '@/features/storage/settings/components/HasuraStorageAVSettings';
|
||||||
import { useGetStorageSettingsQuery } from '@/utils/__generated__/graphql';
|
import { useGetStorageSettingsQuery } from '@/utils/__generated__/graphql';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export default function StorageSettingsPage() {
|
|||||||
rootClassName="bg-transparent"
|
rootClassName="bg-transparent"
|
||||||
>
|
>
|
||||||
<StorageServiceVersionSettings />
|
<StorageServiceVersionSettings />
|
||||||
|
<HasuraStorageAVSettings />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
218
dashboard/src/pages/run-one-click-install.tsx
Normal file
218
dashboard/src/pages/run-one-click-install.tsx
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import { useDialog } from '@/components/common/DialogProvider';
|
||||||
|
import { AuthenticatedLayout } from '@/components/layout/AuthenticatedLayout';
|
||||||
|
import { Container } from '@/components/layout/Container';
|
||||||
|
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
||||||
|
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||||
|
import { Box } from '@/components/ui/v2/Box';
|
||||||
|
import { Button } from '@/components/ui/v2/Button';
|
||||||
|
import { Input } from '@/components/ui/v2/Input';
|
||||||
|
import { List } from '@/components/ui/v2/List';
|
||||||
|
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||||
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
|
import { InfoCard } from '@/features/projects/overview/components/InfoCard';
|
||||||
|
import {
|
||||||
|
useGetAllWorkspacesAndProjectsQuery,
|
||||||
|
type GetAllWorkspacesAndProjectsQuery,
|
||||||
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { Divider } from '@mui/material';
|
||||||
|
import { useUserData } from '@nhost/nextjs';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import type { ChangeEvent, ReactElement } from 'react';
|
||||||
|
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
type Workspace = Omit<
|
||||||
|
GetAllWorkspacesAndProjectsQuery['workspaces'][0],
|
||||||
|
'__typename'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export default function SelectWorkspaceAndProject() {
|
||||||
|
const user = useUserData();
|
||||||
|
const router = useRouter();
|
||||||
|
const { openAlertDialog } = useDialog();
|
||||||
|
|
||||||
|
const { data, loading } = useGetAllWorkspacesAndProjectsQuery({
|
||||||
|
skip: !user,
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaces: Workspace[] = data?.workspaces || [];
|
||||||
|
|
||||||
|
const projects = workspaces.flatMap((workspace) =>
|
||||||
|
workspace.projects.map((project) => ({
|
||||||
|
workspaceName: workspace.name,
|
||||||
|
projectName: project.name,
|
||||||
|
value: `${workspace.slug}/${project.slug}`,
|
||||||
|
isFree: project.plan.isFree,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [filter, setFilter] = useState('');
|
||||||
|
|
||||||
|
const handleFilterChange = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setFilter(event.target.value);
|
||||||
|
}, 200),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
|
||||||
|
|
||||||
|
const checkConfigFromQuery = useCallback(
|
||||||
|
(base64Config: string) => {
|
||||||
|
try {
|
||||||
|
JSON.parse(atob(base64Config));
|
||||||
|
} catch (error) {
|
||||||
|
openAlertDialog({
|
||||||
|
title: 'Configuration not set properly',
|
||||||
|
payload:
|
||||||
|
'Either the link is wrong or the configuration is not properly encoded',
|
||||||
|
props: {
|
||||||
|
primaryButtonText: 'Ok',
|
||||||
|
hideSecondaryAction: true,
|
||||||
|
onPrimaryAction: async () => {
|
||||||
|
await router.push('/');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[openAlertDialog, router],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkConfigFromQuery(router.query?.config as string);
|
||||||
|
}, [checkConfigFromQuery, router.query]);
|
||||||
|
|
||||||
|
const goToServices = async (project: {
|
||||||
|
workspaceName: string;
|
||||||
|
projectName: string;
|
||||||
|
value: string;
|
||||||
|
isFree: boolean;
|
||||||
|
}) => {
|
||||||
|
if (!project) {
|
||||||
|
openAlertDialog({
|
||||||
|
title: 'Please select a workspace and a project',
|
||||||
|
payload:
|
||||||
|
'You must select a workspace and a project before proceeding to create the run service',
|
||||||
|
props: {
|
||||||
|
primaryButtonText: 'Ok',
|
||||||
|
hideSecondaryAction: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.isFree) {
|
||||||
|
openAlertDialog({
|
||||||
|
title: 'The project must have a pro plan',
|
||||||
|
payload: 'Creating run services is only availabel for pro projects',
|
||||||
|
props: {
|
||||||
|
primaryButtonText: 'Ok',
|
||||||
|
hideSecondaryAction: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await router.push({
|
||||||
|
pathname: `/${project.value}/services`,
|
||||||
|
// Keep the same query params that got us here
|
||||||
|
query: router.query,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const projectsToDisplay = filter
|
||||||
|
? projects.filter((project) =>
|
||||||
|
project.projectName.toLowerCase().includes(filter.toLowerCase()),
|
||||||
|
)
|
||||||
|
: projects;
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<ActivityIndicator
|
||||||
|
delay={500}
|
||||||
|
label="Loading workspaces and projects..."
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<div className="mx-auto grid max-w-[760px] grid-flow-row gap-4 py-6 sm:py-14">
|
||||||
|
<Text variant="h2" component="h1" className="">
|
||||||
|
New Run Service
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<InfoCard
|
||||||
|
title="Please select the workspace and the project where you want to create the service"
|
||||||
|
disableCopy
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 flex w-full">
|
||||||
|
<Input
|
||||||
|
placeholder="Search..."
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
fullWidth
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<RetryableErrorBoundary>
|
||||||
|
{projectsToDisplay.length === 0 ? (
|
||||||
|
<Box className="h-import py-2">
|
||||||
|
<Text variant="subtitle2">No results found.</Text>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<List className="h-import overflow-y-auto">
|
||||||
|
{projectsToDisplay.map((project, index) => (
|
||||||
|
<Fragment key={project.value}>
|
||||||
|
<ListItem.Root
|
||||||
|
className="grid grid-flow-col justify-start gap-2 py-2.5"
|
||||||
|
secondaryAction={
|
||||||
|
<Button
|
||||||
|
variant="borderless"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => goToServices(project)}
|
||||||
|
>
|
||||||
|
Proceed
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListItem.Avatar>
|
||||||
|
<span className="inline-block h-6 w-6 overflow-hidden rounded-md">
|
||||||
|
<Image
|
||||||
|
src="/logos/new.svg"
|
||||||
|
alt="Nhost Logo"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</ListItem.Avatar>
|
||||||
|
<ListItem.Text
|
||||||
|
primary={project.projectName}
|
||||||
|
secondary={`${project.workspaceName} / ${project.projectName}`}
|
||||||
|
/>
|
||||||
|
</ListItem.Root>
|
||||||
|
|
||||||
|
{index < projects.length - 1 && <Divider component="li" />}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
|
</RetryableErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectWorkspaceAndProject.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout title="New Run Service">{page}</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
29
dashboard/src/utils/__generated__/graphql.ts
generated
29
dashboard/src/utils/__generated__/graphql.ts
generated
@@ -1805,6 +1805,7 @@ export type ConfigStandardOauthProviderWithScopeUpdateInput = {
|
|||||||
/** Configuration for storage service */
|
/** Configuration for storage service */
|
||||||
export type ConfigStorage = {
|
export type ConfigStorage = {
|
||||||
__typename?: 'ConfigStorage';
|
__typename?: 'ConfigStorage';
|
||||||
|
antivirus?: Maybe<ConfigStorageAntivirus>;
|
||||||
/** Resources for the service */
|
/** Resources for the service */
|
||||||
resources?: Maybe<ConfigResources>;
|
resources?: Maybe<ConfigResources>;
|
||||||
/**
|
/**
|
||||||
@@ -1818,20 +1819,43 @@ export type ConfigStorage = {
|
|||||||
version?: Maybe<Scalars['String']>;
|
version?: Maybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ConfigStorageAntivirus = {
|
||||||
|
__typename?: 'ConfigStorageAntivirus';
|
||||||
|
server?: Maybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConfigStorageAntivirusComparisonExp = {
|
||||||
|
_and?: InputMaybe<Array<ConfigStorageAntivirusComparisonExp>>;
|
||||||
|
_not?: InputMaybe<ConfigStorageAntivirusComparisonExp>;
|
||||||
|
_or?: InputMaybe<Array<ConfigStorageAntivirusComparisonExp>>;
|
||||||
|
server?: InputMaybe<ConfigStringComparisonExp>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConfigStorageAntivirusInsertInput = {
|
||||||
|
server?: InputMaybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConfigStorageAntivirusUpdateInput = {
|
||||||
|
server?: InputMaybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type ConfigStorageComparisonExp = {
|
export type ConfigStorageComparisonExp = {
|
||||||
_and?: InputMaybe<Array<ConfigStorageComparisonExp>>;
|
_and?: InputMaybe<Array<ConfigStorageComparisonExp>>;
|
||||||
_not?: InputMaybe<ConfigStorageComparisonExp>;
|
_not?: InputMaybe<ConfigStorageComparisonExp>;
|
||||||
_or?: InputMaybe<Array<ConfigStorageComparisonExp>>;
|
_or?: InputMaybe<Array<ConfigStorageComparisonExp>>;
|
||||||
|
antivirus?: InputMaybe<ConfigStorageAntivirusComparisonExp>;
|
||||||
resources?: InputMaybe<ConfigResourcesComparisonExp>;
|
resources?: InputMaybe<ConfigResourcesComparisonExp>;
|
||||||
version?: InputMaybe<ConfigStringComparisonExp>;
|
version?: InputMaybe<ConfigStringComparisonExp>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConfigStorageInsertInput = {
|
export type ConfigStorageInsertInput = {
|
||||||
|
antivirus?: InputMaybe<ConfigStorageAntivirusInsertInput>;
|
||||||
resources?: InputMaybe<ConfigResourcesInsertInput>;
|
resources?: InputMaybe<ConfigResourcesInsertInput>;
|
||||||
version?: InputMaybe<Scalars['String']>;
|
version?: InputMaybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConfigStorageUpdateInput = {
|
export type ConfigStorageUpdateInput = {
|
||||||
|
antivirus?: InputMaybe<ConfigStorageAntivirusUpdateInput>;
|
||||||
resources?: InputMaybe<ConfigResourcesUpdateInput>;
|
resources?: InputMaybe<ConfigResourcesUpdateInput>;
|
||||||
version?: InputMaybe<Scalars['String']>;
|
version?: InputMaybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
@@ -19750,7 +19774,7 @@ export type GetStorageSettingsQueryVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetStorageSettingsQuery = { __typename?: 'query_root', config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', storage?: { __typename?: 'ConfigStorage', version?: string | null } | null } | null };
|
export type GetStorageSettingsQuery = { __typename?: 'query_root', config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', storage?: { __typename?: 'ConfigStorage', version?: string | null, antivirus?: { __typename?: 'ConfigStorageAntivirus', server?: string | null } | null } | null } | null };
|
||||||
|
|
||||||
export type DeleteApplicationMutationVariables = Exact<{
|
export type DeleteApplicationMutationVariables = Exact<{
|
||||||
appId: Scalars['uuid'];
|
appId: Scalars['uuid'];
|
||||||
@@ -21063,6 +21087,9 @@ export const GetStorageSettingsDocument = gql`
|
|||||||
__typename
|
__typename
|
||||||
storage {
|
storage {
|
||||||
version
|
version
|
||||||
|
antivirus {
|
||||||
|
server
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ httpPoolSize = 100
|
|||||||
version = 16
|
version = 16
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
version = '0.20.2'
|
version = '0.21.2'
|
||||||
|
|
||||||
[auth.redirections]
|
[auth.redirections]
|
||||||
clientUrl = 'http://localhost:3000'
|
clientUrl = 'https://react-apollo.example.nhost.io/'
|
||||||
|
|
||||||
[auth.signUp]
|
[auth.signUp]
|
||||||
enabled = true
|
enabled = true
|
||||||
@@ -94,13 +94,17 @@ enabled = false
|
|||||||
enabled = false
|
enabled = false
|
||||||
|
|
||||||
[auth.method.oauth.github]
|
[auth.method.oauth.github]
|
||||||
enabled = false
|
enabled = true
|
||||||
|
clientId = '{{ secrets.GITHUB_CLIENT_ID }}'
|
||||||
|
clientSecret = '{{ secrets.GITHUB_CLIENT_SECRET }}'
|
||||||
|
|
||||||
[auth.method.oauth.gitlab]
|
[auth.method.oauth.gitlab]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
|
||||||
[auth.method.oauth.google]
|
[auth.method.oauth.google]
|
||||||
enabled = false
|
enabled = true
|
||||||
|
clientId = '{{ secrets.GOOGLE_CLIENT_ID }}'
|
||||||
|
clientSecret = '{{ secrets.GOOGLE_CLIENT_SECRET }}'
|
||||||
|
|
||||||
[auth.method.oauth.linkedin]
|
[auth.method.oauth.linkedin]
|
||||||
enabled = false
|
enabled = false
|
||||||
@@ -124,7 +128,11 @@ enabled = false
|
|||||||
enabled = false
|
enabled = false
|
||||||
|
|
||||||
[auth.method.webauthn]
|
[auth.method.webauthn]
|
||||||
enabled = false
|
enabled = true
|
||||||
|
|
||||||
|
[auth.method.webauthn.relyingParty]
|
||||||
|
name = 'apollo-example'
|
||||||
|
origins = ['https://react-apollo.example.nhost.io']
|
||||||
|
|
||||||
[auth.method.webauthn.attestation]
|
[auth.method.webauthn.attestation]
|
||||||
timeout = 60000
|
timeout = 60000
|
||||||
|
|||||||
12
examples/react-apollo/nhost/overlays/local.json
Normal file
12
examples/react-apollo/nhost/overlays/local.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/auth/method/webauthn/relyingParty/origins/0",
|
||||||
|
"value": "http://localhost:3000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/auth/redirections/clientUrl",
|
||||||
|
"value": "http://localhost:3000"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -8,15 +8,15 @@
|
|||||||
outputs = { self, nixpkgs, flake-utils, nix-filter }:
|
outputs = { self, nixpkgs, flake-utils, nix-filter }:
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
version = "v1.1.0";
|
version = "v1.5.2";
|
||||||
dist = {
|
dist = {
|
||||||
aarch64-darwin = rec {
|
aarch64-darwin = rec {
|
||||||
url = "https://github.com/nhost/cli/releases/download/${version}/cli-${version}-darwin-arm64.tar.gz";
|
url = "https://github.com/nhost/cli/releases/download/${version}/cli-${version}-darwin-arm64.tar.gz";
|
||||||
sha256 = "sha256-tF40CEkA357yzg2Gmc9ubjHJ5FlI9qQTdVdWNY/+f+Y=";
|
sha256 = "0rakx9lpbj5m3jfra5r2iw065x800i951843l3mxbwk9m1hzm185";
|
||||||
};
|
};
|
||||||
x86_64-linux = rec {
|
x86_64-linux = rec {
|
||||||
url = "https://github.com/nhost/cli/releases/download/${version}/cli-${version}-linux-amd64.tar.gz";
|
url = "https://github.com/nhost/cli/releases/download/${version}/cli-${version}-linux-amd64.tar.gz";
|
||||||
sha256 = "sha256-KLv06dI7A+5KGJ5F8xM1qC+oqHRmJ4kMaifLvaTFqak=";
|
sha256 = "19x83fpvazwzpnrxi0qh6mh14wwhlw77qd7vvc3633dxa5hdigc4";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
overlays = [
|
overlays = [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/apollo
|
# @nhost/apollo
|
||||||
|
|
||||||
|
## 5.2.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/nhost-js@2.2.14
|
||||||
|
|
||||||
## 5.2.15
|
## 5.2.15
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/apollo",
|
"name": "@nhost/apollo",
|
||||||
"version": "5.2.15",
|
"version": "5.2.16",
|
||||||
"description": "Nhost Apollo Client library",
|
"description": "Nhost Apollo Client library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @nhost/react-apollo
|
# @nhost/react-apollo
|
||||||
|
|
||||||
|
## 5.0.33
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/apollo@5.2.16
|
||||||
|
- @nhost/react@2.0.29
|
||||||
|
|
||||||
## 5.0.32
|
## 5.0.32
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/react-apollo",
|
"name": "@nhost/react-apollo",
|
||||||
"version": "5.0.32",
|
"version": "5.0.33",
|
||||||
"description": "Nhost React Apollo client",
|
"description": "Nhost React Apollo client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/react-urql
|
# @nhost/react-urql
|
||||||
|
|
||||||
|
## 2.0.30
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/react@2.0.29
|
||||||
|
|
||||||
## 2.0.29
|
## 2.0.29
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/react-urql",
|
"name": "@nhost/react-urql",
|
||||||
"version": "2.0.29",
|
"version": "2.0.30",
|
||||||
"description": "Nhost React URQL client",
|
"description": "Nhost React URQL client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/hasura-storage-js
|
# @nhost/hasura-storage-js
|
||||||
|
|
||||||
|
## 2.2.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 39de0063b: fix(hasura-storage-js): fix upload response status code check
|
||||||
|
|
||||||
## 2.2.2
|
## 2.2.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/hasura-storage-js",
|
"name": "@nhost/hasura-storage-js",
|
||||||
"version": "2.2.2",
|
"version": "2.2.3",
|
||||||
"description": "Hasura-storage client",
|
"description": "Hasura-storage client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -87,10 +87,14 @@ export const fetchUpload = async (
|
|||||||
xhr.responseType = 'json'
|
xhr.responseType = 'json'
|
||||||
|
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
if (xhr.status < 200 && xhr.status >= 300) {
|
if (xhr.status < 200 || xhr.status >= 300) {
|
||||||
return resolve({
|
return resolve({
|
||||||
fileMetadata: null,
|
fileMetadata: null,
|
||||||
error: { error: xhr.statusText, message: xhr.statusText, status: xhr.status }
|
error: {
|
||||||
|
error: xhr.response?.error ?? xhr.response,
|
||||||
|
message: xhr.response?.error?.message ?? xhr.response,
|
||||||
|
status: xhr.status
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return resolve({ fileMetadata: xhr.response, error: null })
|
return resolve({ fileMetadata: xhr.response, error: null })
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/nextjs
|
# @nhost/nextjs
|
||||||
|
|
||||||
|
## 1.13.35
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/react@2.0.29
|
||||||
|
|
||||||
## 1.13.34
|
## 1.13.34
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/nextjs",
|
"name": "@nhost/nextjs",
|
||||||
"version": "1.13.34",
|
"version": "1.13.35",
|
||||||
"description": "Nhost NextJS library",
|
"description": "Nhost NextJS library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @nhost/nhost-js
|
# @nhost/nhost-js
|
||||||
|
|
||||||
|
## 2.2.14
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [39de0063b]
|
||||||
|
- @nhost/hasura-storage-js@2.2.3
|
||||||
|
|
||||||
## 2.2.13
|
## 2.2.13
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/nhost-js",
|
"name": "@nhost/nhost-js",
|
||||||
"version": "2.2.13",
|
"version": "2.2.14",
|
||||||
"description": "Nhost JavaScript SDK",
|
"description": "Nhost JavaScript SDK",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/react
|
# @nhost/react
|
||||||
|
|
||||||
|
## 2.0.29
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/nhost-js@2.2.14
|
||||||
|
|
||||||
## 2.0.28
|
## 2.0.28
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/react",
|
"name": "@nhost/react",
|
||||||
"version": "2.0.28",
|
"version": "2.0.29",
|
||||||
"description": "Nhost React library",
|
"description": "Nhost React library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/vue
|
# @nhost/vue
|
||||||
|
|
||||||
|
## 1.13.34
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/nhost-js@2.2.14
|
||||||
|
|
||||||
## 1.13.33
|
## 1.13.33
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/vue",
|
"name": "@nhost/vue",
|
||||||
"version": "1.13.33",
|
"version": "1.13.34",
|
||||||
"description": "Nhost Vue library",
|
"description": "Nhost Vue library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
Reference in New Issue
Block a user