Skip to content

Commit 396a55f

Browse files
committed
feat: preview
2 parents 287f404 + 9c6474a commit 396a55f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1543
-523
lines changed

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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 24 additions & 4 deletions
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

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
1+
import { Body, Controller, Get, Param, Post, Req } from '@nestjs/common';
22
import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service';
33
import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service';
44
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
@@ -7,6 +7,7 @@ import { BillingSubscribeDto } from '@gitroom/nestjs-libraries/dtos/billing/bill
77
import { ApiTags } from '@nestjs/swagger';
88
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
99
import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
10+
import { Request } from 'express';
1011

1112
@ApiTags('Billing')
1213
@Controller('/billing')
@@ -23,19 +24,22 @@ export class BillingController {
2324
@Param('id') body: string
2425
) {
2526
return {
26-
exists: !!(await this._subscriptionService.checkSubscription(
27+
status: await this._stripeService.checkSubscription(
2728
org.id,
2829
body
29-
)),
30+
),
3031
};
3132
}
3233

3334
@Post('/subscribe')
3435
subscribe(
3536
@GetOrgFromRequest() org: Organization,
36-
@Body() body: BillingSubscribeDto
37+
@GetUserFromRequest() user: User,
38+
@Body() body: BillingSubscribeDto,
39+
@Req() req: Request
3740
) {
38-
return this._stripeService.subscribe(org.id, body);
41+
const uniqueId = req?.cookies?.track;
42+
return this._stripeService.subscribe(uniqueId, org.id, user.id, body, org.allowTrial);
3943
}
4044

4145
@Get('/portal')

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

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
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';
44
import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service';
5+
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
6+
import { RealIP } from 'nestjs-real-ip';
7+
import { UserAgent } from '@gitroom/nestjs-libraries/user/user.agent';
8+
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
9+
import { Request, Response } from 'express';
10+
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
11+
import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management';
512

613
@ApiTags('Public')
714
@Controller('/public')
815
export class PublicController {
916
constructor(
1017
private _agenciesService: AgenciesService,
11-
private _postsService: PostsService
18+
private _postsService: PostsService,
19+
private _trackService: TrackService
1220
) {}
21+
1322
@Get('/agencies-list')
1423
async getAgencyByUser() {
1524
return this._agenciesService.getAllAgencies();
@@ -49,4 +58,48 @@ export class PublicController {
4958
})
5059
);
5160
}
61+
62+
@Post('/t')
63+
async trackEvent(
64+
@Res() res: Response,
65+
@Req() req: Request,
66+
@RealIP() ip: string,
67+
@UserAgent() userAgent: string,
68+
@Body()
69+
body: { fbclid?: string; tt: TrackEnum; additional: Record<string, any> }
70+
) {
71+
const uniqueId = req?.cookies?.track || makeId(10);
72+
const fbclid = req?.cookies?.fbclid || body.fbclid;
73+
await this._trackService.track(
74+
uniqueId,
75+
ip,
76+
userAgent,
77+
body.tt,
78+
body.additional,
79+
fbclid
80+
);
81+
if (!req.cookies.track) {
82+
res.cookie('track', uniqueId, {
83+
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
84+
secure: true,
85+
httpOnly: true,
86+
sameSite: 'none',
87+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
88+
});
89+
}
90+
91+
if (body.fbclid && !req.cookies.fbclid) {
92+
res.cookie('fbclid', body.fbclid, {
93+
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
94+
secure: true,
95+
httpOnly: true,
96+
sameSite: 'none',
97+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
98+
});
99+
}
100+
101+
res.status(200).json({
102+
track: uniqueId,
103+
});
104+
}
52105
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ export class StripeController {
5454
// Maybe it comes from another stripe webhook
5555
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
5656
// @ts-ignore
57-
if (event?.data?.object?.metadata?.service !== 'gitroom') {
57+
if (event?.data?.object?.metadata?.service !== 'gitroom' && event.type !== 'invoice.payment_succeeded') {
5858
return { ok: true };
5959
}
6060

6161
switch (event.type) {
62+
case 'invoice.payment_succeeded':
63+
return this._stripeService.paymentSucceeded(event);
6264
case 'checkout.session.completed':
6365
return this._stripeService.updateOrder(event);
6466
case 'account.updated':

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

Lines changed: 41 additions & 3 deletions
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,19 @@ 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,
71+
allowTrial: organization?.allowTrial,
6472
// @ts-ignore
65-
publicApi: (organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN') ? organization?.apiKey : '',
73+
publicApi: organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN'
74+
? organization?.apiKey
75+
: '',
6676
};
6777
}
6878

@@ -205,4 +215,32 @@ export class UsersController {
205215

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

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

Lines changed: 36 additions & 14 deletions
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) {

0 commit comments

Comments
 (0)