Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: feat: support mongodb connection #2040

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions db/mongodb/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
relationMode = "prisma"
}

model User {
id String @id @map("_id") @db.String
username String @unique @db.String
password String @db.String
role String @map("role") @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date
updatedAt DateTime? @updatedAt @map("updated_at") @db.Date
deletedAt DateTime? @map("deleted_at") @db.Date

website Website[]
teamUser TeamUser[]

@@map("user")
}

model Session {
id String @id @map("_id") @db.String
websiteId String @map("website_id") @db.String
hostname String? @db.String
browser String? @db.String
os String? @db.String
device String? @db.String
screen String? @db.String
language String? @db.String
country String? @db.String
subdivision1 String? @db.String
subdivision2 String? @db.String
city String? @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date

websiteEvent WebsiteEvent[]

@@index([createdAt])
@@index([websiteId])
@@map("session")
}

model Website {
id String @id @map("_id") @db.String
name String @db.String
domain String? @db.String
shareId String? @unique @map("share_id") @db.String
resetAt DateTime? @map("reset_at") @db.Date
userId String? @map("user_id") @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date
updatedAt DateTime? @updatedAt @map("updated_at") @db.Date
deletedAt DateTime? @map("deleted_at") @db.Date

user User? @relation(fields: [userId], references: [id])
teamWebsite TeamWebsite[]
eventData EventData[]

@@index([userId])
@@index([createdAt])
@@map("website")
}

model WebsiteEvent {
id String @id() @map("_id") @db.String
websiteId String @map("website_id") @db.String
sessionId String @map("session_id") @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date
urlPath String @map("url_path") @db.String
urlQuery String? @map("url_query") @db.String
referrerPath String? @map("referrer_path") @db.String
referrerQuery String? @map("referrer_query") @db.String
referrerDomain String? @map("referrer_domain") @db.String
pageTitle String? @map("page_title") @db.String
eventType Int @default(1) @map("event_type") @db.Int
eventName String? @map("event_name") @db.String

eventData EventData[]
session Session @relation(fields: [sessionId], references: [id])

@@index([createdAt])
@@index([sessionId])
@@index([websiteId])
@@index([websiteId, createdAt])
@@index([websiteId, sessionId, createdAt])
@@map("website_event")
}

model EventData {
id String @id() @map("_id") @db.String
websiteEventId String @map("website_event_id") @db.String
websiteId String @map("website_id") @db.String
eventKey String @map("event_key") @db.String
eventStringValue String? @map("event_string_value") @db.String
eventNumericValue Float? @map("event_numeric_value") @db.Double // (19, 4)
eventDateValue DateTime? @map("event_date_value") @db.Date
eventDataType Int @map("event_data_type") @db.Int
createdAt DateTime? @default(now()) @map("created_at") @db.Date

website Website @relation(fields: [websiteId], references: [id])
websiteEvent WebsiteEvent @relation(fields: [websiteEventId], references: [id])

@@index([createdAt])
@@index([websiteId])
@@index([websiteEventId])
@@index([websiteId, websiteEventId, createdAt])
@@map("event_data")
}

model Team {
id String @id() @map("_id") @db.String
name String @db.String
accessCode String? @unique @map("access_code") @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date
updatedAt DateTime? @updatedAt @map("updated_at") @db.Date

teamUser TeamUser[]
teamWebsite TeamWebsite[]

@@map("team")
}

model TeamUser {
id String @id() @map("_id") @db.String
teamId String @map("team_id") @db.String
userId String @map("user_id") @db.String
role String @map("role") @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date
updatedAt DateTime? @updatedAt @map("updated_at") @db.Date

team Team @relation(fields: [teamId], references: [id])
user User @relation(fields: [userId], references: [id])

@@index([teamId])
@@index([userId])
@@map("team_user")
}

model TeamWebsite {
id String @id() @map("_id") @db.String
teamId String @map("team_id") @db.String
websiteId String @map("website_id") @db.String
createdAt DateTime? @default(now()) @map("created_at") @db.Date

team Team @relation(fields: [teamId], references: [id])
website Website @relation(fields: [websiteId], references: [id])

@@index([teamId])
@@index([websiteId])
@@map("team_website")
}
6 changes: 6 additions & 0 deletions lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const MYSQL = 'mysql';
export const CLICKHOUSE = 'clickhouse';
export const KAFKA = 'kafka';
export const KAFKA_PRODUCER = 'kafka-producer';
export const MONGODB = 'mongodb';

// Fixes issue with converting bigint values
BigInt.prototype.toJSON = function () {
Expand All @@ -15,6 +16,8 @@ export function getDatabaseType(url = process.env.DATABASE_URL) {

if (type === 'postgres') {
return POSTGRESQL;
} else if (type === 'mongodb+srv') {
return MONGODB;
}

return type;
Expand All @@ -26,6 +29,9 @@ export async function runQuery(queries) {
if (db === POSTGRESQL || db === MYSQL) {
return queries[PRISMA]();
}
if (db === MONGODB) {
return queries[MONGODB]();
}

if (db === CLICKHOUSE) {
if (queries[KAFKA]) {
Expand Down
27 changes: 26 additions & 1 deletion lib/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import prisma from '@umami/prisma-client';
import moment from 'moment-timezone';
import { MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db';
import { MYSQL, POSTGRESQL, getDatabaseType, MONGODB } from 'lib/db';
import { getEventDataType } from './eventData';
import { FILTER_COLUMNS } from './constants';

Expand Down Expand Up @@ -51,6 +51,10 @@ function getDateQuery(field: string, unit: string, timezone?: string): string {

return `date_format(${field}, '${MYSQL_DATE_FORMATS[unit]}')`;
}

if (db === MONGODB) {
return MYSQL_DATE_FORMATS[unit];
}
}

function getTimestampInterval(field: string): string {
Expand Down Expand Up @@ -138,6 +142,25 @@ function parseFilters(
};
}

function parseMongoFilter(filters: { [key: string]: any } = {}) {
const query = {};

for (let k in filters) {
const v = filters[k];
if (v !== undefined) {
const tempK = FILTER_COLUMNS[k];
if (tempK !== undefined) {
k = tempK;
}
if (k === 'browser' || k === 'os' || k === 'device' || k === 'language') {
k = 'session.' + k;
}
query[k] = v;
}
}
return { $match: query };
}

async function rawQuery(query: string, params: never[] = []): Promise<any> {
const db = getDatabaseType(process.env.DATABASE_URL);

Expand All @@ -152,11 +175,13 @@ async function rawQuery(query: string, params: never[] = []): Promise<any> {

export default {
...prisma,
getDatabaseType: () => getDatabaseType(process.env.DATABASE_URL),
getDateQuery,
getTimestampInterval,
getFilterQuery,
getEventDataFilterQuery,
toUuid,
parseFilters,
parseMongoFilter,
rawQuery,
};
35 changes: 26 additions & 9 deletions pages/api/websites/[id]/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default async (
city,
},
});
const prevPeriod = await getWebsiteStats(websiteId, {
let prevPeriod = await getWebsiteStats(websiteId, {
startDate: prevStartDate,
endDate: prevEndDate,
filters: {
Expand All @@ -92,14 +92,31 @@ export default async (
city,
},
});

const stats = Object.keys(metrics[0]).reduce((obj, key) => {
obj[key] = {
value: Number(metrics[0][key]) || 0,
change: Number(metrics[0][key]) - Number(prevPeriod[0][key]) || 0,
};
return obj;
}, {});
if (prevPeriod.length === 0) {
prevPeriod = [
{
pageviews: 0,
uniques: 0,
bounces: 0,
totaltime: 0,
},
];
}
let stats: object = {
pageviews: 0,
uniques: 0,
bounces: 0,
totaltime: 0,
};
if (metrics.length != 0) {
stats = Object.keys(metrics[0]).reduce((obj, key) => {
obj[key] = {
value: Number(metrics[0][key]) || 0,
change: Number(metrics[0][key]) - Number(prevPeriod[0][key]) || 0,
};
return obj;
}, {});
}

return ok(res, stats);
}
Expand Down
23 changes: 20 additions & 3 deletions queries/admin/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import cache from 'lib/cache';
import { ROLES } from 'lib/constants';
import prisma from 'lib/prisma';
import { Website, User, Roles } from 'lib/types';
import { getDatabaseType } from '../../lib/db';

function whereDeletedAtNotNull() {
const db = getDatabaseType(process.env.DATABASE_URL);
if (db === 'mongodb') {
return { isSet: false };
} else {
return null;
}
}

export async function getUser(
where: Prisma.UserWhereInput | Prisma.UserWhereUniqueInput,
Expand All @@ -11,7 +21,14 @@ export async function getUser(
const { includePassword = false, showDeleted = false } = options;

return prisma.client.user.findFirst({
where: { ...where, ...(showDeleted ? {} : { deletedAt: null }) },
where: {
...where,
...(showDeleted
? {}
: {
deletedAt: whereDeletedAtNotNull(),
}),
},
select: {
id: true,
username: true,
Expand All @@ -26,7 +43,7 @@ export async function getUsers(): Promise<User[]> {
return prisma.client.user.findMany({
take: 100,
where: {
deletedAt: null,
deletedAt: whereDeletedAtNotNull(),
},
orderBy: [
{
Expand Down Expand Up @@ -76,7 +93,7 @@ export async function getUserWebsites(userId: string): Promise<Website[]> {
return prisma.client.website.findMany({
where: {
userId,
deletedAt: null,
deletedAt: whereDeletedAtNotNull(),
},
orderBy: [
{
Expand Down
Loading