@@ -11,20 +87,38 @@ export const Seller = () => {
John Smith
-
+ {data?.connectedAccount && (
+
+ )}
-
+
Details
-
- asdfasdf
+
+ {tagsList.map((tag) => (
+ key.key)}
+ search={false}
+ options={tag.options}
+ title={tag.name}
+ />
+ ))}
diff --git a/libraries/nestjs-libraries/src/database/prisma/database.module.ts b/libraries/nestjs-libraries/src/database/prisma/database.module.ts
index 1d294d5c..402e72a0 100644
--- a/libraries/nestjs-libraries/src/database/prisma/database.module.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/database.module.ts
@@ -20,6 +20,8 @@ import { CommentsRepository } from '@gitroom/nestjs-libraries/database/prisma/co
import { CommentsService } from '@gitroom/nestjs-libraries/database/prisma/comments/comments.service';
import { NotificationsRepository } from '@gitroom/nestjs-libraries/database/prisma/notifications/notifications.repository';
import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
+import { ItemUserRepository } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.repository';
+import { ItemUserService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.service';
@Global()
@Module({
@@ -45,6 +47,8 @@ import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
MediaService,
MediaRepository,
CommentsRepository,
+ ItemUserRepository,
+ ItemUserService,
CommentsService,
IntegrationManager,
EmailService,
diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts
new file mode 100644
index 00000000..132c11d3
--- /dev/null
+++ b/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts
@@ -0,0 +1,43 @@
+import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service';
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class ItemUserRepository {
+ constructor(
+ private _itemUser: PrismaRepository<'itemUser'>,
+ ) {}
+
+ addOrRemoveItem(add: boolean, userId: string, item: string) {
+ if (!add) {
+ return this._itemUser.model.itemUser.deleteMany({
+ where: {
+ user: {
+ id: userId,
+ },
+ key: item,
+ },
+ });
+ }
+
+ return this._itemUser.model.itemUser.create({
+ data: {
+ key: item,
+ user: {
+ connect: {
+ id: userId,
+ },
+ },
+ },
+ });
+ }
+
+ getItems(userId: string) {
+ return this._itemUser.model.itemUser.findMany({
+ where: {
+ user: {
+ id: userId,
+ },
+ },
+ });
+ }
+}
diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts
new file mode 100644
index 00000000..4fd530e4
--- /dev/null
+++ b/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@nestjs/common';
+import { ItemUserRepository } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.repository';
+
+@Injectable()
+export class ItemUserService {
+ constructor(
+ private _itemUserRepository: ItemUserRepository,
+ ) {}
+
+ addOrRemoveItem(add: boolean, userId: string, item: string) {
+ return this._itemUserRepository.addOrRemoveItem(add, userId, item);
+ }
+
+ getItems(userId: string) {
+ return this._itemUserRepository.getItems(userId);
+ }
+}
diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts
new file mode 100644
index 00000000..8d1a43fc
--- /dev/null
+++ b/libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts
@@ -0,0 +1,111 @@
+export const tagsList = [
+ {
+ key: 'services',
+ name: 'Services',
+ options: [
+ {
+ key: 'content-writer',
+ value: 'Content Writer'
+ },
+ {
+ key: 'influencers',
+ value: 'Influencers'
+ }
+ ]
+ },
+ {
+ key: 'niches',
+ name: 'Niches',
+ options: [
+ {
+ key: 'kubernetes',
+ value: 'Kubernetes'
+ },
+ {
+ key: 'fullstack',
+ value: 'Fullstack'
+ },
+ {
+ key: 'security',
+ value: 'Security'
+ },
+ {
+ key: 'infrastructure',
+ value: 'Infrastructure'
+ },
+ {
+ key: 'productivity',
+ value: 'Productivity'
+ },
+ {
+ key: 'web3',
+ value: 'Web3'
+ },
+ {
+ key: 'cloud-native',
+ value: 'Cloud Native'
+ },
+ {
+ key: 'ml',
+ value: 'ML'
+ }
+ ]
+ },
+ {
+ key: 'technologies',
+ name: 'Technologies',
+ options: [
+ {
+ key: 'html',
+ value: 'HTML'
+ },
+ {
+ key: 'css',
+ value: 'CSS'
+ },
+ {
+ key: 'javascript',
+ value: 'JavaScript'
+ },
+ {
+ key: 'typescript',
+ value: 'TypeScript'
+ },
+ {
+ key: 'rust',
+ value: 'Rust'
+ },
+ {
+ key: 'go',
+ value: 'Go'
+ },
+ {
+ key: 'python',
+ value: 'Python'
+ },
+ {
+ key: 'java',
+ value: 'Java'
+ },
+ {
+ key: 'php',
+ value: 'PHP'
+ },
+ {
+ key: 'ruby',
+ value: 'Ruby'
+ },
+ {
+ key: 'c',
+ value: 'C/C++'
+ }
+ ]
+ }
+];
+
+export const allTagsOptions = tagsList.reduce((acc, tag) => {
+ return [
+ ...acc,
+ ...tag.options
+ ]
+}, [] as Array<{key: string, value: string}>);
\ No newline at end of file
diff --git a/libraries/nestjs-libraries/src/database/prisma/prisma.service.ts b/libraries/nestjs-libraries/src/database/prisma/prisma.service.ts
index 05d751b5..7e1755d1 100644
--- a/libraries/nestjs-libraries/src/database/prisma/prisma.service.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/prisma.service.ts
@@ -3,16 +3,25 @@ import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
- async onModuleInit() {
- await this.$connect();
- }
+ constructor() {
+ super({
+ log: [
+ {
+ emit: 'event',
+ level: 'query',
+ },
+ ],
+ });
+ }
+ async onModuleInit() {
+ await this.$connect();
+ }
}
@Injectable()
export class PrismaRepository
{
- public model: Pick;
- constructor(private _prismaService: PrismaService) {
- this.model = this._prismaService;
- }
+ public model: Pick;
+ constructor(private _prismaService: PrismaService) {
+ this.model = this._prismaService;
+ }
}
-
diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma
index 493ae4bd..575d9fa3 100644
--- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma
+++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma
@@ -34,6 +34,7 @@ model User {
providerName Provider
name String?
lastName String?
+ bio String?
pictureId String?
picture Media? @relation(fields: [pictureId], references: [id])
providerId String?
@@ -44,11 +45,15 @@ model User {
updatedAt DateTime @updatedAt
lastReadNotifications DateTime @default(now())
inviteId String?
- tagUser TagUser[]
+ items ItemUser[]
+ marketplace Boolean @default(true)
+ account String?
+ connectedAccount Boolean @default(false)
@@unique([email, providerName])
@@index([lastReadNotifications])
@@index([inviteId])
+ @@index([account])
}
model UserOrganization {
@@ -99,19 +104,15 @@ model TrendingLog {
date DateTime
}
-model TagUser {
+model ItemUser {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id])
userId String
- tagOptions TagOptions @relation(fields: [tagOptionsId], references: [id])
- tagOptionsId String
-}
-
-model TagOptions {
- id String @id @default(uuid())
key String
- value String
- tagUser TagUser[]
+
+ @@index([userId])
+ @@index([key])
+ @@unique([userId, key])
}
model Star {
diff --git a/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.repository.ts b/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.repository.ts
index dc9458aa..9f088cb3 100644
--- a/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.repository.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.repository.ts
@@ -5,9 +5,32 @@ import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/pris
export class SubscriptionRepository {
constructor(
private readonly _subscription: PrismaRepository<'subscription'>,
- private readonly _organization: PrismaRepository<'organization'>
+ private readonly _organization: PrismaRepository<'organization'>,
+ private readonly _user: PrismaRepository<'user'>
) {}
+ getUserAccount(userId: string) {
+ return this._user.model.user.findFirst({
+ where: {
+ id: userId,
+ },
+ select: {
+ account: true,
+ },
+ });
+ }
+
+ updateAccount(userId: string, account: string) {
+ return this._user.model.user.update({
+ where: {
+ id: userId,
+ },
+ data: {
+ account,
+ },
+ });
+ }
+
getSubscriptionByOrganizationId(organizationId: string) {
return this._subscription.model.subscription.findFirst({
where: {
@@ -17,6 +40,17 @@ export class SubscriptionRepository {
});
}
+ updateConnectedStatus(account: string, accountCharges: boolean) {
+ return this._user.model.user.updateMany({
+ where: {
+ account
+ },
+ data: {
+ connectedAccount: accountCharges,
+ },
+ });
+ }
+
checkSubscription(organizationId: string, subscriptionId: string) {
return this._subscription.model.subscription.findFirst({
where: {
diff --git a/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts b/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts
index 12f1a41a..fd1a454c 100644
--- a/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts
@@ -18,6 +18,14 @@ export class SubscriptionService {
);
}
+ updateAccount(userId: string, account: string) {
+ return this._subscriptionRepository.updateAccount(userId, account);
+ }
+
+ getUserAccount(userId: string) {
+ return this._subscriptionRepository.getUserAccount(userId);
+ }
+
async deleteSubscription(customerId: string) {
await this.modifySubscription(
customerId,
@@ -43,6 +51,10 @@ export class SubscriptionService {
);
}
+ updateConnectedStatus(account: string, accountCharges: boolean) {
+ return this._subscriptionRepository.updateConnectedStatus(account, accountCharges);
+ }
+
async modifySubscription(
customerId: string,
totalChannels: number,
diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts
index e18e8f61..cc822e26 100644
--- a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts
@@ -1,7 +1,9 @@
import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service';
import { Injectable } from '@nestjs/common';
import { Provider } from '@prisma/client';
-import {AuthService} from "@gitroom/helpers/auth/auth.service";
+import { AuthService } from '@gitroom/helpers/auth/auth.service';
+import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto';
+import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list';
@Injectable()
export class UsersRepository {
@@ -35,4 +37,106 @@ export class UsersRepository {
},
});
}
+
+ changeMarketplaceActive(userId: string, active: boolean) {
+ return this._user.model.user.update({
+ where: {
+ id: userId,
+ },
+ data: {
+ marketplace: active,
+ },
+ });
+ }
+
+ async getPersonal(userId: string) {
+ const user = await this._user.model.user.findUnique({
+ where: {
+ id: userId,
+ },
+ select: {
+ id: true,
+ name: true,
+ bio: true,
+ picture: {
+ select: {
+ id: true,
+ path: true,
+ },
+ },
+ },
+ });
+
+ return user;
+ }
+
+ async getMarketplacePeople(orgId: string, userId: string, items: ItemsDto) {
+ const info = {
+ id: {
+ not: userId,
+ },
+ account: {
+ not: null,
+ },
+ connectedAccount: true,
+ marketplace: true,
+ items: {
+ ...(items.items.length
+ ? {
+ some: {
+ OR: items.items.map((key) => ({ key })),
+ },
+ }
+ : {
+ some: {
+ OR: allTagsOptions.map((p) => ({ key: p.key })),
+ },
+ }),
+ },
+ };
+
+ const list = await this._user.model.user.findMany({
+ where: {
+ ...info,
+ },
+ select: {
+ name: true,
+ organizations: {
+ select: {
+ organization: {
+ select: {
+ Integration: {
+ where: {
+ disabled: false,
+ deletedAt: null,
+ },
+ select: {
+ providerIdentifier: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ items: {
+ select: {
+ key: true,
+ },
+ },
+ },
+ skip: (items.page - 1) * 10,
+ take: 10,
+ });
+
+ const count = await this._user.model.user.count({
+ where: {
+ ...info,
+ },
+ });
+
+ return {
+ list,
+ count,
+ };
+ }
}
diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts
index d8e6a609..3f183ce4 100644
--- a/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { UsersRepository } from '@gitroom/nestjs-libraries/database/prisma/users/users.repository';
import { Provider } from '@prisma/client';
+import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto';
@Injectable()
export class UsersService {
@@ -17,4 +18,16 @@ export class UsersService {
updatePassword(id: string, password: string) {
return this._usersRepository.updatePassword(id, password);
}
+
+ changeMarketplaceActive(userId: string, active: boolean) {
+ return this._usersRepository.changeMarketplaceActive(userId, active);
+ }
+
+ getMarketplacePeople(orgId: string, userId: string, body: ItemsDto) {
+ return this._usersRepository.getMarketplacePeople(orgId, userId, body);
+ }
+
+ getPersonal(userId: string) {
+ return this._usersRepository.getPersonal(userId);
+ }
}
diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts
new file mode 100644
index 00000000..3d3696ec
--- /dev/null
+++ b/libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts
@@ -0,0 +1,11 @@
+import { IsBoolean, IsIn, IsString } from 'class-validator';
+import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list';
+
+export class AddRemoveItemDto {
+ @IsString()
+ @IsIn(allTagsOptions.map(p => p.key))
+ key: string;
+
+ @IsBoolean()
+ state: boolean;
+}
diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts
new file mode 100644
index 00000000..b19d4b2d
--- /dev/null
+++ b/libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts
@@ -0,0 +1,6 @@
+import { IsBoolean } from 'class-validator';
+
+export class ChangeActiveDto {
+ @IsBoolean()
+ active: boolean;
+}
\ No newline at end of file
diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts
new file mode 100644
index 00000000..5e56d108
--- /dev/null
+++ b/libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts
@@ -0,0 +1,12 @@
+import { IsArray, IsIn, IsNumber, Min } from 'class-validator';
+import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list';
+
+export class ItemsDto {
+ @IsArray()
+ @IsIn(allTagsOptions.map(p => p.key), {each: true})
+ items: string[];
+
+ @IsNumber()
+ @Min(1)
+ page: number;
+}
diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts
index 08477b56..f09e2687 100644
--- a/libraries/nestjs-libraries/src/services/stripe.service.ts
+++ b/libraries/nestjs-libraries/src/services/stripe.service.ts
@@ -8,7 +8,7 @@ import { BillingSubscribeDto } from '@gitroom/nestjs-libraries/dtos/billing/bill
import { groupBy } from 'lodash';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
- apiVersion: '2023-10-16',
+ apiVersion: '2024-04-10',
});
@Injectable()
@@ -20,6 +20,17 @@ export class StripeService {
validateRequest(rawBody: Buffer, signature: string, endpointSecret: string) {
return stripe.webhooks.constructEvent(rawBody, signature, endpointSecret);
}
+
+ async updateAccount(event: Stripe.AccountUpdatedEvent) {
+ if (!event.account) {
+ return ;
+ }
+
+ console.log(JSON.stringify(event.data.object, null, 2));
+ const accountCharges = event.data.object.payouts_enabled && event.data.object.payouts_enabled && !event?.data?.object?.requirements?.disabled_reason;
+ await this._subscriptionService.updateConnectedStatus(event.account!, accountCharges);
+ }
+
createSubscription(event: Stripe.CustomerSubscriptionCreatedEvent) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -237,6 +248,48 @@ export class StripeService {
return { url };
}
+ async createAccountProcess(userId: string, email: string) {
+ const account =
+ (await this._subscriptionService.getUserAccount(userId))?.account ||
+ (await this.createAccount(userId, email));
+ return { url: await this.addBankAccount(account) };
+ }
+
+ async createAccount(userId: string, email: string) {
+ const account = await stripe.accounts.create({
+ controller: {
+ stripe_dashboard: {
+ type: 'express',
+ },
+ fees: {
+ payer: 'application',
+ },
+ losses: {
+ payments: 'application',
+ },
+ },
+ metadata: {
+ service: 'gitroom',
+ },
+ email
+ });
+
+ await this._subscriptionService.updateAccount(userId, account.id);
+
+ return account.id;
+ }
+
+ async addBankAccount(userId: string) {
+ const accountLink = await stripe.accountLinks.create({
+ account: userId,
+ refresh_url: process.env['FRONTEND_URL'] + '/marketplace/seller',
+ return_url: process.env['FRONTEND_URL'] + '/marketplace/seller',
+ type: 'account_onboarding',
+ });
+
+ return accountLink.url;
+ }
+
async subscribe(organizationId: string, body: BillingSubscribeDto) {
const id = makeId(10);
diff --git a/package-lock.json b/package-lock.json
index afde30f7..52e4f842 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -85,7 +85,7 @@
"rxjs": "^7.8.0",
"sharp": "^0.33.2",
"simple-statistics": "^7.8.3",
- "stripe": "^14.14.0",
+ "stripe": "^15.5.0",
"sweetalert2": "^11.6.13",
"swr": "^2.2.5",
"tslib": "^2.3.0",
@@ -37065,9 +37065,9 @@
}
},
"node_modules/stripe": {
- "version": "14.20.0",
- "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.20.0.tgz",
- "integrity": "sha512-+3EP8GSWnKVHNATChhDzwAKk3nqSJKQOf2Q+dMGdgEk2sQXWYoA8GXY0A1TjL0m6895FVNavgvno6+0+6lC+kw==",
+ "version": "15.5.0",
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-15.5.0.tgz",
+ "integrity": "sha512-c04ToET4ZUzoeSh2rWarXCPNa2+6YzkwNAcWaT4axYRlN/u1XMkz9+inouNsXWjeT6ttBrp1twz10x/sCbWLpQ==",
"dependencies": {
"@types/node": ">=8.1.0",
"qs": "^6.11.0"
diff --git a/package.json b/package.json
index d982e8c6..1db8d988 100644
--- a/package.json
+++ b/package.json
@@ -89,7 +89,7 @@
"rxjs": "^7.8.0",
"sharp": "^0.33.2",
"simple-statistics": "^7.8.3",
- "stripe": "^14.14.0",
+ "stripe": "^15.5.0",
"sweetalert2": "^11.6.13",
"swr": "^2.2.5",
"tslib": "^2.3.0",