Skip to content

Commit

Permalink
Add mixpanel analytics and events to server
Browse files Browse the repository at this point in the history
  • Loading branch information
NavidK0 committed Feb 17, 2021
1 parent 848f81e commit 437063a
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 48 deletions.
60 changes: 60 additions & 0 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
},
"homepage": "/~https://github.com/Neutron-Creative/Singlelink#readme",
"dependencies": {
"@types/mixpanel": "^2.14.2",
"aws-sdk": "^2.796.0",
"axios": "^0.21.1",
"bcrypt": "^5.0.0",
Expand All @@ -51,6 +52,7 @@
"fastify-rate-limit": "^4.0.3",
"http-status-codes": "^2.1.4",
"jsonwebtoken": "^8.5.1",
"mixpanel": "^0.13.0",
"nc-deeplink": "^1.0.6",
"pg": "^8.5.1"
},
Expand Down
3 changes: 3 additions & 0 deletions server/src/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@
"message": "Hello,\n\nSomebody requested a password request for your account on SingleLink.\n\nIf this was your doing, please visit the link below to reset your password.\n\n${url}\n\nThis link will be valid for 15 minutes.\nIf you cannot click the link above, copy & paste the link into your browser.\n\nIf this was not you, please ignore this email.\n\nThank you,\nSingleLink Team\n\nNote: Do not reply to this email, as there is no inbox for it.",
"messageCharset": "UTF-8"
}
},
"analytics": {
"mixpanelToken": ""
}
}
7 changes: 7 additions & 0 deletions server/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ if (process.env.S3_SECRET_KEY) {
config.s3Bucket.secretKey = process.env.S3_SECRET_KEY;
}

/**
* Mixpanel analytics token
*/
if (process.env.MIXPANEL_TOKEN) {
config.analytics.mixpanelToken = process.env.MIXPANEL_TOKEN;
}


// Extra logic for debug messages

Expand Down
3 changes: 3 additions & 0 deletions server/src/config/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class Constants {
public static readonly ANONYMOUS_USER_ID = 'anonymous';
}
3 changes: 0 additions & 3 deletions server/src/controllers/admin-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import {DatabaseManager} from "../data/database-manager";
import {Controller} from "./controller";
import {Auth, AuthenticatedRequest} from "../utils/auth";
import {AdminService} from "../services/admin-service";
import {StatusCodes} from "http-status-codes";
import {ReplyUtils} from "../utils/reply-utils";
import {HttpError} from "../utils/http-error";

interface GetGroupRequest extends AuthenticatedRequest {
Body: {} & AuthenticatedRequest["Body"]
Expand Down
32 changes: 30 additions & 2 deletions server/src/controllers/analytics-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {Auth, AuthenticatedRequest} from "../utils/auth";
import {ReplyUtils} from "../utils/reply-utils";
import {ProfileService} from "../services/profile-service";
import {LinkService} from "../services/link-service";
import Mixpanel from "mixpanel";
import {config} from "../config/config";
import {Constants} from "../config/constants";

interface LinkAnalyticsRequest extends RequestGenericInterface {
Params: {
Expand Down Expand Up @@ -45,6 +48,7 @@ export class AnalyticsController extends Controller {
private analyticsService: AnalyticsService;
private linkService: LinkService;
private profileService: ProfileService;
private mixpanel = Mixpanel.init(config.analytics.mixpanelToken);

constructor(fastify: FastifyInstance, databaseManager: DatabaseManager) {
super(fastify, databaseManager);
Expand Down Expand Up @@ -107,12 +111,26 @@ export class AnalyticsController extends Controller {

let link = await this.linkService.getLink(id);
const profileId = link.profileId;
const profile = await this.profileService.getMetadata(profileId);
const profile = await this.profileService.getProfile(profileId);

if (!profile.metadata?.privacyMode && profile.visibility !== "unpublished") {
await this.analyticsService.createVisit(id, "link");

this.mixpanel.track('clicked profile link', {
distinct_id: profile.userId,
profile: profileId,
link: link.id,
url: link.url
});
} else {
await this.analyticsService.createAnonymousVisit("link");

this.mixpanel.track('clicked profile link', {
distinct_id: Constants.ANONYMOUS_USER_ID,
profile: profileId,
link: link.id,
url: link.url
});
}

if (link.useDeepLink) {
Expand Down Expand Up @@ -156,12 +174,22 @@ export class AnalyticsController extends Controller {
return ReplyUtils.error("The profile was not found.");
}

const profile: { visibility: DbProfile["visibility"]; metadata: DbProfile["metadata"] } = await this.profileService.getMetadata(id);
const profile = await this.profileService.getProfile(id);

if (!profile.metadata?.privacyMode && profile.visibility !== "unpublished") {
await this.analyticsService.createVisit(id, "page");

this.mixpanel.track('viewed profile', {
distinct_id: profile.userId,
profile: profile.id,
});
} else {
await this.analyticsService.createAnonymousVisit("page");

this.mixpanel.track('viewed profile', {
distinct_id: Constants.ANONYMOUS_USER_ID,
profile: profile.id,
});
}

reply.code(StatusCodes.OK).send();
Expand Down
3 changes: 3 additions & 0 deletions server/src/controllers/info-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import {Controller} from "./controller";
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
import {DatabaseManager} from "../data/database-manager";
import {InfoService} from "../services/info-service";
import Mixpanel from "mixpanel";
import {config} from "../config/config";

/**
* This controller maps and provides for all the controllers under /info.
*/
export class InfoController extends Controller {
private infoService: InfoService;
private mixpanel = Mixpanel.init(config.analytics.mixpanelToken);

constructor(fastify: FastifyInstance, databaseManager: DatabaseManager) {
super(fastify, databaseManager);
Expand Down
46 changes: 41 additions & 5 deletions server/src/controllers/link-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {ReplyUtils} from "../utils/reply-utils";
import {StatusCodes} from "http-status-codes";
import {Controller} from "./controller";
import {HttpError} from "../utils/http-error";
import Mixpanel from "mixpanel";
import {config} from "../config/config";

interface CreateLinkRequest extends AuthenticatedRequest {
Body: {
Expand Down Expand Up @@ -48,7 +50,8 @@ interface ReorderLinkRequest extends AuthenticatedRequest {
* This controller maps and provides for all the controllers under /link.
*/
export class LinkController extends Controller {
private linkService: LinkService;
private readonly linkService: LinkService;
private mixpanel = Mixpanel.init(config.analytics.mixpanelToken);

constructor(fastify: FastifyInstance, databaseManager: DatabaseManager) {
super(fastify, databaseManager);
Expand Down Expand Up @@ -94,7 +97,7 @@ export class LinkController extends Controller {

let count = await this.linkService.getProfileLinkCount(profile.id);

return await this.linkService.createLink(
let link = await this.linkService.createLink(
profile.id,
body.url,
count,
Expand All @@ -104,6 +107,15 @@ export class LinkController extends Controller {
body.customCss,
body.useDeepLink
);

this.mixpanel.track('profile link created', {
distinct_id: profile.userId,
profile: profile.id,
link: link.id,
url: link.url
});

return link;
} catch (e) {
if (e instanceof HttpError) {
reply.code(e.statusCode);
Expand Down Expand Up @@ -133,7 +145,7 @@ export class LinkController extends Controller {

await Auth.checkLinkOwnership(this.linkService, body.id, body.authProfile);

return await this.linkService.updateLink(
let link = await this.linkService.updateLink(
body.id,
body.url,
body.sortOrder,
Expand All @@ -143,6 +155,15 @@ export class LinkController extends Controller {
body.customCss,
body.useDeepLink
);

this.mixpanel.track('profile link updated', {
distinct_id: body.authUser.id,
profile: body.authProfile.id,
link: link.id,
url: link.url
});

return link;
} catch (e) {
if (e instanceof HttpError) {
reply.code(e.statusCode);
Expand Down Expand Up @@ -172,7 +193,15 @@ export class LinkController extends Controller {

await Auth.checkLinkOwnership(this.linkService, body.id, body.authProfile);

return await this.linkService.deleteLink(body.id);
let link = await this.linkService.deleteLink(body.id);

this.mixpanel.track('profile link deleted', {
distinct_id: body.authUser.id,
profile: body.authProfile.id,
link: body.id
});

return link;
} catch (e) {
if (e instanceof HttpError) {
reply.code(e.statusCode);
Expand Down Expand Up @@ -210,7 +239,14 @@ export class LinkController extends Controller {
return;
}

return await this.linkService.reorderLinks(body.authProfile.id, body.oldIndex, body.newIndex);
let links = await this.linkService.reorderLinks(body.authProfile.id, body.oldIndex, body.newIndex);

this.mixpanel.track('profile links reordered', {
distinct_id: body.authUser.id,
profile: body.authProfile.id,
});

return links;
} catch (e) {
if (e instanceof HttpError) {
reply.code(e.statusCode);
Expand Down
Loading

0 comments on commit 437063a

Please sign in to comment.