From c3610e21e82921f8c2607914a78e8d73052a0d1c Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Sun, 5 Feb 2023 17:20:58 +0100 Subject: [PATCH] Extend analytics by country --- apps/api/src/app/auth/auth.service.ts | 12 +- apps/api/src/app/user/create-user.dto.ts | 7 + apps/api/src/app/user/user.controller.ts | 6 +- apps/api/src/app/user/user.service.ts | 17 +- .../pages/register/register-page.component.ts | 6 +- apps/client/src/app/services/data.service.ts | 4 +- .../src/app/services/user/user.service.ts | 15 + .../src/lib/timezone-cities-to-countries.ts | 426 ++++++++++++++++++ .../src/lib/types/analyticsEventType.type.ts | 1 + libs/common/src/lib/types/index.ts | 2 + prisma/schema.prisma | 7 + 11 files changed, 492 insertions(+), 11 deletions(-) create mode 100644 apps/api/src/app/user/create-user.dto.ts create mode 100644 libs/common/src/lib/timezone-cities-to-countries.ts create mode 100644 libs/common/src/lib/types/analyticsEventType.type.ts diff --git a/apps/api/src/app/auth/auth.service.ts b/apps/api/src/app/auth/auth.service.ts index 0be9f2877..2c2dc2fc7 100644 --- a/apps/api/src/app/auth/auth.service.ts +++ b/apps/api/src/app/auth/auth.service.ts @@ -61,8 +61,10 @@ export class AuthService { // Create new user if not found user = await this.userService.createUser({ - provider, - thirdPartyId: principalId + data: { + provider, + thirdPartyId: principalId + } }); } @@ -96,8 +98,10 @@ export class AuthService { // Create new user if not found user = await this.userService.createUser({ - provider, - thirdPartyId + data: { + provider, + thirdPartyId + } }); } diff --git a/apps/api/src/app/user/create-user.dto.ts b/apps/api/src/app/user/create-user.dto.ts new file mode 100644 index 000000000..7751f75fa --- /dev/null +++ b/apps/api/src/app/user/create-user.dto.ts @@ -0,0 +1,7 @@ +import { IsOptional, IsString } from 'class-validator'; + +export class CreateUserDto { + @IsString() + @IsOptional() + country?: string; +} diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index 6a1729b73..0eb37fb60 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -22,6 +22,7 @@ import { User as UserModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { size } from 'lodash'; +import { CreateUserDto } from './create-user.dto'; import { UserItem } from './interfaces/user-item.interface'; import { UpdateUserSettingDto } from './update-user-setting.dto'; import { UserService } from './user.service'; @@ -65,7 +66,7 @@ export class UserController { } @Post() - public async signupUser(): Promise { + public async signupUser(@Body() data: CreateUserDto): Promise { const isUserSignupEnabled = await this.propertyService.isUserSignupEnabled(); @@ -79,7 +80,8 @@ export class UserController { const hasAdmin = await this.userService.hasAdmin(); const { accessToken, id, role } = await this.userService.createUser({ - role: hasAdmin ? 'USER' : 'ADMIN' + country: data.country, + data: { role: hasAdmin ? 'USER' : 'ADMIN' } }); return { diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 176de7004..55752c78a 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -14,6 +14,7 @@ import { hasRole, permissions } from '@ghostfolio/common/permissions'; +import { AnalyticsEventType } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { Prisma, Role, User } from '@prisma/client'; import { sortBy } from 'lodash'; @@ -231,7 +232,10 @@ export class UserService { return hash.digest('hex'); } - public async createUser(data: Prisma.UserCreateInput): Promise { + public async createUser({ + country, + data + }: { country?: string } & { data: Prisma.UserCreateInput }): Promise { if (!data?.provider) { data.provider = 'ANONYMOUS'; } @@ -256,6 +260,17 @@ export class UserService { } }); + if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { + await this.prismaService.analyticsEvent.create({ + data: { + data: { + country + }, + type: 'createUser' + } + }); + } + if (data.provider === 'ANONYMOUS') { const accessToken = this.createAccessToken( user.id, diff --git a/apps/client/src/app/pages/register/register-page.component.ts b/apps/client/src/app/pages/register/register-page.component.ts index b9cdfcc71..9752c6012 100644 --- a/apps/client/src/app/pages/register/register-page.component.ts +++ b/apps/client/src/app/pages/register/register-page.component.ts @@ -4,6 +4,7 @@ import { Router } from '@angular/router'; import { DataService } from '@ghostfolio/client/services/data.service'; import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; +import { UserService } from '@ghostfolio/client/services/user/user.service'; import { InfoItem, LineChartItem } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { Role } from '@prisma/client'; @@ -37,7 +38,8 @@ export class RegisterPageComponent implements OnDestroy, OnInit { private dialog: MatDialog, private internetIdentityService: InternetIdentityService, private router: Router, - private tokenStorageService: TokenStorageService + private tokenStorageService: TokenStorageService, + private userService: UserService ) { this.info = this.dataService.fetchInfo(); @@ -61,7 +63,7 @@ export class RegisterPageComponent implements OnDestroy, OnInit { public async createAccount() { this.dataService - .postUser() + .postUser({ country: this.userService.getCountry() }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ accessToken, authToken, role }) => { this.openShowAccessTokenDialog(accessToken, authToken, role); diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index d4071caeb..d34d54ded 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -405,8 +405,8 @@ export class DataService { return this.http.post(`/api/v1/order`, aOrder); } - public postUser() { - return this.http.post(`/api/v1/user`, {}); + public postUser({ country }: { country: string }) { + return this.http.post(`/api/v1/user`, { country }); } public putAccount(aAccount: UpdateAccountDto) { diff --git a/apps/client/src/app/services/user/user.service.ts b/apps/client/src/app/services/user/user.service.ts index 7f903df3a..fbbab5a1a 100644 --- a/apps/client/src/app/services/user/user.service.ts +++ b/apps/client/src/app/services/user/user.service.ts @@ -6,6 +6,7 @@ import { SubscriptionInterstitialDialogParams } from '@ghostfolio/client/compone import { SubscriptionInterstitialDialog } from '@ghostfolio/client/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component'; import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { timezoneCitiesToCountries } from '@ghostfolio/common/timezone-cities-to-countries'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject, of } from 'rxjs'; import { throwError } from 'rxjs'; @@ -45,6 +46,20 @@ export class UserService extends ObservableStore { } } + public getCountry() { + let country: string; + + if (Intl) { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const timeZoneArray = timeZone.split('/'); + const city = timeZoneArray[timeZoneArray.length - 1]; + + country = timezoneCitiesToCountries[city]; + } + + return country; + } + public remove() { this.setState({ user: null }, UserStoreActions.RemoveUser); } diff --git a/libs/common/src/lib/timezone-cities-to-countries.ts b/libs/common/src/lib/timezone-cities-to-countries.ts new file mode 100644 index 000000000..9bc186ff3 --- /dev/null +++ b/libs/common/src/lib/timezone-cities-to-countries.ts @@ -0,0 +1,426 @@ +export const timezoneCitiesToCountries = { + Abidjan: "Côte d'Ivoire", + Accra: 'Ghana', + Adak: 'United States', + Addis_Ababa: 'Ethiopia', + Adelaide: 'Australia', + Aden: 'Yemen', + Algiers: 'Algeria', + Almaty: 'Kazakhstan', + Amman: 'Jordan', + Amsterdam: 'Netherlands', + Anadyr: 'Russia', + Anchorage: 'United States', + Andorra: 'Andorra', + Anguilla: 'Anguilla', + Antananarivo: 'Madagascar', + Antigua: 'Antigua & Barbuda', + Apia: 'Samoa (western)', + Aqtau: 'Kazakhstan', + Aqtobe: 'Kazakhstan', + Araguaina: 'Brazil', + Aruba: 'Aruba', + Ashgabat: 'Turkmenistan', + Asmara: 'Eritrea', + Astrakhan: 'Russia', + Asuncion: 'Paraguay', + Athens: 'Greece', + Atikokan: 'Canada', + Atyrau: 'Kazakhstan', + Auckland: 'New Zealand', + Azores: 'Portugal', + Baghdad: 'Iraq', + Bahia: 'Brazil', + Bahia_Banderas: 'Mexico', + Bahrain: 'Bahrain', + Baku: 'Azerbaijan', + Bamako: 'Mali', + Bangkok: 'Thailand', + Bangui: 'Central African Rep.', + Banjul: 'Gambia', + Barbados: 'Barbados', + Barnaul: 'Russia', + Beirut: 'Lebanon', + Belem: 'Brazil', + Belgrade: 'Serbia', + Belize: 'Belize', + Berlin: 'Germany', + Bermuda: 'Bermuda', + Beulah: 'United States', + Bishkek: 'Kyrgyzstan', + Bissau: 'Guinea-Bissau', + 'Blanc-Sablon': 'Canada', + Blantyre: 'Malawi', + Boa_Vista: 'Brazil', + Bogota: 'Colombia', + Boise: 'United States', + Bougainville: 'Papua New Guinea', + Bratislava: 'Slovakia', + Brazzaville: 'Congo (Rep.)', + Brisbane: 'Australia', + Broken_Hill: 'Australia', + Brunei: 'Brunei', + Brussels: 'Belgium', + Bucharest: 'Romania', + Budapest: 'Hungary', + Buenos_Aires: 'Argentina', + Bujumbura: 'Burundi', + Busingen: 'Germany', + Cairo: 'Egypt', + Cambridge_Bay: 'Canada', + Campo_Grande: 'Brazil', + Canary: 'Spain', + Cancun: 'Mexico', + Cape_Verde: 'Cape Verde', + Caracas: 'Venezuela', + Casablanca: 'Morocco', + Casey: 'Antarctica', + Catamarca: 'Argentina', + Cayenne: 'French Guiana', + Cayman: 'Cayman Islands', + Center: 'United States', + Ceuta: 'Spain', + Chagos: 'British Indian Ocean Territory', + Chatham: 'New Zealand', + Chicago: 'United States', + Chihuahua: 'Mexico', + Chisinau: 'Moldova', + Chita: 'Russia', + Choibalsan: 'Mongolia', + Christmas: 'Christmas Island', + Chuuk: 'Micronesia', + Cocos: 'Cocos (Keeling) Islands', + Colombo: 'Sri Lanka', + Comoro: 'Comoros', + Conakry: 'Guinea', + Copenhagen: 'Denmark', + Cordoba: 'Argentina', + Costa_Rica: 'Costa Rica', + Creston: 'Canada', + Cuiaba: 'Brazil', + Curacao: 'Curaçao', + Dakar: 'Senegal', + Damascus: 'Syria', + Danmarkshavn: 'Greenland', + Dar_es_Salaam: 'Tanzania', + Darwin: 'Australia', + Davis: 'Antarctica', + Dawson: 'Canada', + Dawson_Creek: 'Canada', + Denver: 'United States', + Detroit: 'United States', + Dhaka: 'Bangladesh', + Dili: 'East Timor', + Djibouti: 'Djibouti', + Dominica: 'Dominica', + Douala: 'Cameroon', + Dubai: 'United Arab Emirates', + Dublin: 'Ireland', + DumontDUrville: 'Antarctica', + Dushanbe: 'Tajikistan', + Easter: 'Chile', + Edmonton: 'Canada', + Efate: 'Vanuatu', + Eirunepe: 'Brazil', + El_Aaiun: 'Western Sahara', + El_Salvador: 'El Salvador', + Eucla: 'Australia', + Fakaofo: 'Tokelau', + Famagusta: 'Cyprus', + Faroe: 'Faroe Islands', + Fiji: 'Fiji', + Fort_Nelson: 'Canada', + Fortaleza: 'Brazil', + Freetown: 'Sierra Leone', + Funafuti: 'Tuvalu', + Gaborone: 'Botswana', + Galapagos: 'Ecuador', + Gambier: 'French Polynesia', + Gaza: 'Palestine', + Gibraltar: 'Gibraltar', + Glace_Bay: 'Canada', + Goose_Bay: 'Canada', + Grand_Turk: 'Turks & Caicos Is', + Grenada: 'Grenada', + Guadalcanal: 'Solomon Islands', + Guadeloupe: 'Guadeloupe', + Guam: 'Guam', + Guatemala: 'Guatemala', + Guayaquil: 'Ecuador', + Guernsey: 'Guernsey', + Guyana: 'Guyana', + Halifax: 'Canada', + Harare: 'Zimbabwe', + Havana: 'Cuba', + Hebron: 'Palestine', + Helsinki: 'Finland', + Hermosillo: 'Mexico', + Ho_Chi_Minh: 'Vietnam', + Hobart: 'Australia', + Hong_Kong: 'Hong Kong', + Honolulu: 'United States', + Hovd: 'Mongolia', + Indianapolis: 'United States', + Inuvik: 'Canada', + Iqaluit: 'Canada', + Irkutsk: 'Russia', + Isle_of_Man: 'Isle of Man', + Istanbul: 'Turkey', + Jakarta: 'Indonesia', + Jamaica: 'Jamaica', + Jayapura: 'Indonesia', + Jersey: 'Jersey', + Jerusalem: 'Israel', + Johannesburg: 'South Africa', + Juba: 'South Sudan', + Jujuy: 'Argentina', + Juneau: 'United States', + Kabul: 'Afghanistan', + Kaliningrad: 'Russia', + Kamchatka: 'Russia', + Kampala: 'Uganda', + Kanton: 'Kiribati', + Karachi: 'Pakistan', + Kathmandu: 'Nepal', + Kerguelen: 'French Southern & Antarctic Lands', + Khandyga: 'Russia', + Khartoum: 'Sudan', + Kiev: 'Ukraine', + Kigali: 'Rwanda', + Kinshasa: 'Congo (Dem. Rep.)', + Kiritimati: 'Kiribati', + Kirov: 'Russia', + Knox: 'United States', + Kolkata: 'India', + Kosrae: 'Micronesia', + Kralendijk: 'Caribbean NL', + Krasnoyarsk: 'Russia', + Kuala_Lumpur: 'Malaysia', + Kuching: 'Malaysia', + Kuwait: 'Kuwait', + Kwajalein: 'Marshall Islands', + La_Paz: 'Bolivia', + La_Rioja: 'Argentina', + Lagos: 'Nigeria', + Libreville: 'Gabon', + Lima: 'Peru', + Lindeman: 'Australia', + Lisbon: 'Portugal', + Ljubljana: 'Slovenia', + Lome: 'Togo', + London: 'Britain (UK)', + Longyearbyen: 'Svalbard & Jan Mayen', + Lord_Howe: 'Australia', + Los_Angeles: 'United States', + Louisville: 'United States', + Lower_Princes: 'St Maarten (Dutch)', + Luanda: 'Angola', + Lubumbashi: 'Congo (Dem. Rep.)', + Lusaka: 'Zambia', + Luxembourg: 'Luxembourg', + Macau: 'Macau', + Maceio: 'Brazil', + Macquarie: 'Australia', + Madeira: 'Portugal', + Madrid: 'Spain', + Magadan: 'Russia', + Mahe: 'Seychelles', + Majuro: 'Marshall Islands', + Makassar: 'Indonesia', + Malabo: 'Equatorial Guinea', + Maldives: 'Maldives', + Malta: 'Malta', + Managua: 'Nicaragua', + Manaus: 'Brazil', + Manila: 'Philippines', + Maputo: 'Mozambique', + Marengo: 'United States', + Mariehamn: 'Åland Islands', + Marigot: 'St Martin (French)', + Marquesas: 'French Polynesia', + Martinique: 'Martinique', + Maseru: 'Lesotho', + Matamoros: 'Mexico', + Mauritius: 'Mauritius', + Mawson: 'Antarctica', + Mayotte: 'Mayotte', + Mazatlan: 'Mexico', + Mbabane: 'Eswatini (Swaziland)', + McMurdo: 'Antarctica', + Melbourne: 'Australia', + Mendoza: 'Argentina', + Menominee: 'United States', + Merida: 'Mexico', + Metlakatla: 'United States', + Mexico_City: 'Mexico', + Midway: 'US minor outlying islands', + Minsk: 'Belarus', + Miquelon: 'St Pierre & Miquelon', + Mogadishu: 'Somalia', + Monaco: 'Monaco', + Moncton: 'Canada', + Monrovia: 'Liberia', + Monterrey: 'Mexico', + Montevideo: 'Uruguay', + Monticello: 'United States', + Montserrat: 'Montserrat', + Moscow: 'Russia', + Muscat: 'Oman', + Nairobi: 'Kenya', + Nassau: 'Bahamas', + Nauru: 'Nauru', + Ndjamena: 'Chad', + New_Salem: 'United States', + New_York: 'United States', + Niamey: 'Niger', + Nicosia: 'Cyprus', + Nipigon: 'Canada', + Niue: 'Niue', + Nome: 'United States', + Norfolk: 'Norfolk Island', + Noronha: 'Brazil', + Nouakchott: 'Mauritania', + Noumea: 'New Caledonia', + Novokuznetsk: 'Russia', + Novosibirsk: 'Russia', + Nuuk: 'Greenland', + Ojinaga: 'Mexico', + Omsk: 'Russia', + Oral: 'Kazakhstan', + Oslo: 'Norway', + Ouagadougou: 'Burkina Faso', + Pago_Pago: 'Samoa (American)', + Palau: 'Palau', + Palmer: 'Antarctica', + Panama: 'Panama', + Pangnirtung: 'Canada', + Paramaribo: 'Suriname', + Paris: 'France', + Perth: 'Australia', + Petersburg: 'United States', + Phnom_Penh: 'Cambodia', + Phoenix: 'United States', + Pitcairn: 'Pitcairn', + Podgorica: 'Montenegro', + Pohnpei: 'Micronesia', + Pontianak: 'Indonesia', + 'Port-au-Prince': 'Haiti', + Port_Moresby: 'Papua New Guinea', + Port_of_Spain: 'Trinidad & Tobago', + 'Porto-Novo': 'Benin', + Porto_Velho: 'Brazil', + Prague: 'Czech Republic', + Puerto_Rico: 'Puerto Rico', + Punta_Arenas: 'Chile', + Pyongyang: 'Korea (North)', + Qatar: 'Qatar', + Qostanay: 'Kazakhstan', + Qyzylorda: 'Kazakhstan', + Rainy_River: 'Canada', + Rankin_Inlet: 'Canada', + Rarotonga: 'Cook Islands', + Recife: 'Brazil', + Regina: 'Canada', + Resolute: 'Canada', + Reunion: 'Réunion', + Reykjavik: 'Iceland', + Riga: 'Latvia', + Rio_Branco: 'Brazil', + Rio_Gallegos: 'Argentina', + Riyadh: 'Saudi Arabia', + Rome: 'Italy', + Rothera: 'Antarctica', + Saipan: 'Northern Mariana Islands', + Sakhalin: 'Russia', + Salta: 'Argentina', + Samara: 'Russia', + Samarkand: 'Uzbekistan', + San_Juan: 'Argentina', + San_Luis: 'Argentina', + San_Marino: 'San Marino', + Santarem: 'Brazil', + Santiago: 'Chile', + Santo_Domingo: 'Dominican Republic', + Sao_Paulo: 'Brazil', + Sao_Tome: 'Sao Tome & Principe', + Sarajevo: 'Bosnia & Herzegovina', + Saratov: 'Russia', + Scoresbysund: 'Greenland', + Seoul: 'Korea (South)', + Shanghai: 'China', + Simferopol: 'Russia', + Singapore: 'Singapore', + Sitka: 'United States', + Skopje: 'North Macedonia', + Sofia: 'Bulgaria', + South_Georgia: 'South Georgia & the South Sandwich Islands', + Srednekolymsk: 'Russia', + St_Barthelemy: 'St Barthelemy', + St_Helena: 'St Helena', + St_Johns: 'Canada', + St_Kitts: 'St Kitts & Nevis', + St_Lucia: 'St Lucia', + St_Thomas: 'Virgin Islands (US)', + St_Vincent: 'St Vincent', + Stanley: 'Falkland Islands', + Stockholm: 'Sweden', + Swift_Current: 'Canada', + Sydney: 'Australia', + Syowa: 'Antarctica', + Tahiti: 'French Polynesia', + Taipei: 'Taiwan', + Tallinn: 'Estonia', + Tarawa: 'Kiribati', + Tashkent: 'Uzbekistan', + Tbilisi: 'Georgia', + Tegucigalpa: 'Honduras', + Tehran: 'Iran', + Tell_City: 'United States', + Thimphu: 'Bhutan', + Thule: 'Greenland', + Thunder_Bay: 'Canada', + Tijuana: 'Mexico', + Tirane: 'Albania', + Tokyo: 'Japan', + Tomsk: 'Russia', + Tongatapu: 'Tonga', + Toronto: 'Canada', + Tortola: 'Virgin Islands (UK)', + Tripoli: 'Libya', + Troll: 'Antarctica', + Tucuman: 'Argentina', + Tunis: 'Tunisia', + Ulaanbaatar: 'Mongolia', + Ulyanovsk: 'Russia', + Urumqi: 'China', + Ushuaia: 'Argentina', + 'Ust-Nera': 'Russia', + Uzhgorod: 'Ukraine', + Vaduz: 'Liechtenstein', + Vancouver: 'Canada', + Vatican: 'Vatican City', + Vevay: 'United States', + Vienna: 'Austria', + Vientiane: 'Laos', + Vilnius: 'Lithuania', + Vincennes: 'United States', + Vladivostok: 'Russia', + Volgograd: 'Russia', + Vostok: 'Antarctica', + Wake: 'US minor outlying islands', + Wallis: 'Wallis & Futuna', + Warsaw: 'Poland', + Whitehorse: 'Canada', + Winamac: 'United States', + Windhoek: 'Namibia', + Winnipeg: 'Canada', + Yakutat: 'United States', + Yakutsk: 'Russia', + Yangon: 'Myanmar (Burma)', + Yekaterinburg: 'Russia', + Yellowknife: 'Canada', + Yerevan: 'Armenia', + Zagreb: 'Croatia', + Zaporozhye: 'Ukraine', + Zurich: 'Switzerland' +}; diff --git a/libs/common/src/lib/types/analyticsEventType.type.ts b/libs/common/src/lib/types/analyticsEventType.type.ts new file mode 100644 index 000000000..590119f3b --- /dev/null +++ b/libs/common/src/lib/types/analyticsEventType.type.ts @@ -0,0 +1 @@ +export type AnalyticsEventType = 'createUser'; diff --git a/libs/common/src/lib/types/index.ts b/libs/common/src/lib/types/index.ts index 255a1c3fe..d8c7fa9d9 100644 --- a/libs/common/src/lib/types/index.ts +++ b/libs/common/src/lib/types/index.ts @@ -1,6 +1,7 @@ import type { AccessWithGranteeUser } from './access-with-grantee-user.type'; import { AccountWithPlatform } from './account-with-platform.type'; import { AccountWithValue } from './account-with-value.type'; +import { AnalyticsEventType } from './analyticsEventType.type'; import type { ColorScheme } from './color-scheme'; import type { DateRange } from './date-range.type'; import type { Granularity } from './granularity.type'; @@ -16,6 +17,7 @@ export type { AccessWithGranteeUser, AccountWithPlatform, AccountWithValue, + AnalyticsEventType, ColorScheme, DateRange, Granularity, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3c252395e..9f4c860d1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -46,6 +46,13 @@ model Analytics { User User @relation(fields: [userId], references: [id]) } +model AnalyticsEvent { + createdAt DateTime @default(now()) + data Json? + id String @id @default(uuid()) + type String +} + model AuthDevice { createdAt DateTime @default(now()) credentialId Bytes