feat: suspicious registartion

This commit is contained in:
Nevo David 2025-08-09 20:22:38 +07:00
parent caf99d38c5
commit a8e32dc399
8 changed files with 93 additions and 17 deletions

View File

@ -1,13 +1,5 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Query,
UseFilters,
Body, Controller, Delete, Get, HttpException, Param, Post, Put, Query, UseFilters
} from '@nestjs/common';
import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service';
import { ConnectIntegrationDto } from '@gitroom/nestjs-libraries/dtos/integrations/connect.integration.dto';
@ -261,7 +253,7 @@ export class IntegrationsController {
throw new Error('Invalid integration');
}
let newList: any[] | {none: true} = [];
let newList: any[] | { none: true } = [];
try {
newList = (await this.functionIntegration(org, body)) || [];
} catch (err) {
@ -298,7 +290,7 @@ export class IntegrationsController {
image: p.image,
label: p.name,
})),
...newList as any[],
...(newList as any[]),
],
(p) => p.id
).filter((f) => f.label && f.id);
@ -487,6 +479,18 @@ export class IntegrationsController {
validName = `Channel_${String(id).slice(0, 8)}`;
}
}
if (
process.env.STRIPE_PUBLISHABLE_KEY &&
org.isTrailing &&
!!(await this._integrationService.checkPreviousConnections(
org.id,
String(id)
))
) {
throw new HttpException('', 412);
}
return this._integrationService.createOrUpdateIntegration(
additionalSettings,
!!integrationProvider.oneTimeToken,

View File

@ -1,14 +1,14 @@
'use client';
import { useModals } from '@mantine/modals';
import React, { FC, useCallback, useMemo } from 'react';
import React, { FC, useCallback, useEffect, useMemo } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { Input } from '@gitroom/react/form/input';
import { FieldValues, FormProvider, useForm } from 'react-hook-form';
import { Button } from '@gitroom/react/form/button';
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { ApiKeyDto } from '@gitroom/nestjs-libraries/dtos/integrations/api.key.dto';
import { useRouter } from 'next/navigation';
import { useRouter, useSearchParams } from 'next/navigation';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { useVariables } from '@gitroom/react/helpers/variable.context';
import { useToaster } from '@gitroom/react/toaster/toaster';
@ -42,9 +42,16 @@ export const AddProviderButton: FC<{
update?: () => void;
}> = (props) => {
const { update } = props;
const query = useSearchParams();
const add = useAddProvider(update);
const t = useT();
useEffect(() => {
if (query.get('onboarding')) {
add();
}
}, []);
return (
<button
className="text-btnText bg-btnSimple h-[44px] pt-[12px] pb-[14px] ps-[16px] pe-[20px] justify-center items-center flex rounded-[8px] gap-[8px]"

View File

@ -40,9 +40,14 @@ export const ContinueIntegration: FC<{
const data = await fetch(`/integrations/social/${provider}/connect`, {
method: 'POST',
body: JSON.stringify({...modifiedParams, timezone}),
body: JSON.stringify({ ...modifiedParams, timezone }),
});
if (data.status === HttpStatusCode.PreconditionFailed) {
push(`/launches?precondition=true`);
return ;
}
if (data.status === HttpStatusCode.NotAcceptable) {
const { msg } = await data.json();
push(`/launches?msg=${msg}`);

View File

@ -84,7 +84,6 @@ export const LayoutSettings = ({ children }: { children: ReactNode }) => {
<Toaster />
<ShowPostSelector />
<NewSubscription />
{user.tier !== 'FREE' && <Onboarding />}
<Support />
<ContinueProvider />
<div className="min-h-[100vh] w-full max-w-[1440px] mx-auto bg-primary px-6 text-textColor flex flex-col">

View File

@ -0,0 +1,43 @@
import React, { FC, useEffect } from 'react';
import { useSearchParams } from 'next/navigation';
import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component';
import { useModals } from '@mantine/modals';
import { Button } from '@gitroom/react/form/button';
export const PreConditionComponentModal: FC = () => {
return (
<div className="flex flex-col gap-[16px]">
<div className="whitespace-pre-line">
This social channel was connected previously to another Postiz account.{'\n'}
To continue, please fast-track your trial for an immediate charge.{'\n'}{'\n'}
** Please be advised that the account will not eligible for a refund, and the charge is final.
</div>
<div className="flex gap-[2px] justify-center">
<Button onClick={() => window.location.href='/billing?finishTrial=true'}>Fast track - Charge me now</Button>
<Button secondary={true}>Cancel</Button>
</div>
</div>
);
};
export const PreConditionComponent: FC = () => {
const modal = useModals();
const query = useSearchParams();
useEffect(() => {
if (query.get('precondition')) {
modal.openModal({
title: '',
withCloseButton: false,
classNames: {
modal: 'text-textColor',
},
size: 'auto',
children: (
<ModalWrapperComponent title="Suspicious activity detected">
<PreConditionComponentModal />
</ModalWrapperComponent>
),
});
}
}, []);
return null;
};

View File

@ -38,6 +38,7 @@ import { ChromeExtensionComponent } from '@gitroom/frontend/components/layout/ch
import NotificationComponent from '@gitroom/frontend/components/notifications/notification.component';
import { BillingAfter } from '@gitroom/frontend/components/new-layout/billing.after';
import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector';
import { PreConditionComponent } from '@gitroom/frontend/components/layout/pre-condition.component';
const jakartaSans = Plus_Jakarta_Sans({
weight: ['600', '500'],
@ -79,8 +80,8 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => {
<MediaSettingsLayout />
<Toaster />
<ShowPostSelector />
<PreConditionComponent />
<NewSubscription />
{user.tier !== 'FREE' && <Onboarding />}
<Support />
<ContinueProvider />
<div

View File

@ -55,7 +55,7 @@ export class IntegrationRepository {
mentions: { name: string; username: string; image: string }[]
) {
if (mentions.length === 0) {
return [];
return [] as any[];
}
return this._mentions.model.mentions.createMany({
data: mentions.map((mention) => ({
@ -68,6 +68,19 @@ export class IntegrationRepository {
});
}
async checkPreviousConnections(org: string, id: string) {
const findIt = await this._integration.model.integration.findFirst({
where: {
organizationId: {
not: org,
},
rootInternalId: id.split('_').pop(),
},
});
return findIt;
}
updateProviderSettings(org: string, id: string, settings: string) {
return this._integration.model.integration.update({
where: {

View File

@ -73,6 +73,10 @@ export class IntegrationService {
);
}
checkPreviousConnections(org: string, id: string) {
return this._integrationRepository.checkPreviousConnections(org, id);
}
async createOrUpdateIntegration(
additionalSettings:
| {