Feature/extend analytics by country (#1661)
* Extend analytics by country * Fix Upgrade Plan button of subscription interstitial * Update changelog
This commit is contained in:
parent
e4468252c6
commit
1b2f8e5586
@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Migrated the style of `GfMarketDataDetailDialogModule` to `@angular/material` `15` (mdc)
|
||||
- Upgraded `ng-extract-i18n-merge` from version `2.1.2` to `2.5.0`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the `Upgrade Plan` button of the interstitial for the subscription
|
||||
|
||||
## 1.231.0 - 2023-02-04
|
||||
|
||||
### Added
|
||||
|
@ -244,6 +244,7 @@ export class AdminService {
|
||||
Analytics: {
|
||||
select: {
|
||||
activityCount: true,
|
||||
country: true,
|
||||
updatedAt: true
|
||||
}
|
||||
},
|
||||
@ -277,6 +278,7 @@ export class AdminService {
|
||||
id,
|
||||
subscription,
|
||||
accountCount: _count.Account || 0,
|
||||
country: Analytics.country,
|
||||
lastActivity: Analytics.updatedAt,
|
||||
transactionCount: _count.Order || 0
|
||||
};
|
||||
|
@ -61,8 +61,10 @@ export class AuthService {
|
||||
|
||||
// Create new user if not found
|
||||
user = await this.userService.createUser({
|
||||
data: {
|
||||
provider,
|
||||
thirdPartyId: principalId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -96,8 +98,10 @@ export class AuthService {
|
||||
|
||||
// Create new user if not found
|
||||
user = await this.userService.createUser({
|
||||
data: {
|
||||
provider,
|
||||
thirdPartyId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
7
apps/api/src/app/user/create-user.dto.ts
Normal file
7
apps/api/src/app/user/create-user.dto.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
country?: string;
|
||||
}
|
@ -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<UserItem> {
|
||||
public async signupUser(@Body() data: CreateUserDto): Promise<UserItem> {
|
||||
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 {
|
||||
|
@ -18,6 +18,8 @@ import { Injectable } from '@nestjs/common';
|
||||
import { Prisma, Role, User } from '@prisma/client';
|
||||
import { sortBy } from 'lodash';
|
||||
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
@Injectable()
|
||||
@ -231,7 +233,10 @@ export class UserService {
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
public async createUser(data: Prisma.UserCreateInput): Promise<User> {
|
||||
public async createUser({
|
||||
country,
|
||||
data
|
||||
}: CreateUserDto & { data: Prisma.UserCreateInput }): Promise<User> {
|
||||
if (!data?.provider) {
|
||||
data.provider = 'ANONYMOUS';
|
||||
}
|
||||
@ -256,6 +261,15 @@ export class UserService {
|
||||
}
|
||||
});
|
||||
|
||||
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
|
||||
await this.prismaService.analytics.create({
|
||||
data: {
|
||||
country,
|
||||
User: { connect: { id: user.id } }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (data.provider === 'ANONYMOUS') {
|
||||
const accessToken = this.createAccessToken(
|
||||
user.id,
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { AdminData, User } from '@ghostfolio/common/interfaces';
|
||||
import { AdminData, InfoItem, User } from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import {
|
||||
differenceInSeconds,
|
||||
formatDistanceToNowStrict,
|
||||
@ -16,6 +17,8 @@ import { takeUntil } from 'rxjs/operators';
|
||||
templateUrl: './admin-users.html'
|
||||
})
|
||||
export class AdminUsersComponent implements OnDestroy, OnInit {
|
||||
public hasPermissionForSubscription: boolean;
|
||||
public info: InfoItem;
|
||||
public user: User;
|
||||
public users: AdminData['users'];
|
||||
|
||||
@ -26,6 +29,13 @@ export class AdminUsersComponent implements OnDestroy, OnInit {
|
||||
private dataService: DataService,
|
||||
private userService: UserService
|
||||
) {
|
||||
this.info = this.dataService.fetchInfo();
|
||||
|
||||
this.hasPermissionForSubscription = hasPermission(
|
||||
this.info?.globalPermissions,
|
||||
permissions.enableSubscription
|
||||
);
|
||||
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((state) => {
|
||||
|
@ -7,7 +7,13 @@
|
||||
<tr class="mat-header-row">
|
||||
<th class="mat-header-cell px-1 py-2 text-right">#</th>
|
||||
<th class="mat-header-cell px-1 py-2" i18n>User</th>
|
||||
<th class="mat-header-cell px-1 py-2 text-right">
|
||||
<th
|
||||
*ngIf="hasPermissionForSubscription"
|
||||
class="mat-header-cell px-1 py-2 text-right"
|
||||
>
|
||||
<ng-container i18n>Country</ng-container>
|
||||
</th>
|
||||
<th class="mat-header-cell px-1 py-2">
|
||||
<ng-container i18n>Registration</ng-container>
|
||||
</th>
|
||||
<th class="mat-header-cell px-1 py-2 text-right">
|
||||
@ -16,7 +22,10 @@
|
||||
<th class="mat-header-cell px-1 py-2 text-right">
|
||||
<ng-container i18n>Activities</ng-container>
|
||||
</th>
|
||||
<th class="mat-header-cell px-1 py-2 text-right">
|
||||
<th
|
||||
*ngIf="hasPermissionForSubscription"
|
||||
class="mat-header-cell px-1 py-2 text-right"
|
||||
>
|
||||
<ng-container i18n>Engagement per Day</ng-container>
|
||||
</th>
|
||||
<th class="mat-header-cell px-1 py-2" i18n>Last Request</th>
|
||||
@ -41,7 +50,13 @@
|
||||
></gf-premium-indicator>
|
||||
</div>
|
||||
</td>
|
||||
<td class="mat-cell px-1 py-2 text-right">
|
||||
<td
|
||||
*ngIf="hasPermissionForSubscription"
|
||||
class="mat-cell px-1 py-2 text-right"
|
||||
>
|
||||
{{ userItem.country }}
|
||||
</td>
|
||||
<td class="mat-cell px-1 py-2">
|
||||
{{ formatDistanceToNow(userItem.createdAt) }}
|
||||
</td>
|
||||
<td class="mat-cell px-1 py-2 text-right">
|
||||
@ -58,7 +73,10 @@
|
||||
[value]="userItem.transactionCount"
|
||||
></gf-value>
|
||||
</td>
|
||||
<td class="mat-cell px-1 py-2 text-right">
|
||||
<td
|
||||
*ngIf="hasPermissionForSubscription"
|
||||
class="mat-cell px-1 py-2 text-right"
|
||||
>
|
||||
<gf-value
|
||||
class="d-inline-block justify-content-end"
|
||||
[locale]="user?.settings?.locale"
|
||||
|
@ -19,7 +19,7 @@ export class SubscriptionInterstitialDialog {
|
||||
public dialogRef: MatDialogRef<SubscriptionInterstitialDialog>
|
||||
) {}
|
||||
|
||||
public onCancel() {
|
||||
public closeDialog() {
|
||||
this.dialogRef.close({});
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,13 @@
|
||||
<p>Refine your personal investment strategy now.</p>
|
||||
</div>
|
||||
<div class="justify-content-end" mat-dialog-actions>
|
||||
<button i18n mat-button (click)="onCancel()">Skip</button>
|
||||
<a color="primary" mat-flat-button [routerLink]="['/pricing']">
|
||||
<button i18n mat-button (click)="closeDialog()">Skip</button>
|
||||
<a
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/pricing']"
|
||||
(click)="closeDialog()"
|
||||
>
|
||||
<span i18n>Upgrade Plan</span>
|
||||
<ion-icon class="ml-1" name="arrow-forward-outline"></ion-icon>
|
||||
</a>
|
||||
|
@ -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);
|
||||
|
@ -405,8 +405,8 @@ export class DataService {
|
||||
return this.http.post<OrderModel>(`/api/v1/order`, aOrder);
|
||||
}
|
||||
|
||||
public postUser() {
|
||||
return this.http.post<UserItem>(`/api/v1/user`, {});
|
||||
public postUser({ country }: { country: string }) {
|
||||
return this.http.post<UserItem>(`/api/v1/user`, { country });
|
||||
}
|
||||
|
||||
public putAccount(aAccount: UpdateAccountDto) {
|
||||
|
@ -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<UserStoreState> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ export interface AdminData {
|
||||
userCount: number;
|
||||
users: {
|
||||
accountCount: number;
|
||||
country: string;
|
||||
createdAt: Date;
|
||||
engagement: number;
|
||||
id: string;
|
||||
|
426
libs/common/src/lib/timezone-cities-to-countries.ts
Normal file
426
libs/common/src/lib/timezone-cities-to-countries.ts
Normal file
@ -0,0 +1,426 @@
|
||||
export const timezoneCitiesToCountries = {
|
||||
Abidjan: 'CI',
|
||||
Accra: 'GH',
|
||||
Adak: 'US',
|
||||
Addis_Ababa: 'ET',
|
||||
Adelaide: 'AU',
|
||||
Aden: 'YE',
|
||||
Algiers: 'DZ',
|
||||
Almaty: 'KZ',
|
||||
Amman: 'JO',
|
||||
Amsterdam: 'NL',
|
||||
Anadyr: 'RU',
|
||||
Anchorage: 'US',
|
||||
Andorra: 'AD',
|
||||
Anguilla: 'AI',
|
||||
Antananarivo: 'MG',
|
||||
Antigua: 'AG',
|
||||
Apia: 'WS',
|
||||
Aqtau: 'KZ',
|
||||
Aqtobe: 'KZ',
|
||||
Araguaina: 'BR',
|
||||
Aruba: 'AW',
|
||||
Ashgabat: 'TM',
|
||||
Asmara: 'ER',
|
||||
Astrakhan: 'RU',
|
||||
Asuncion: 'PY',
|
||||
Athens: 'GR',
|
||||
Atikokan: 'CA',
|
||||
Atyrau: 'KZ',
|
||||
Auckland: 'NZ',
|
||||
Azores: 'PT',
|
||||
Baghdad: 'IQ',
|
||||
Bahia: 'BR',
|
||||
Bahia_Banderas: 'MX',
|
||||
Bahrain: 'BH',
|
||||
Baku: 'AZ',
|
||||
Bamako: 'ML',
|
||||
Bangkok: 'TH',
|
||||
Bangui: 'CF',
|
||||
Banjul: 'GM',
|
||||
Barbados: 'BB',
|
||||
Barnaul: 'RU',
|
||||
Beirut: 'LB',
|
||||
Belem: 'BR',
|
||||
Belgrade: 'RS',
|
||||
Belize: 'BZ',
|
||||
Berlin: 'DE',
|
||||
Bermuda: 'BM',
|
||||
Beulah: 'US',
|
||||
Bishkek: 'KG',
|
||||
Bissau: 'GW',
|
||||
'Blanc-Sablon': 'CA',
|
||||
Blantyre: 'MW',
|
||||
Boa_Vista: 'BR',
|
||||
Bogota: 'CO',
|
||||
Boise: 'US',
|
||||
Bougainville: 'PG',
|
||||
Bratislava: 'SK',
|
||||
Brazzaville: 'CG',
|
||||
Brisbane: 'AU',
|
||||
Broken_Hill: 'AU',
|
||||
Brunei: 'BN',
|
||||
Brussels: 'BE',
|
||||
Bucharest: 'RO',
|
||||
Budapest: 'HU',
|
||||
Buenos_Aires: 'AR',
|
||||
Bujumbura: 'BI',
|
||||
Busingen: 'DE',
|
||||
Cairo: 'EG',
|
||||
Cambridge_Bay: 'CA',
|
||||
Campo_Grande: 'BR',
|
||||
Canary: 'ES',
|
||||
Cancun: 'MX',
|
||||
Cape_Verde: 'CV',
|
||||
Caracas: 'VE',
|
||||
Casablanca: 'MA',
|
||||
Casey: 'AQ',
|
||||
Catamarca: 'AR',
|
||||
Cayenne: 'GF',
|
||||
Cayman: 'KY',
|
||||
Center: 'US',
|
||||
Ceuta: 'ES',
|
||||
Chagos: 'IO',
|
||||
Chatham: 'NZ',
|
||||
Chicago: 'US',
|
||||
Chihuahua: 'MX',
|
||||
Chisinau: 'MD',
|
||||
Chita: 'RU',
|
||||
Choibalsan: 'MN',
|
||||
Christmas: 'CX',
|
||||
Chuuk: 'FM',
|
||||
Cocos: 'CC',
|
||||
Colombo: 'LK',
|
||||
Comoro: 'KM',
|
||||
Conakry: 'GN',
|
||||
Copenhagen: 'DK',
|
||||
Cordoba: 'AR',
|
||||
Costa_Rica: 'CR',
|
||||
Creston: 'CA',
|
||||
Cuiaba: 'BR',
|
||||
Curacao: 'CW',
|
||||
Dakar: 'SN',
|
||||
Damascus: 'SY',
|
||||
Danmarkshavn: 'GL',
|
||||
Dar_es_Salaam: 'TZ',
|
||||
Darwin: 'AU',
|
||||
Davis: 'AQ',
|
||||
Dawson: 'CA',
|
||||
Dawson_Creek: 'CA',
|
||||
Denver: 'US',
|
||||
Detroit: 'US',
|
||||
Dhaka: 'BD',
|
||||
Dili: 'TL',
|
||||
Djibouti: 'DJ',
|
||||
Dominica: 'DM',
|
||||
Douala: 'CM',
|
||||
Dubai: 'AE',
|
||||
Dublin: 'IE',
|
||||
DumontDUrville: 'AQ',
|
||||
Dushanbe: 'TJ',
|
||||
Easter: 'CL',
|
||||
Edmonton: 'CA',
|
||||
Efate: 'VU',
|
||||
Eirunepe: 'BR',
|
||||
El_Aaiun: 'EH',
|
||||
El_Salvador: 'SV',
|
||||
Eucla: 'AU',
|
||||
Fakaofo: 'TK',
|
||||
Famagusta: 'CY',
|
||||
Faroe: 'FO',
|
||||
Fiji: 'FJ',
|
||||
Fort_Nelson: 'CA',
|
||||
Fortaleza: 'BR',
|
||||
Freetown: 'SL',
|
||||
Funafuti: 'TV',
|
||||
Gaborone: 'BW',
|
||||
Galapagos: 'EC',
|
||||
Gambier: 'PF',
|
||||
Gaza: 'PS',
|
||||
Gibraltar: 'GI',
|
||||
Glace_Bay: 'CA',
|
||||
Goose_Bay: 'CA',
|
||||
Grand_Turk: 'TC',
|
||||
Grenada: 'GD',
|
||||
Guadalcanal: 'SB',
|
||||
Guadeloupe: 'GP',
|
||||
Guam: 'GU',
|
||||
Guatemala: 'GT',
|
||||
Guayaquil: 'EC',
|
||||
Guernsey: 'GG',
|
||||
Guyana: 'GY',
|
||||
Halifax: 'CA',
|
||||
Harare: 'ZW',
|
||||
Havana: 'CU',
|
||||
Hebron: 'PS',
|
||||
Helsinki: 'FI',
|
||||
Hermosillo: 'MX',
|
||||
Ho_Chi_Minh: 'VN',
|
||||
Hobart: 'AU',
|
||||
Hong_Kong: 'HK',
|
||||
Honolulu: 'US',
|
||||
Hovd: 'MN',
|
||||
Indianapolis: 'US',
|
||||
Inuvik: 'CA',
|
||||
Iqaluit: 'CA',
|
||||
Irkutsk: 'RU',
|
||||
Isle_of_Man: 'IM',
|
||||
Istanbul: 'TR',
|
||||
Jakarta: 'ID',
|
||||
Jamaica: 'JM',
|
||||
Jayapura: 'ID',
|
||||
Jersey: 'JE',
|
||||
Jerusalem: 'IL',
|
||||
Johannesburg: 'ZA',
|
||||
Juba: 'SS',
|
||||
Jujuy: 'AR',
|
||||
Juneau: 'US',
|
||||
Kabul: 'AF',
|
||||
Kaliningrad: 'RU',
|
||||
Kamchatka: 'RU',
|
||||
Kampala: 'UG',
|
||||
Kanton: 'KI',
|
||||
Karachi: 'PK',
|
||||
Kathmandu: 'NP',
|
||||
Kerguelen: 'TF',
|
||||
Khandyga: 'RU',
|
||||
Khartoum: 'SD',
|
||||
Kiev: 'UA',
|
||||
Kigali: 'RW',
|
||||
Kinshasa: 'CD',
|
||||
Kiritimati: 'KI',
|
||||
Kirov: 'RU',
|
||||
Knox: 'US',
|
||||
Kolkata: 'IN',
|
||||
Kosrae: 'FM',
|
||||
Kralendijk: 'NL',
|
||||
Krasnoyarsk: 'RU',
|
||||
Kuala_Lumpur: 'MY',
|
||||
Kuching: 'MY',
|
||||
Kuwait: 'KW',
|
||||
Kwajalein: 'MH',
|
||||
La_Paz: 'BO',
|
||||
La_Rioja: 'AR',
|
||||
Lagos: 'NG',
|
||||
Libreville: 'GA',
|
||||
Lima: 'PE',
|
||||
Lindeman: 'AU',
|
||||
Lisbon: 'PT',
|
||||
Ljubljana: 'SI',
|
||||
Lome: 'TG',
|
||||
London: 'GB',
|
||||
Longyearbyen: 'SJ',
|
||||
Lord_Howe: 'AU',
|
||||
Los_Angeles: 'US',
|
||||
Louisville: 'US',
|
||||
Lower_Princes: 'SX',
|
||||
Luanda: 'AO',
|
||||
Lubumbashi: 'CD',
|
||||
Lusaka: 'ZM',
|
||||
Luxembourg: 'LU',
|
||||
Macau: 'MO',
|
||||
Maceio: 'BR',
|
||||
Macquarie: 'AU',
|
||||
Madeira: 'PT',
|
||||
Madrid: 'ES',
|
||||
Magadan: 'RU',
|
||||
Mahe: 'SC',
|
||||
Majuro: 'MH',
|
||||
Makassar: 'ID',
|
||||
Malabo: 'GQ',
|
||||
Maldives: 'MV',
|
||||
Malta: 'MT',
|
||||
Managua: 'NI',
|
||||
Manaus: 'BR',
|
||||
Manila: 'PH',
|
||||
Maputo: 'MZ',
|
||||
Marengo: 'US',
|
||||
Mariehamn: 'AX',
|
||||
Marigot: 'MF',
|
||||
Marquesas: 'PF',
|
||||
Martinique: 'MQ',
|
||||
Maseru: 'LS',
|
||||
Matamoros: 'MX',
|
||||
Mauritius: 'MU',
|
||||
Mawson: 'AQ',
|
||||
Mayotte: 'YT',
|
||||
Mazatlan: 'MX',
|
||||
Mbabane: 'SZ',
|
||||
McMurdo: 'AQ',
|
||||
Melbourne: 'AU',
|
||||
Mendoza: 'AR',
|
||||
Menominee: 'US',
|
||||
Merida: 'MX',
|
||||
Metlakatla: 'US',
|
||||
Mexico_City: 'MX',
|
||||
Midway: 'UM',
|
||||
Minsk: 'BY',
|
||||
Miquelon: 'PM',
|
||||
Mogadishu: 'SO',
|
||||
Monaco: 'MC',
|
||||
Moncton: 'CA',
|
||||
Monrovia: 'LR',
|
||||
Monterrey: 'MX',
|
||||
Montevideo: 'UY',
|
||||
Monticello: 'US',
|
||||
Montserrat: 'MS',
|
||||
Moscow: 'RU',
|
||||
Muscat: 'OM',
|
||||
Nairobi: 'KE',
|
||||
Nassau: 'BS',
|
||||
Nauru: 'NR',
|
||||
Ndjamena: 'TD',
|
||||
New_Salem: 'US',
|
||||
New_York: 'US',
|
||||
Niamey: 'NE',
|
||||
Nicosia: 'CY',
|
||||
Nipigon: 'CA',
|
||||
Niue: 'NU',
|
||||
Nome: 'US',
|
||||
Norfolk: 'NF',
|
||||
Noronha: 'BR',
|
||||
Nouakchott: 'MR',
|
||||
Noumea: 'NC',
|
||||
Novokuznetsk: 'RU',
|
||||
Novosibirsk: 'RU',
|
||||
Nuuk: 'GL',
|
||||
Ojinaga: 'MX',
|
||||
Omsk: 'RU',
|
||||
Oral: 'KZ',
|
||||
Oslo: 'NO',
|
||||
Ouagadougou: 'BF',
|
||||
Pago_Pago: 'AS',
|
||||
Palau: 'PW',
|
||||
Palmer: 'AQ',
|
||||
Panama: 'PA',
|
||||
Pangnirtung: 'CA',
|
||||
Paramaribo: 'SR',
|
||||
Paris: 'FR',
|
||||
Perth: 'AU',
|
||||
Petersburg: 'US',
|
||||
Phnom_Penh: 'KH',
|
||||
Phoenix: 'US',
|
||||
Pitcairn: 'PN',
|
||||
Podgorica: 'ME',
|
||||
Pohnpei: 'FM',
|
||||
Pontianak: 'ID',
|
||||
'Port-au-Prince': 'HT',
|
||||
Port_Moresby: 'PG',
|
||||
Port_of_Spain: 'TT',
|
||||
'Porto-Novo': 'BJ',
|
||||
Porto_Velho: 'BR',
|
||||
Prague: 'CZ',
|
||||
Puerto_Rico: 'PR',
|
||||
Punta_Arenas: 'CL',
|
||||
Pyongyang: 'KP',
|
||||
Qatar: 'QA',
|
||||
Qostanay: 'KZ',
|
||||
Qyzylorda: 'KZ',
|
||||
Rainy_River: 'CA',
|
||||
Rankin_Inlet: 'CA',
|
||||
Rarotonga: 'CK',
|
||||
Recife: 'BR',
|
||||
Regina: 'CA',
|
||||
Resolute: 'CA',
|
||||
Reunion: 'RE',
|
||||
Reykjavik: 'IS',
|
||||
Riga: 'LV',
|
||||
Rio_Branco: 'BR',
|
||||
Rio_Gallegos: 'AR',
|
||||
Riyadh: 'SA',
|
||||
Rome: 'IT',
|
||||
Rothera: 'AQ',
|
||||
Saipan: 'MP',
|
||||
Sakhalin: 'RU',
|
||||
Salta: 'AR',
|
||||
Samara: 'RU',
|
||||
Samarkand: 'UZ',
|
||||
San_Juan: 'AR',
|
||||
San_Luis: 'AR',
|
||||
San_Marino: 'SM',
|
||||
Santarem: 'BR',
|
||||
Santiago: 'CL',
|
||||
Santo_Domingo: 'DO',
|
||||
Sao_Paulo: 'BR',
|
||||
Sao_Tome: 'ST',
|
||||
Sarajevo: 'BA',
|
||||
Saratov: 'RU',
|
||||
Scoresbysund: 'GL',
|
||||
Seoul: 'KR',
|
||||
Shanghai: 'CN',
|
||||
Simferopol: 'RU',
|
||||
Singapore: 'SG',
|
||||
Sitka: 'US',
|
||||
Skopje: 'MK',
|
||||
Sofia: 'BG',
|
||||
South_Georgia: 'GS',
|
||||
Srednekolymsk: 'RU',
|
||||
St_Barthelemy: 'BL',
|
||||
St_Helena: 'SH',
|
||||
St_Johns: 'CA',
|
||||
St_Kitts: 'KN',
|
||||
St_Lucia: 'LC',
|
||||
St_Thomas: 'VI',
|
||||
St_Vincent: 'VC',
|
||||
Stanley: 'FK',
|
||||
Stockholm: 'SE',
|
||||
Swift_Current: 'CA',
|
||||
Sydney: 'AU',
|
||||
Syowa: 'AQ',
|
||||
Tahiti: 'PF',
|
||||
Taipei: 'TW',
|
||||
Tallinn: 'EE',
|
||||
Tarawa: 'KI',
|
||||
Tashkent: 'UZ',
|
||||
Tbilisi: 'GE',
|
||||
Tegucigalpa: 'HN',
|
||||
Tehran: 'IR',
|
||||
Tell_City: 'US',
|
||||
Thimphu: 'BT',
|
||||
Thule: 'GL',
|
||||
Thunder_Bay: 'CA',
|
||||
Tijuana: 'MX',
|
||||
Tirane: 'AL',
|
||||
Tokyo: 'JP',
|
||||
Tomsk: 'RU',
|
||||
Tongatapu: 'TO',
|
||||
Toronto: 'CA',
|
||||
Tortola: 'VI (UK)',
|
||||
Tripoli: 'LY',
|
||||
Troll: 'AQ',
|
||||
Tucuman: 'AR',
|
||||
Tunis: 'TN',
|
||||
Ulaanbaatar: 'MN',
|
||||
Ulyanovsk: 'RU',
|
||||
Urumqi: 'CN',
|
||||
Ushuaia: 'AR',
|
||||
'Ust-Nera': 'RU',
|
||||
Uzhgorod: 'UA',
|
||||
Vaduz: 'LI',
|
||||
Vancouver: 'CA',
|
||||
Vatican: 'VA',
|
||||
Vevay: 'US',
|
||||
Vienna: 'AT',
|
||||
Vientiane: 'LA',
|
||||
Vilnius: 'LT',
|
||||
Vincennes: 'US',
|
||||
Vladivostok: 'RU',
|
||||
Volgograd: 'RU',
|
||||
Vostok: 'AQ',
|
||||
Wake: 'UM',
|
||||
Wallis: 'WF',
|
||||
Warsaw: 'PL',
|
||||
Whitehorse: 'CA',
|
||||
Winamac: 'US',
|
||||
Windhoek: 'NA',
|
||||
Winnipeg: 'CA',
|
||||
Yakutat: 'US',
|
||||
Yakutsk: 'RU',
|
||||
Yangon: 'MM',
|
||||
Yekaterinburg: 'RU',
|
||||
Yellowknife: 'CA',
|
||||
Yerevan: 'AM',
|
||||
Zagreb: 'HR',
|
||||
Zaporozhye: 'UA',
|
||||
Zurich: 'CH'
|
||||
};
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Analytics" ADD COLUMN "country" TEXT;
|
@ -41,6 +41,7 @@ model Account {
|
||||
|
||||
model Analytics {
|
||||
activityCount Int @default(0)
|
||||
country String?
|
||||
updatedAt DateTime @updatedAt
|
||||
userId String @id
|
||||
User User @relation(fields: [userId], references: [id])
|
||||
|
Loading…
x
Reference in New Issue
Block a user