Skip to content

Commit 2d0c47d

Browse files
committed
feat: fbp + fclib
1 parent 3d185ad commit 2d0c47d

File tree

14 files changed

+571
-514
lines changed

14 files changed

+571
-514
lines changed

apps/backend/src/api/api.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { CopilotController } from '@gitroom/backend/api/routes/copilot.controlle
2626
import { AgenciesController } from '@gitroom/backend/api/routes/agencies.controller';
2727
import { PublicController } from '@gitroom/backend/api/routes/public.controller';
2828
import { RootController } from '@gitroom/backend/api/routes/root.controller';
29+
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
2930

3031
const authenticatedController = [
3132
UsersController,
@@ -63,6 +64,7 @@ const authenticatedController = [
6364
PermissionsService,
6465
CodesService,
6566
IntegrationManager,
67+
TrackService,
6668
],
6769
get exports() {
6870
return [...this.imports, ...this.providers];

apps/backend/src/api/routes/auth.controller.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { Body, Controller, Get, Param, Post, Req, Res } from '@nestjs/common';
1+
import {
2+
Body,
3+
Controller,
4+
Get,
5+
Ip,
6+
Param,
7+
Post,
8+
Req,
9+
Res,
10+
} from '@nestjs/common';
211
import { Response, Request } from 'express';
312

413
import { CreateOrgUserDto } from '@gitroom/nestjs-libraries/dtos/auth/create.org.user.dto';
@@ -9,6 +18,8 @@ import { ForgotPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot.pa
918
import { ApiTags } from '@nestjs/swagger';
1019
import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management';
1120
import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
21+
import { RealIP } from 'nestjs-real-ip';
22+
import { UserAgent } from '@gitroom/nestjs-libraries/user/user.agent';
1223

1324
@ApiTags('Auth')
1425
@Controller('/auth')
@@ -21,7 +32,9 @@ export class AuthController {
2132
async register(
2233
@Req() req: Request,
2334
@Body() body: CreateOrgUserDto,
24-
@Res({ passthrough: true }) response: Response
35+
@Res({ passthrough: true }) response: Response,
36+
@RealIP() ip: string,
37+
@UserAgent() userAgent: string
2538
) {
2639
try {
2740
const getOrgFromCookie = this._authService.getOrgFromCookie(
@@ -31,10 +44,13 @@ export class AuthController {
3144
const { jwt, addedOrg } = await this._authService.routeAuth(
3245
body.provider,
3346
body,
47+
ip,
48+
userAgent,
3449
getOrgFromCookie
3550
);
3651

37-
const activationRequired = body.provider === 'LOCAL' && this._emailService.hasProvider();
52+
const activationRequired =
53+
body.provider === 'LOCAL' && this._emailService.hasProvider();
3854

3955
if (activationRequired) {
4056
response.header('activate', 'true');
@@ -73,7 +89,9 @@ export class AuthController {
7389
async login(
7490
@Req() req: Request,
7591
@Body() body: LoginUserDto,
76-
@Res({ passthrough: true }) response: Response
92+
@Res({ passthrough: true }) response: Response,
93+
@RealIP() ip: string,
94+
@UserAgent() userAgent: string
7795
) {
7896
try {
7997
const getOrgFromCookie = this._authService.getOrgFromCookie(
@@ -83,6 +101,8 @@ export class AuthController {
83101
const { jwt, addedOrg } = await this._authService.routeAuth(
84102
body.provider,
85103
body,
104+
ip,
105+
userAgent,
86106
getOrgFromCookie
87107
);
88108

Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
import { Controller, Get, Param } from '@nestjs/common';
1+
import { Body, Controller, Get, Param, Post, Req, Res } from '@nestjs/common';
22
import { ApiTags } from '@nestjs/swagger';
33
import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service';
4+
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
5+
import { RealIP } from 'nestjs-real-ip';
6+
import { UserAgent } from '@gitroom/nestjs-libraries/user/user.agent';
7+
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
8+
import { Request, Response } from 'express';
9+
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
10+
import { User } from '@prisma/client';
11+
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
12+
import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management';
413

514
@ApiTags('Public')
615
@Controller('/public')
716
export class PublicController {
8-
constructor(private _agenciesService: AgenciesService) {}
17+
constructor(
18+
private _agenciesService: AgenciesService,
19+
private _trackService: TrackService
20+
) {}
921
@Get('/agencies-list')
1022
async getAgencyByUser() {
1123
return this._agenciesService.getAllAgencies();
@@ -17,14 +29,61 @@ export class PublicController {
1729
}
1830

1931
@Get('/agencies-information/:agency')
20-
async getAgencyInformation(
21-
@Param('agency') agency: string,
22-
) {
32+
async getAgencyInformation(@Param('agency') agency: string) {
2333
return this._agenciesService.getAgencyInformation(agency);
2434
}
2535

2636
@Get('/agencies-list-count')
2737
async getAgenciesCount() {
2838
return this._agenciesService.getCount();
2939
}
40+
41+
@Post('/t')
42+
async trackEvent(
43+
@Res() res: Response,
44+
@Req() req: Request,
45+
@RealIP() ip: string,
46+
@UserAgent() userAgent: string,
47+
@Body()
48+
body: { fbclid?: string; tt: TrackEnum; additional: Record<string, any> }
49+
) {
50+
const uniqueId = req?.cookies?.track || makeId(10);
51+
console.log(
52+
req?.cookies?.track,
53+
ip,
54+
userAgent,
55+
body.tt,
56+
body.additional,
57+
body.fbclid
58+
);
59+
await this._trackService.track(
60+
req?.cookies?.track,
61+
ip,
62+
userAgent,
63+
body.tt,
64+
body.additional,
65+
body.fbclid
66+
);
67+
if (!req.cookies.track) {
68+
res.cookie('track', uniqueId, {
69+
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
70+
secure: true,
71+
httpOnly: true,
72+
sameSite: 'none',
73+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
74+
});
75+
}
76+
77+
if (body.fbclid && !req.cookies.fbclid) {
78+
res.cookie('fbclid', body.fbclid, {
79+
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
80+
secure: true,
81+
httpOnly: true,
82+
sameSite: 'none',
83+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
84+
});
85+
}
86+
87+
res.status(200).send();
88+
}
3089
}

apps/backend/src/api/routes/users.controller.ts

+36-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ import { ApiTags } from '@nestjs/swagger';
2727
import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service';
2828
import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto';
2929
import { HttpForbiddenException } from '@gitroom/nestjs-libraries/services/exception.filter';
30+
import { RealIP } from 'nestjs-real-ip';
31+
import { UserAgent } from '@gitroom/nestjs-libraries/user/user.agent';
32+
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
33+
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
34+
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
3035

3136
@ApiTags('User')
3237
@Controller('/user')
@@ -36,7 +41,8 @@ export class UsersController {
3641
private _stripeService: StripeService,
3742
private _authService: AuthService,
3843
private _orgService: OrganizationService,
39-
private _userService: UsersService
44+
private _userService: UsersService,
45+
private _trackService: TrackService
4046
) {}
4147
@Get('/self')
4248
async getSelf(
@@ -54,15 +60,18 @@ export class UsersController {
5460
// @ts-ignore
5561
totalChannels: organization?.subscription?.totalChannels || pricing.FREE.channel,
5662
// @ts-ignore
57-
tier: organization?.subscription?.subscriptionTier || (!process.env.STRIPE_PUBLISHABLE_KEY ? 'ULTIMATE' : 'FREE'),
63+
tier: organization?.subscription?.subscriptionTier ||
64+
(!process.env.STRIPE_PUBLISHABLE_KEY ? 'ULTIMATE' : 'FREE'),
5865
// @ts-ignore
5966
role: organization?.users[0]?.role,
6067
// @ts-ignore
6168
isLifetime: !!organization?.subscription?.isLifetime,
6269
admin: !!user.isSuperAdmin,
6370
impersonate: !!req.cookies.impersonate,
6471
// @ts-ignore
65-
publicApi: (organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN') ? organization?.apiKey : '',
72+
publicApi: organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN'
73+
? organization?.apiKey
74+
: '',
6675
};
6776
}
6877

@@ -205,4 +214,28 @@ export class UsersController {
205214

206215
response.status(200).send();
207216
}
217+
218+
@Post('/t')
219+
async trackEvent(
220+
@Res({ passthrough: true }) res: Response,
221+
@Req() req: Request,
222+
@GetUserFromRequest() user: User,
223+
@RealIP() ip: string,
224+
@UserAgent() userAgent: string,
225+
@Body() body: { tt: TrackEnum; additional: Record<string, any> }
226+
) {
227+
const uniqueId = req?.cookies?.track || makeId(10);
228+
await this._trackService.track(req?.cookies?.track, ip, userAgent, body.tt, body.additional, null, user);
229+
if (!req.cookies.track) {
230+
res.cookie('track', uniqueId, {
231+
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
232+
secure: true,
233+
httpOnly: true,
234+
sameSite: 'none',
235+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
236+
});
237+
}
238+
239+
res.status(200).send();
240+
}
208241
}

apps/backend/src/main.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ async function bootstrap() {
1717
credentials: true,
1818
exposedHeaders: ['reload', 'onboarding', 'activate'],
1919
origin: [
20+
'http://localhost:3001',
2021
process.env.FRONTEND_URL,
2122
...(process.env.MAIN_URL ? [process.env.MAIN_URL] : []),
2223
],

apps/backend/src/services/auth/auth.service.ts

+36-14
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ export class AuthService {
1818
private _userService: UsersService,
1919
private _organizationService: OrganizationService,
2020
private _notificationService: NotificationService,
21-
private _emailService: EmailService,
21+
private _emailService: EmailService
2222
) {}
2323
async routeAuth(
2424
provider: Provider,
2525
body: CreateOrgUserDto | LoginUserDto,
26+
ip: string,
27+
userAgent: string,
2628
addToOrg?: boolean | { orgId: string; role: 'USER' | 'ADMIN'; id: string }
2729
) {
2830
if (provider === Provider.LOCAL) {
@@ -32,7 +34,11 @@ export class AuthService {
3234
throw new Error('User already exists');
3335
}
3436

35-
const create = await this._organizationService.createOrgAndUser(body);
37+
const create = await this._organizationService.createOrgAndUser(
38+
body,
39+
ip,
40+
userAgent
41+
);
3642

3743
const addedOrg =
3844
addToOrg && typeof addToOrg !== 'boolean'
@@ -45,7 +51,11 @@ export class AuthService {
4551
: false;
4652

4753
const obj = { addedOrg, jwt: await this.jwt(create.users[0].user) };
48-
await this._emailService.sendEmail(body.email, 'Activate your account', `Click <a href="${process.env.FRONTEND_URL}/auth/activate/${obj.jwt}">here</a> to activate your account`);
54+
await this._emailService.sendEmail(
55+
body.email,
56+
'Activate your account',
57+
`Click <a href="${process.env.FRONTEND_URL}/auth/activate/${obj.jwt}">here</a> to activate your account`
58+
);
4959
return obj;
5060
}
5161

@@ -62,7 +72,9 @@ export class AuthService {
6272

6373
const user = await this.loginOrRegisterProvider(
6474
provider,
65-
body as CreateOrgUserDto
75+
body as CreateOrgUserDto,
76+
ip,
77+
userAgent
6678
);
6779

6880
const addedOrg =
@@ -101,7 +113,9 @@ export class AuthService {
101113

102114
private async loginOrRegisterProvider(
103115
provider: Provider,
104-
body: CreateOrgUserDto
116+
body: CreateOrgUserDto,
117+
ip: string,
118+
userAgent: string
105119
) {
106120
const providerInstance = ProvidersFactory.loadProvider(provider);
107121
const providerUser = await providerInstance.getUser(body.providerToken);
@@ -118,15 +132,19 @@ export class AuthService {
118132
return user;
119133
}
120134

121-
const create = await this._organizationService.createOrgAndUser({
122-
company: body.company,
123-
email: providerUser.email,
124-
password: '',
125-
provider,
126-
providerId: providerUser.id,
127-
});
135+
const create = await this._organizationService.createOrgAndUser(
136+
{
137+
company: body.company,
138+
email: providerUser.email,
139+
password: '',
140+
provider,
141+
providerId: providerUser.id,
142+
},
143+
ip,
144+
userAgent
145+
);
128146

129-
NewsletterService.register(providerUser.email);
147+
await NewsletterService.register(providerUser.email);
130148

131149
return create.users[0].user;
132150
}
@@ -162,7 +180,11 @@ export class AuthService {
162180
}
163181

164182
async activate(code: string) {
165-
const user = AuthChecker.verifyJWT(code) as { id: string, activated: boolean, email: string };
183+
const user = AuthChecker.verifyJWT(code) as {
184+
id: string;
185+
activated: boolean;
186+
email: string;
187+
};
166188
if (user.id && !user.activated) {
167189
const getUserAgain = await this._userService.getUserByEmail(user.email);
168190
if (getUserAgain.activated) {

libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,9 @@ export class OrganizationRepository {
207207

208208
async createOrgAndUser(
209209
body: Omit<CreateOrgUserDto, 'providerToken'> & { providerId?: string },
210-
hasEmail: boolean
210+
hasEmail: boolean,
211+
ip: string,
212+
userAgent: string
211213
) {
212214
return this._organization.model.organization.create({
213215
data: {
@@ -226,6 +228,8 @@ export class OrganizationRepository {
226228
providerName: body.provider,
227229
providerId: body.providerId || '',
228230
timezone: 0,
231+
ip,
232+
agent: userAgent,
229233
},
230234
},
231235
},

libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ export class OrganizationService {
1515
private _notificationsService: NotificationService
1616
) {}
1717
async createOrgAndUser(
18-
body: Omit<CreateOrgUserDto, 'providerToken'> & { providerId?: string }
18+
body: Omit<CreateOrgUserDto, 'providerToken'> & { providerId?: string },
19+
ip: string,
20+
userAgent: string
1921
) {
2022
return this._organizationRepository.createOrgAndUser(
2123
body,
22-
this._notificationsService.hasEmailProvider()
24+
this._notificationsService.hasEmailProvider(),
25+
ip,
26+
userAgent
2327
);
2428
}
2529

0 commit comments

Comments
 (0)