From fe5234806abe23add471bd97082005d2a3865bc8 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 24 Nov 2023 02:14:38 +0100 Subject: [PATCH] first draft seeding users to gms --- backend/package.json | 1 + backend/src/apis/gms/GmsClient.ts | 66 ++++++++ backend/src/apis/gms/model/GmsEnums.ts | 37 +++++ backend/src/apis/gms/model/GmsUser.ts | 136 ++++++++++++++++- backend/src/apis/gms/model/GmsUserProfile.ts | 14 +- backend/src/config/index.ts | 2 +- backend/src/seeds/gmsUsers.ts | 91 +++++++++++ backend/src/util/communityUser.ts | 4 + .../Community.ts | 66 ++++++++ .../0075-introduce_gms_registration/User.ts | 144 ++++++++++++++++++ .../UserContact.ts | 93 +++++++++++ database/entity/Community.ts | 2 +- database/entity/User.ts | 2 +- database/entity/UserContact.ts | 2 +- .../0075-introduce_gms_registration.ts | 66 ++++++++ dht-node/src/config/index.ts | 2 +- federation/src/config/index.ts | 2 +- 17 files changed, 710 insertions(+), 20 deletions(-) create mode 100644 backend/src/apis/gms/model/GmsEnums.ts create mode 100644 backend/src/seeds/gmsUsers.ts create mode 100644 database/entity/0075-introduce_gms_registration/Community.ts create mode 100644 database/entity/0075-introduce_gms_registration/User.ts create mode 100644 database/entity/0075-introduce_gms_registration/UserContact.ts create mode 100644 database/migrations/0075-introduce_gms_registration.ts diff --git a/backend/package.json b/backend/package.json index 1f4ea67af..8140bdd3d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -16,6 +16,7 @@ "test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --forceExit --detectOpenHandles", "seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts", "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts", + "gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUsers.ts", "locales": "scripts/sort.sh" }, "dependencies": { diff --git a/backend/src/apis/gms/GmsClient.ts b/backend/src/apis/gms/GmsClient.ts index d96fbf879..ec3298267 100644 --- a/backend/src/apis/gms/GmsClient.ts +++ b/backend/src/apis/gms/GmsClient.ts @@ -78,3 +78,69 @@ export async function userList(): Promise { return errMsg } } + +export async function userByUuid(uuid: string): Promise { + const baseUrl = 'http://'.concat(CONFIG.GMS_HOST).concat(':').concat(CONFIG.GMS_PORT).concat('/') + const service = 'community-user/list?page=1&perPage=20' + const config = { + headers: { + accept: 'application/json', + language: 'en', + timezone: 'UTC', + connection: 'keep-alive', + authorization: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiVTJGc2RHVmtYMThuNzllbGJscThDbmxxZ0I2SGxicTZuajlpM2lmV3BTc3pHZFRtOFVTQjJZNWY2bG56elhuSUF0SEwvYVBWdE1uMjA3bnNtWDQ0M21xWVFyd0xJMklHNGtpRkZ3U2FKbVJwRk9VZXNDMXIyRGlta3VLMklwN1lYRTU0c2MzVmlScmMzaHE3djlFNkRabk4xeVMrU1QwRWVZRFI5c09pTDJCdmg4a05DNUc5NTdoZUJzeWlRbXcrNFFmMXFuUk5SNXpWdXhtZEE2WUUrT3hlcS85Y0d6NURyTmhoaHM3MTJZTFcvTmprZGNwdU55dUgxeWxhNEhJZyIsImlhdCI6MTcwMDUxMDg4OX0.WhtNGZc9A_hUfh8CcPjr44kWQWMkKJ7hlYXELOd3yy4', + }, + } + try { + const result = await axios.get(baseUrl.concat(service), config) + logger.debug('GET-Response of community/list:', result) + if (result.status !== 200) { + throw new LogError( + 'HTTP Status Error in community-user/list:', + result.status, + result.statusText, + ) + } + logger.debug('responseData:', result.data.responseData.data) + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const gmsUser = JSON.parse(result.data.responseData.data) + logger.debug('gmsUser:', gmsUser) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return gmsUser + } catch (error: any) { + logger.error('Error in Get community-user/list:', error) + const errMsg: string = error.message + return errMsg + } +} + +export async function createGmsUser(apiKey: string, user: GmsUser): Promise { + const baseUrl = 'http://'.concat(CONFIG.GMS_HOST).concat(':').concat(CONFIG.GMS_PORT).concat('/') + const service = 'community-user' + const config = { + headers: { + accept: 'application/json', + language: 'en', + timezone: 'UTC', + connection: 'keep-alive', + authorization: apiKey, + // 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiVTJGc2RHVmtYMThuNzllbGJscThDbmxxZ0I2SGxicTZuajlpM2lmV3BTc3pHZFRtOFVTQjJZNWY2bG56elhuSUF0SEwvYVBWdE1uMjA3bnNtWDQ0M21xWVFyd0xJMklHNGtpRkZ3U2FKbVJwRk9VZXNDMXIyRGlta3VLMklwN1lYRTU0c2MzVmlScmMzaHE3djlFNkRabk4xeVMrU1QwRWVZRFI5c09pTDJCdmg4a05DNUc5NTdoZUJzeWlRbXcrNFFmMXFuUk5SNXpWdXhtZEE2WUUrT3hlcS85Y0d6NURyTmhoaHM3MTJZTFcvTmprZGNwdU55dUgxeWxhNEhJZyIsImlhdCI6MTcwMDUxMDg4OX0.WhtNGZc9A_hUfh8CcPjr44kWQWMkKJ7hlYXELOd3yy4', + }, + } + try { + const result = await axios.post(baseUrl.concat(service), user, config) + logger.debug('POST-Response of community-user:', result) + if (result.status !== 200) { + throw new LogError('HTTP Status Error in community-user:', result.status, result.statusText) + } + logger.debug('responseData:', result.data.responseData.data) + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const gmsUser = JSON.parse(result.data.responseData) + logger.debug('gmsUser:', gmsUser) + return true + } catch (error: any) { + logger.error('Error in Get community-user:', error) + throw new LogError(error.message) + } +} diff --git a/backend/src/apis/gms/model/GmsEnums.ts b/backend/src/apis/gms/model/GmsEnums.ts new file mode 100644 index 000000000..00662c47c --- /dev/null +++ b/backend/src/apis/gms/model/GmsEnums.ts @@ -0,0 +1,37 @@ +import { registerEnumType } from 'type-graphql' + +export enum GmsPublishNameType { + GMS_PUBLISH_NAME_NOTHING = 0, + GMS_PUBLISH_NAME_INITIALS = 1, + GMS_PUBLISH_NAME_FIRST = 2, + GMS_PUBLISH_NAME_FIRST_INITIAL = 3, + GMS_PUBLISH_NAME_FULL = 4, +} + +registerEnumType(GmsPublishNameType, { + name: 'GmsPublishNameType', // this one is mandatory + description: 'Type of name publishing', // this one is optional +}) + +export enum GmsPublishPhoneType { + GMS_PUBLISH_PHONE_NOTHING = 0, + GMS_PUBLISH_PHONE_COUNTRY = 1, + GMS_PUBLISH_PHONE_FULL = 2, +} + +registerEnumType(GmsPublishPhoneType, { + name: 'GmsPublishPhoneType', // this one is mandatory + description: 'Type of Phone publishing', // this one is optional +}) + +export enum GmsPublishPostType { + GMS_PUBLISH_POST_NOTHING = 0, + GMS_PUBLISH_POST_COUNTRY = 1, + GMS_PUBLISH_POST_CITY = 2, + GMS_PUBLISH_POST_FULL = 3, +} + +registerEnumType(GmsPublishPostType, { + name: 'GmsPublishPostType', // this one is mandatory + description: 'Type of name publishing', // this one is optional +}) diff --git a/backend/src/apis/gms/model/GmsUser.ts b/backend/src/apis/gms/model/GmsUser.ts index aa1ae4ce4..84c7041d8 100644 --- a/backend/src/apis/gms/model/GmsUser.ts +++ b/backend/src/apis/gms/model/GmsUser.ts @@ -1,18 +1,140 @@ +import { User as dbUser } from '@entity/User' + +import { GmsPublishNameType, GmsPublishPhoneType, GmsPublishPostType } from './GmsEnums' import { GmsRole } from './GmsRoles' import { GmsUserAccount } from './GmsUserAccount' import { GmsUserProfile } from './GmsUserProfile' export class GmsUser { + constructor(user: dbUser) { + this.userUuid = user.gradidoID + // this.communityUuid = user.communityUuid + this.email = this.getGmsEmail(user) + this.countryCode = this.getGmsCountryCode(user) + this.mobile = this.getGmsPhone(user) + this.firstName = this.getGmsFirstName(user) + this.lastName = this.getGmsLastName(user) + this.alias = user.alias ? user.alias : undefined + this.address = this.getGmsAddress(user) + this.zipCode = this.getGmsZipCode(user) + this.city = this.getGmsCity(user) + this.country = this.getGmsCountry(user) + this.type = 1 + this.location = null + } + id: number - uuid: string + userUuid: string communityUuid: string - email: string - countryCode: string - mobile: string + email: string | undefined + countryCode: string | undefined + mobile: string | undefined status: number createdAt: Date updatedAt: Date - userProfile: GmsUserProfile - userAccounts: GmsUserAccount[] - roles: GmsRole[] + firstName: string | undefined + lastName: string | undefined + alias: string | undefined + type: number + address: string | undefined + city: string | undefined + state: string + country: string | undefined + zipCode: string | undefined + language: string + location: unknown + + private getGmsFirstName(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + (user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_FIRST || + user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_FULL) + ) { + return user.firstName + } + if ( + user.gmsAllowed && + (user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL || + user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_INITIALS) + ) { + return user.firstName.substring(0, 1) + '.' + } + } + + private getGmsLastName(user: dbUser): string | undefined { + if (user.gmsAllowed && user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_FULL) { + return user.lastName + } + if ( + user.gmsAllowed && + (user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL || + user.gmsPublishName === GmsPublishNameType.GMS_PUBLISH_NAME_INITIALS) + ) { + return user.lastName.substring(0, 1) + '.' + } + } + + private getGmsEmail(user: dbUser): string | undefined { + if (user.gmsAllowed && user.emailContact.gmsPublishEmail === true) { + return user.emailContact.email + } + } + + private getGmsCountryCode(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + (user.emailContact.gmsPublishPhone === GmsPublishPhoneType.GMS_PUBLISH_PHONE_COUNTRY || + user.emailContact.gmsPublishPhone === GmsPublishPhoneType.GMS_PUBLISH_PHONE_FULL) + ) { + return user.emailContact.countryCode + } + } + + private getGmsPhone(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + user.emailContact.gmsPublishPhone === GmsPublishPhoneType.GMS_PUBLISH_PHONE_FULL + ) { + return user.emailContact.phone + } + } + + private getGmsAddress(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_FULL + ) { + return user.emailContact.address + } + } + + private getGmsZipCode(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + (user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_CITY || + user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_FULL) + ) { + return user.emailContact.zipCode + } + } + + private getGmsCity(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + (user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_CITY || + user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_FULL) + ) { + return user.emailContact.city + } + } + + private getGmsCountry(user: dbUser): string | undefined { + if ( + user.gmsAllowed && + (user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_COUNTRY || + user.emailContact.gmsPublishPost === GmsPublishPostType.GMS_PUBLISH_POST_FULL) + ) { + return user.emailContact.country + } + } } diff --git a/backend/src/apis/gms/model/GmsUserProfile.ts b/backend/src/apis/gms/model/GmsUserProfile.ts index de47a1639..eb0b2dccf 100644 --- a/backend/src/apis/gms/model/GmsUserProfile.ts +++ b/backend/src/apis/gms/model/GmsUserProfile.ts @@ -1,22 +1,22 @@ import { Decimal } from 'decimal.js-light' export class GmsUserProfile { - firstName: string - lastName: string + firstName: string | undefined + lastName: string | undefined alias: string type: number - name: string + name: string | undefined location: { type: string coordinates: [Decimal, Decimal] } accuracy: unknown - address: string - city: string + address: string | undefined + city: string | undefined state: string - country: string - zipCode: string + country: string | undefined + zipCode: string | undefined language: string profileImage: unknown } diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 645192476..d28256119 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -12,7 +12,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0074-insert_communityuuid in_existing_users', + DB_VERSION: '0075-introduce_gms_registration', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/backend/src/seeds/gmsUsers.ts b/backend/src/seeds/gmsUsers.ts new file mode 100644 index 000000000..c979ed29a --- /dev/null +++ b/backend/src/seeds/gmsUsers.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ + +import { entities } from '@entity/index' +import { User as DbUser } from '@entity/User' +import { createTestClient } from 'apollo-server-testing' + +import { createGmsUser } from '@/apis/gms/GmsClient' +import { GmsUser } from '@/apis/gms/model/GmsUser' +import { CONFIG } from '@/config' +import { getHomeCommunity } from '@/graphql/resolver/util/communities' +import { createServer } from '@/server/createServer' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +CONFIG.EMAIL = false + +const context = { + token: '', + setHeaders: { + push: (value: { key: string; value: string }): void => { + context.token = value.value + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + forEach: (): void => {}, + }, + clientTimezoneOffset: 0, +} + +export const cleanDB = async () => { + // this only works as long we do not have foreign key constraints + for (const entity of entities) { + await resetEntity(entity) + } +} + +const resetEntity = async (entity: any) => { + const items = await entity.find({ withDeleted: true }) + if (items.length > 0) { + const ids = items.map((e: any) => e.id) + await entity.delete(ids) + } +} + +const run = async () => { + const server = await createServer(context) + const seedClient = createTestClient(server.apollo) + const { con } = server + + const homeCom = await getHomeCommunity() + if (homeCom.gmsApiKey === null) { + throw new LogError('HomeCommunity needs GMS-ApiKey to publish user data to GMS.') + } + // read the ids of all local users, which are still not gms registered + const userIds = await DbUser.query( + 'SELECT `id` from `users` `u` where `u`.`foreign` = false and `deleted_at` is null and `gms_registered` = false', + ) + logger.debug('userIds:', userIds) + + for (const idStr of userIds) { + logger.debug('Id:', idStr.id) + const user = await DbUser.findOne({ + where: { id: idStr.id }, + relations: ['emailContact'], + }) + if (user) { + logger.debug('found local User:', user) + const gmsUser = new GmsUser(user) + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + if (await createGmsUser(homeCom.gmsApiKey, gmsUser)) { + logger.debug('GMS user published successfully:', gmsUser) + user.gmsRegistered = true + user.gmsRegisteredAt = new Date() + await DbUser.save(user) + logger.debug('mark user as gms published:', user) + } + } catch (err) { + logger.warn('publishing user fails with ', err) + } + } + } + logger.info('##gms## publishing all local users successful...') + + await con.close() +} + +void run() diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index bad06f201..a0c4bf681 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -50,6 +50,10 @@ const communityDbUser: dbUser = { }, foreign: false, communityUuid: '55555555-4444-4333-2222-11111111', + gmsPublishName: 0, + gmsAllowed: false, + gmsRegistered: false, + gmsRegisteredAt: null, } const communityUser = new User(communityDbUser) diff --git a/database/entity/0075-introduce_gms_registration/Community.ts b/database/entity/0075-introduce_gms_registration/Community.ts new file mode 100644 index 000000000..89c31763f --- /dev/null +++ b/database/entity/0075-introduce_gms_registration/Community.ts @@ -0,0 +1,66 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm' + +@Entity('communities') +export class Community extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'foreign', type: 'bool', nullable: false, default: true }) + foreign: boolean + + @Column({ name: 'url', length: 255, nullable: false }) + url: string + + @Column({ name: 'public_key', type: 'binary', length: 32, nullable: false }) + publicKey: Buffer + + @Column({ name: 'private_key', type: 'binary', length: 64, nullable: true }) + privateKey: Buffer | null + + @Column({ + name: 'community_uuid', + type: 'char', + length: 36, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + communityUuid: string | null + + @Column({ name: 'authenticated_at', type: 'datetime', nullable: true }) + authenticatedAt: Date | null + + @Column({ name: 'name', type: 'varchar', length: 40, nullable: true }) + name: string | null + + @Column({ name: 'description', type: 'varchar', length: 255, nullable: true }) + description: string | null + + @CreateDateColumn({ name: 'creation_date', type: 'datetime', nullable: true }) + creationDate: Date | null + + @Column({ name: 'gms_api_key', type: 'varchar', length: 512, nullable: true, default: null }) + gmsApiKey: string | null + + @CreateDateColumn({ + name: 'created_at', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP(3)', + nullable: false, + }) + createdAt: Date + + @UpdateDateColumn({ + name: 'updated_at', + type: 'datetime', + onUpdate: 'CURRENT_TIMESTAMP(3)', + nullable: true, + }) + updatedAt: Date | null +} diff --git a/database/entity/0075-introduce_gms_registration/User.ts b/database/entity/0075-introduce_gms_registration/User.ts new file mode 100644 index 000000000..edcdd1257 --- /dev/null +++ b/database/entity/0075-introduce_gms_registration/User.ts @@ -0,0 +1,144 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, + OneToOne, +} from 'typeorm' +import { Contribution } from '../Contribution' +import { ContributionMessage } from '../ContributionMessage' +import { UserContact } from '../UserContact' +import { UserRole } from '../UserRole' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ type: 'bool', default: false }) + foreign: boolean + + @Column({ + name: 'gradido_id', + length: 36, + nullable: false, + collation: 'utf8mb4_unicode_ci', + }) + gradidoID: string + + @Column({ + name: 'community_uuid', + type: 'char', + length: 36, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + communityUuid: string + + @Column({ + name: 'alias', + length: 20, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + alias: string + + @OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user) + @JoinColumn({ name: 'email_id' }) + emailContact: UserContact + + @Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null }) + emailId: number | null + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ name: 'gms_publish_name', type: 'int', unsigned: true, nullable: false, default: 0 }) + gmsPublishName: number + + @Column({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP(3)', nullable: false }) + createdAt: Date + + @DeleteDateColumn({ name: 'deleted_at', nullable: true }) + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ + name: 'password_encryption_type', + type: 'int', + unsigned: true, + nullable: false, + default: 0, + }) + passwordEncryptionType: number + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ type: 'bool', default: false }) + hideAmountGDD: boolean + + @Column({ type: 'bool', default: false }) + hideAmountGDT: boolean + + @OneToMany(() => UserRole, (userRole) => userRole.user) + @JoinColumn({ name: 'user_id' }) + userRoles: UserRole[] + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ + name: 'contribution_link_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + contributionLinkId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @Column({ name: 'gms_allowed', type: 'bool', default: true }) + gmsAllowed: boolean + + @Column({ name: 'gms_registered', type: 'bool', default: false }) + gmsRegistered: boolean + + @Column({ name: 'gms_registered_at', type: 'datetime', default: null, nullable: true }) + gmsRegisteredAt: Date | null + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] + + @OneToMany(() => ContributionMessage, (message) => message.user) + @JoinColumn({ name: 'user_id' }) + messages?: ContributionMessage[] + + @OneToMany(() => UserContact, (userContact: UserContact) => userContact.user) + @JoinColumn({ name: 'user_id' }) + userContacts?: UserContact[] +} diff --git a/database/entity/0075-introduce_gms_registration/UserContact.ts b/database/entity/0075-introduce_gms_registration/UserContact.ts new file mode 100644 index 000000000..a3d0a8f52 --- /dev/null +++ b/database/entity/0075-introduce_gms_registration/UserContact.ts @@ -0,0 +1,93 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToOne, +} from 'typeorm' +import { User } from '../User' + +@Entity('user_contacts', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class UserContact extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ + name: 'type', + length: 100, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + type: string + + @OneToOne(() => User, (user) => user.emailContact) + user: User + + @Column({ name: 'user_id', type: 'int', unsigned: true, nullable: false }) + userId: number + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ name: 'gms_publish_email', type: 'bool', nullable: false, default: false }) + gmsPublishEmail: boolean + + @Column({ name: 'email_verification_code', type: 'bigint', unsigned: true, unique: true }) + emailVerificationCode: string + + @Column({ name: 'email_opt_in_type_id' }) + emailOptInTypeId: number + + @Column({ name: 'email_resend_count' }) + emailResendCount: number + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ + name: 'country_code', + length: 255, + unique: false, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + countryCode: string + + @Column({ length: 255, unique: false, nullable: true, collation: 'utf8mb4_unicode_ci' }) + phone: string + + @Column({ name: 'gms_publish_phone', type: 'int', unsigned: true, nullable: false, default: 0 }) + gmsPublishPhone: number + + @Column({ length: 255, unique: false, nullable: true, collation: 'utf8mb4_unicode_ci' }) + address: string + + @Column({ length: 255, unique: false, nullable: true, collation: 'utf8mb4_unicode_ci' }) + city: string + + @Column({ + name: 'zip_code', + length: 255, + unique: false, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + zipCode: string + + @Column({ length: 255, unique: false, nullable: true, collation: 'utf8mb4_unicode_ci' }) + country: string + + @Column({ name: 'gms_publish_post', type: 'int', unsigned: true, nullable: false, default: 0 }) + gmsPublishPost: number + + @Column({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'updated_at', nullable: true, default: null, type: 'datetime' }) + updatedAt: Date | null + + @DeleteDateColumn({ name: 'deleted_at', nullable: true }) + deletedAt: Date | null +} diff --git a/database/entity/Community.ts b/database/entity/Community.ts index d398cf584..26a989a6a 100644 --- a/database/entity/Community.ts +++ b/database/entity/Community.ts @@ -1 +1 @@ -export { Community } from './0068-community_tables_public_key_length/Community' +export { Community } from './0075-introduce_gms_registration/Community' diff --git a/database/entity/User.ts b/database/entity/User.ts index 21785ee9c..e7e42b78c 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0073-introduce_foreign_user_in_users_table/User' +export { User } from './0075-introduce_gms_registration/User' diff --git a/database/entity/UserContact.ts b/database/entity/UserContact.ts index 17d4575b0..97f9fada1 100644 --- a/database/entity/UserContact.ts +++ b/database/entity/UserContact.ts @@ -1 +1 @@ -export { UserContact } from './0057-clear_old_password_junk/UserContact' +export { UserContact } from './0075-introduce_gms_registration/UserContact' diff --git a/database/migrations/0075-introduce_gms_registration.ts b/database/migrations/0075-introduce_gms_registration.ts new file mode 100644 index 000000000..6de55333f --- /dev/null +++ b/database/migrations/0075-introduce_gms_registration.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `users` MODIFY COLUMN `foreign` tinyint(1) NOT NULL DEFAULT 0 AFTER `id`;', + ) + await queryFn( + 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `gms_publish_name` int unsigned NOT NULL DEFAULT 3 AFTER `last_name`;', // COMMENT '0:nothing, 1:initials only, 2:firstName only, 3:firstName + Initial of LastName, 4:fullName' + ) + await queryFn( + 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `gms_allowed` tinyint(1) NOT NULL DEFAULT 1;', + ) + await queryFn( + 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `gms_registered` tinyint(1) NOT NULL DEFAULT 0;', + ) + await queryFn( + 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `gms_registered_at` datetime(3) DEFAULT NULL NULL;', + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `gms_publish_email` tinyint(1) NOT NULL DEFAULT 0 AFTER `email_checked`;', // COMMENT '0:nothing, 1:email' + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `country_code` varchar(255) DEFAULT NULL NULL AFTER `gms_publish_email`;', + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `gms_publish_phone` int unsigned NOT NULL DEFAULT 0 AFTER `phone`;', // COMMENT '0:nothing, 1:country_code only, 2:complet phone number' + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `address` varchar(255) DEFAULT NULL NULL AFTER `gms_publish_phone`;', + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `city` varchar(255) DEFAULT NULL NULL AFTER `address`;', + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `zip_code` varchar(255) DEFAULT NULL NULL AFTER `city`;', + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `country` varchar(255) DEFAULT NULL NULL AFTER `zip_code`;', + ) + await queryFn( + 'ALTER TABLE `user_contacts` ADD COLUMN IF NOT EXISTS `gms_publish_post` int unsigned NOT NULL DEFAULT 0 AFTER `country`;', // COMMENT '0:nothing, 1:country, 2:city, 3: detailed address' + ) + await queryFn( + 'ALTER TABLE `communities` ADD COLUMN IF NOT EXISTS `gms_api_key` varchar(512) DEFAULT NULL NULL AFTER `description`;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `users` MODIFY COLUMN `foreign` tinyint(4) NOT NULL DEFAULT 0 AFTER `id`;', + ) + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `gms_publish_name`;') + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `gms_allowed`;') + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `gms_registered`;') + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `gms_registered_at`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `gms_publish_email`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `country_code`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `gms_publish_phone`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `address`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `city`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `zip_code`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `country`;') + await queryFn('ALTER TABLE `user_contacts` DROP COLUMN IF EXISTS `gms_publish_post`;') + await queryFn('ALTER TABLE `communities` DROP COLUMN IF EXISTS `gms_api_key`;') +} diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 7aed88ccd..21ff29000 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0074-insert_communityuuid in_existing_users', + DB_VERSION: '0075-introduce_gms_registration', LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info LOG_LEVEL: process.env.LOG_LEVEL || 'info', diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 2770ada06..8e0f6e083 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0074-insert_communityuuid in_existing_users', + DB_VERSION: '0075-introduce_gms_registration', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info