diff --git a/apps/builder/src/features/workspace/WorkspaceProvider.tsx b/apps/builder/src/features/workspace/WorkspaceProvider.tsx index 107d5aa01..a2da3006c 100644 --- a/apps/builder/src/features/workspace/WorkspaceProvider.tsx +++ b/apps/builder/src/features/workspace/WorkspaceProvider.tsx @@ -6,7 +6,7 @@ import { useMemo, useState, } from 'react' -import { byId } from '@typebot.io/lib' +import { byId, isNotDefined } from '@typebot.io/lib' import { WorkspaceRole } from '@typebot.io/prisma' import { useRouter } from 'next/router' import { trpc } from '@/lib/trpc' @@ -39,7 +39,7 @@ export const WorkspaceProvider = ({ typebotId, children, }: WorkspaceContextProps) => { - const { query } = useRouter() + const { pathname, query, push } = useRouter() const { user } = useUser() const userId = user?.id const [workspaceId, setWorkspaceId] = useState() @@ -132,9 +132,18 @@ export const WorkspaceProvider = ({ workspaces, ]) + useEffect(() => { + if (isNotDefined(workspace?.isSuspended)) return + if (workspace?.isSuspended && pathname !== '/suspended') push('/suspended') + }, [pathname, push, workspace?.isSuspended]) + const switchWorkspace = (workspaceId: string) => { - setWorkspaceId(workspaceId) setWorkspaceIdInLocalStorage(workspaceId) + if (pathname === '/suspended') { + window.location.href = '/typebots' + return + } + setWorkspaceId(workspaceId) } const createWorkspace = async (userFullName?: string) => { diff --git a/apps/builder/src/pages/suspended.tsx b/apps/builder/src/pages/suspended.tsx new file mode 100644 index 000000000..947c6ab45 --- /dev/null +++ b/apps/builder/src/pages/suspended.tsx @@ -0,0 +1,31 @@ +import { TextLink } from '@/components/TextLink' +import { DashboardHeader } from '@/features/dashboard/components/DashboardHeader' +import { WorkspaceProvider } from '@/features/workspace/WorkspaceProvider' +import { Heading, Link, Text, VStack } from '@chakra-ui/react' + +export default function Page() { + return ( + + + + Your workspace has been suspended. + + We detected that one of your typebots does not comply with our{' '} + + terms of service + + + + If you think it's a mistake, feel free to{' '} + + reach out + + . + + + + ) +} diff --git a/apps/docs/openapi/builder/_spec_.json b/apps/docs/openapi/builder/_spec_.json index 02d12f7a5..01c136a4d 100644 --- a/apps/docs/openapi/builder/_spec_.json +++ b/apps/docs/openapi/builder/_spec_.json @@ -3900,6 +3900,9 @@ }, "isQuarantined": { "type": "boolean" + }, + "isSuspended": { + "type": "boolean" } }, "required": [ @@ -3919,7 +3922,8 @@ "customChatsLimit", "customStorageLimit", "customSeatsLimit", - "isQuarantined" + "isQuarantined", + "isSuspended" ], "additionalProperties": false } @@ -4045,6 +4049,9 @@ }, "isQuarantined": { "type": "boolean" + }, + "isSuspended": { + "type": "boolean" } }, "required": [ @@ -4064,7 +4071,8 @@ "customChatsLimit", "customStorageLimit", "customSeatsLimit", - "isQuarantined" + "isQuarantined", + "isSuspended" ], "additionalProperties": false } @@ -4207,6 +4215,9 @@ }, "isQuarantined": { "type": "boolean" + }, + "isSuspended": { + "type": "boolean" } }, "required": [ @@ -4226,7 +4237,8 @@ "customChatsLimit", "customStorageLimit", "customSeatsLimit", - "isQuarantined" + "isQuarantined", + "isSuspended" ], "additionalProperties": false } @@ -5472,6 +5484,9 @@ }, "isQuarantined": { "type": "boolean" + }, + "isSuspended": { + "type": "boolean" } }, "required": [ @@ -5491,7 +5506,8 @@ "customChatsLimit", "customStorageLimit", "customSeatsLimit", - "isQuarantined" + "isQuarantined", + "isSuspended" ], "additionalProperties": false, "nullable": true diff --git a/apps/landing-page/pages/terms-of-service.tsx b/apps/landing-page/pages/terms-of-service.tsx index 5b26c54ab..3195e4e52 100644 --- a/apps/landing-page/pages/terms-of-service.tsx +++ b/apps/landing-page/pages/terms-of-service.tsx @@ -1,6 +1,7 @@ import { Stack, Heading } from '@chakra-ui/react' import { Header } from 'components/common/Header/Header' import { SocialMetaTags } from 'components/common/SocialMetaTags' +import { TextLink } from 'components/common/TextLink' const PrivacyPolicies = () => { return ( @@ -117,11 +118,26 @@ const PrivacyPolicies = () => { bound by the current version of these Terms and Conditions of Use.

- 8. Your Privacy + 8. Prohibition of Scam Typebots +

+ You agree not to create or use typebots on Typebot's Website for + the purpose of engaging in fraudulent activities, scamming + individuals, or any other unethical or illegal activities. This + includes but is not limited to typebots designed to deceive, defraud, + or mislead people for financial gain or personal benefit. Typebot + reserves the right to take appropriate action, including the + termination of any user account, if it determines that a typebot is + being used in violation of this provision. +

-

Please read our Privacy Policy.

+ 9. Your Privacy - 9. Governing Law +

+ Please read our{' '} + Privacy Policy. +

+ + 10. Governing Law

Any claim related to Typebot's Website shall be governed by the diff --git a/apps/viewer/src/features/chat/api/sendMessage.ts b/apps/viewer/src/features/chat/api/sendMessage.ts index fe52ea08d..d16407ad8 100644 --- a/apps/viewer/src/features/chat/api/sendMessage.ts +++ b/apps/viewer/src/features/chat/api/sendMessage.ts @@ -301,6 +301,7 @@ const getTypebot = async ( additionalChatsIndex: true, customChatsLimit: true, isQuarantined: true, + isSuspended: true, }, }, }, @@ -326,12 +327,16 @@ const getTypebot = async ( message: 'Typebot not found', }) - const isQuarantined = + const isQuarantinedOrSuspended = typebotQuery && 'typebot' in typebotQuery && - typebotQuery.typebot.workspace.isQuarantined + (typebotQuery.typebot.workspace.isQuarantined || + typebotQuery.typebot.workspace.isSuspended) - if (('isClosed' in parsedTypebot && parsedTypebot.isClosed) || isQuarantined) + if ( + ('isClosed' in parsedTypebot && parsedTypebot.isClosed) || + isQuarantinedOrSuspended + ) throw new TRPCError({ code: 'BAD_REQUEST', message: 'Typebot is closed', diff --git a/packages/prisma/mysql/schema.prisma b/packages/prisma/mysql/schema.prisma index 8357e1d38..aee67d04a 100644 --- a/packages/prisma/mysql/schema.prisma +++ b/packages/prisma/mysql/schema.prisma @@ -97,6 +97,7 @@ model Workspace { customStorageLimit Int? customSeatsLimit Int? isQuarantined Boolean @default(false) + isSuspended Boolean @default(false) themeTemplates ThemeTemplate[] } diff --git a/packages/prisma/postgresql/migrations/20230704125011_add_is_suspended_prop/migration.sql b/packages/prisma/postgresql/migrations/20230704125011_add_is_suspended_prop/migration.sql new file mode 100644 index 000000000..2168029c4 --- /dev/null +++ b/packages/prisma/postgresql/migrations/20230704125011_add_is_suspended_prop/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Workspace" ADD COLUMN "isSuspended" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/prisma/postgresql/schema.prisma b/packages/prisma/postgresql/schema.prisma index 291c7a7f1..e71f17f98 100644 --- a/packages/prisma/postgresql/schema.prisma +++ b/packages/prisma/postgresql/schema.prisma @@ -91,6 +91,7 @@ model Workspace { customStorageLimit Int? customSeatsLimit Int? isQuarantined Boolean @default(false) + isSuspended Boolean @default(false) themeTemplates ThemeTemplate[] } diff --git a/packages/schemas/features/workspace.ts b/packages/schemas/features/workspace.ts index a4080227a..525f42e83 100644 --- a/packages/schemas/features/workspace.ts +++ b/packages/schemas/features/workspace.ts @@ -49,6 +49,7 @@ export const workspaceSchema = z.object({ customStorageLimit: z.number().nullable(), customSeatsLimit: z.number().nullable(), isQuarantined: z.boolean(), + isSuspended: z.boolean(), }) satisfies z.ZodType export type Workspace = z.infer