diff --git a/dht-node/src/dht_node/@types/@hyperswarm__dht/index.d.ts b/@types/@hyperswarm__dht/index.d.ts similarity index 100% rename from dht-node/src/dht_node/@types/@hyperswarm__dht/index.d.ts rename to @types/@hyperswarm__dht/index.d.ts diff --git a/backend/@types/klicktipp-api/index.d.ts b/@types/klicktipp-api/index.d.ts similarity index 100% rename from backend/@types/klicktipp-api/index.d.ts rename to @types/klicktipp-api/index.d.ts diff --git a/backend/@types/random-bigint/index.d.ts b/@types/random-bigint/index.d.ts similarity index 100% rename from backend/@types/random-bigint/index.d.ts rename to @types/random-bigint/index.d.ts diff --git a/backend/@types/sodium-native/index.d.ts b/@types/sodium-native/index.d.ts similarity index 100% rename from backend/@types/sodium-native/index.d.ts rename to @types/sodium-native/index.d.ts diff --git a/backend/src/graphql/enum/ContributionCycleType.ts b/backend/src/graphql/enum/ContributionCycleType.ts index a3c55aa68..050a6ac0d 100644 --- a/backend/src/graphql/enum/ContributionCycleType.ts +++ b/backend/src/graphql/enum/ContributionCycleType.ts @@ -1,27 +1,7 @@ import { registerEnumType } from 'type-graphql' +import { ContributionCycleType } from 'database' -// lowercase values are not implemented yet -export enum ContributionCycleType { - ONCE = 'ONCE', - HOUR = 'hour', - TWO_HOURS = 'two_hours', - FOUR_HOURS = 'four_hours', - EIGHT_HOURS = 'eight_hours', - HALF_DAY = 'half_day', - DAILY = 'DAILY', - TWO_DAYS = 'two_days', - THREE_DAYS = 'three_days', - FOUR_DAYS = 'four_days', - FIVE_DAYS = 'five_days', - SIX_DAYS = 'six_days', - WEEK = 'week', - TWO_WEEKS = 'two_weeks', - MONTH = 'month', - TWO_MONTH = 'two_month', - QUARTER = 'quarter', - HALF_YEAR = 'half_year', - YEAR = 'year', -} +export { ContributionCycleType } registerEnumType(ContributionCycleType, { name: 'ContributionCycleType', // this one is mandatory diff --git a/backend/src/graphql/enum/ContributionStatus.ts b/backend/src/graphql/enum/ContributionStatus.ts index 67cdf5398..69d2e1171 100644 --- a/backend/src/graphql/enum/ContributionStatus.ts +++ b/backend/src/graphql/enum/ContributionStatus.ts @@ -1,12 +1,7 @@ import { registerEnumType } from 'type-graphql' +import { ContributionStatus } from 'database' -export enum ContributionStatus { - PENDING = 'PENDING', - DELETED = 'DELETED', - IN_PROGRESS = 'IN_PROGRESS', - DENIED = 'DENIED', - CONFIRMED = 'CONFIRMED', -} +export { ContributionStatus } registerEnumType(ContributionStatus, { name: 'ContributionStatus', diff --git a/backend/src/graphql/enum/ContributionType.ts b/backend/src/graphql/enum/ContributionType.ts index e8529edc4..28fae333a 100644 --- a/backend/src/graphql/enum/ContributionType.ts +++ b/backend/src/graphql/enum/ContributionType.ts @@ -1,10 +1,7 @@ import { registerEnumType } from 'type-graphql' +import { ContributionType } from 'database' -export enum ContributionType { - ADMIN = 'ADMIN', - USER = 'USER', - LINK = 'LINK', -} +export { ContributionType } registerEnumType(ContributionType, { name: 'ContributionType', diff --git a/backend/src/graphql/enum/PendingTransactionState.ts b/backend/src/graphql/enum/PendingTransactionState.ts index e59f3fd7d..cf1a17291 100644 --- a/backend/src/graphql/enum/PendingTransactionState.ts +++ b/backend/src/graphql/enum/PendingTransactionState.ts @@ -1,6 +1,8 @@ import { registerEnumType } from 'type-graphql' import { PendingTransactionState } from 'shared' +export { PendingTransactionState } + registerEnumType(PendingTransactionState, { name: 'PendingTransactionState', // this one is mandatory description: 'State of the PendingTransaction', // this one is optional diff --git a/backend/src/graphql/enum/RoleNames.ts b/backend/src/graphql/enum/RoleNames.ts index 8ba8e1441..872b4937f 100644 --- a/backend/src/graphql/enum/RoleNames.ts +++ b/backend/src/graphql/enum/RoleNames.ts @@ -1,13 +1,7 @@ import { registerEnumType } from 'type-graphql' +import { RoleNames } from 'database' -export enum RoleNames { - UNAUTHORIZED = 'UNAUTHORIZED', - USER = 'USER', - MODERATOR = 'MODERATOR', - MODERATOR_AI = 'MODERATOR_AI', - ADMIN = 'ADMIN', - DLT_CONNECTOR = 'DLT_CONNECTOR_ROLE', -} +export { RoleNames } registerEnumType(RoleNames, { name: 'RoleNames', // this one is mandatory diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 31d405942..399dc7e82 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2139,14 +2139,6 @@ describe('ContributionResolver', () => { }) }) - it('stores the EMAIL_CONFIRMATION event in the database', async () => { - await expect(DbEvent.find()).resolves.toContainEqual( - expect.objectContaining({ - type: EventType.EMAIL_CONFIRMATION, - }), - ) - }) - describe('confirm same contribution again', () => { it('throws an error', async () => { jest.clearAllMocks() diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 0fd06c0d1..e19eff8c5 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -2256,7 +2256,7 @@ describe('UserResolver', () => { relations: ['user'], }) const activationLink = `${ - CONFIG.EMAIL_LINK_VERIFICATION + CONFIG.EMAIL_LINK_SETPASSWORD }${userContact.emailVerificationCode.toString()}` expect(sendAccountActivationEmail).toBeCalledWith({ firstName: 'Bibi', diff --git a/backend/src/seeds/contributionLink/ContributionLinkInterface.ts b/backend/src/seeds/contributionLink/ContributionLinkInterface.ts index 15ba4b72d..ce0a60695 100644 --- a/backend/src/seeds/contributionLink/ContributionLinkInterface.ts +++ b/backend/src/seeds/contributionLink/ContributionLinkInterface.ts @@ -1,3 +1,6 @@ +export { ContributionLinkInterface } from 'database' + +/* export interface ContributionLinkInterface { amount: number name: string @@ -5,3 +8,4 @@ export interface ContributionLinkInterface { validFrom?: Date validTo?: Date } +*/ diff --git a/backend/src/seeds/contributionLink/index.ts b/backend/src/seeds/contributionLink/index.ts index 41d28eb60..5be34e171 100644 --- a/backend/src/seeds/contributionLink/index.ts +++ b/backend/src/seeds/contributionLink/index.ts @@ -1,5 +1,6 @@ -import { ContributionLinkInterface } from './ContributionLinkInterface' +export { contributionLinks } from 'database' +/* export const contributionLinks: ContributionLinkInterface[] = [ { name: 'Dokumenta 2017', @@ -16,3 +17,4 @@ export const contributionLinks: ContributionLinkInterface[] = [ validTo: new Date(2022, 8, 25), }, ] +*/ \ No newline at end of file diff --git a/backend/src/seeds/creation/CreationInterface.ts b/backend/src/seeds/creation/CreationInterface.ts index ee450bd93..a05276a3d 100644 --- a/backend/src/seeds/creation/CreationInterface.ts +++ b/backend/src/seeds/creation/CreationInterface.ts @@ -1,3 +1,5 @@ +export { CreationInterface } from 'database' +/* export interface CreationInterface { email: string amount: number @@ -7,3 +9,4 @@ export interface CreationInterface { // number of months to move the confirmed creation to the past moveCreationDate?: number } +*/ \ No newline at end of file diff --git a/backend/src/seeds/creation/index.ts b/backend/src/seeds/creation/index.ts index 1499f663b..f95bc6fa4 100644 --- a/backend/src/seeds/creation/index.ts +++ b/backend/src/seeds/creation/index.ts @@ -1,3 +1,7 @@ + +export { creations } from 'database' + +/* import { nMonthsBefore } from '@/seeds/factory/creation' import { CreationInterface } from './CreationInterface' @@ -153,3 +157,4 @@ export const creations: CreationInterface[] = [ confirmed: true, }, ] +*/ \ No newline at end of file diff --git a/backend/src/seeds/factory/contributionLink.ts b/backend/src/seeds/factory/contributionLink.ts index 4e8c945f3..e29d7a635 100644 --- a/backend/src/seeds/factory/contributionLink.ts +++ b/backend/src/seeds/factory/contributionLink.ts @@ -1,32 +1,15 @@ -import { ApolloServerTestClient } from 'apollo-server-testing' +import { + contributionLinkFactory as contributionLinkFactoryDb, + ContributionLinkInterface +} from 'database' import { ContributionLink } from '@model/ContributionLink' -import { ContributionLinkInterface } from '@/seeds/contributionLink/ContributionLinkInterface' -import { createContributionLink, login } from '@/seeds/graphql/mutations' +export { ContributionLinkInterface } -export const contributionLinkFactory = async ( - client: ApolloServerTestClient, +export async function contributionLinkFactory ( + _client: any, contributionLink: ContributionLinkInterface, -): Promise => { - const { mutate } = client - - // login as admin - await mutate({ - mutation: login, - variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, - }) - const variables = { - amount: contributionLink.amount, - memo: contributionLink.memo, - name: contributionLink.name, - cycle: 'ONCE', - maxPerCycle: 1, - maxAmountPerMonth: 200, - validFrom: contributionLink.validFrom ? contributionLink.validFrom.toISOString() : undefined, - validTo: contributionLink.validTo ? contributionLink.validTo.toISOString() : undefined, - } - - const result = await mutate({ mutation: createContributionLink, variables }) - return result.data.createContributionLink +): Promise { + return new ContributionLink(await contributionLinkFactoryDb(contributionLink)) } diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 9ce36bfa9..2d57dc782 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -1,58 +1,15 @@ -import { ApolloServerTestClient } from 'apollo-server-testing' -import { Contribution, Transaction } from 'database' +import { + Contribution, + creationFactory as creationFactoryDb, + CreationInterface, + nMonthsBefore +} from 'database' -import { findUserByEmail } from '@/graphql/resolver/UserResolver' -import { CreationInterface } from '@/seeds/creation/CreationInterface' -import { confirmContribution, createContribution, login } from '@/seeds/graphql/mutations' - -export const nMonthsBefore = (date: Date, months = 1): string => { - return new Date(date.getFullYear(), date.getMonth() - months, 1).toISOString() -} +export { CreationInterface, nMonthsBefore } export const creationFactory = async ( - client: ApolloServerTestClient, + _client: any, creation: CreationInterface, ): Promise => { - const { mutate } = client - await mutate({ - mutation: login, - variables: { email: creation.email, password: 'Aa12345_' }, - }) - const { - data: { createContribution: contribution }, - } = await mutate({ mutation: createContribution, variables: { ...creation } }) - - if (creation.confirmed) { - const user = await findUserByEmail(creation.email) // userContact.user - - await mutate({ mutation: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) - await mutate({ mutation: confirmContribution, variables: { id: contribution.id } }) - const confirmedContribution = await Contribution.findOneOrFail({ - where: { id: contribution.id }, - }) - - if (creation.moveCreationDate) { - const transaction = await Transaction.findOneOrFail({ - where: { userId: user.id, creationDate: new Date(creation.contributionDate) }, - order: { balanceDate: 'DESC' }, - }) - - if (transaction.decay.equals(0) && transaction.creationDate) { - confirmedContribution.contributionDate = new Date( - nMonthsBefore(transaction.creationDate, creation.moveCreationDate), - ) - transaction.creationDate = new Date( - nMonthsBefore(transaction.creationDate, creation.moveCreationDate), - ) - transaction.balanceDate = new Date( - nMonthsBefore(transaction.balanceDate, creation.moveCreationDate), - ) - await transaction.save() - await confirmedContribution.save() - } - } - return confirmedContribution - } else { - return contribution - } + return creationFactoryDb(creation) } diff --git a/backend/src/seeds/factory/transactionLink.ts b/backend/src/seeds/factory/transactionLink.ts index 80384025f..02911bd32 100644 --- a/backend/src/seeds/factory/transactionLink.ts +++ b/backend/src/seeds/factory/transactionLink.ts @@ -1,46 +1,13 @@ -import { ApolloServerTestClient } from 'apollo-server-testing' -import { TransactionLink } from 'database' +import { + transactionLinkFactory as transactionLinkFactoryDb, + TransactionLinkInterface +} from 'database' -import { transactionLinkExpireDate } from '@/graphql/resolver/TransactionLinkResolver' -import { createTransactionLink, login } from '@/seeds/graphql/mutations' -import { TransactionLinkInterface } from '@/seeds/transactionLink/TransactionLinkInterface' +export { TransactionLinkInterface } -export const transactionLinkFactory = async ( - client: ApolloServerTestClient, +export async function transactionLinkFactory ( + _client: any, transactionLink: TransactionLinkInterface, -): Promise => { - const { mutate } = client - - // login - await mutate({ - mutation: login, - variables: { email: transactionLink.email, password: 'Aa12345_' }, - }) - - const variables = { - amount: transactionLink.amount, - memo: transactionLink.memo, - } - - // get the transaction links's id - const { - data: { - createTransactionLink: { id }, - }, - } = await mutate({ mutation: createTransactionLink, variables }) - - if (transactionLink.createdAt || transactionLink.deletedAt) { - const dbTransactionLink = await TransactionLink.findOneOrFail({ where: { id } }) - - if (transactionLink.createdAt) { - dbTransactionLink.createdAt = transactionLink.createdAt - dbTransactionLink.validUntil = transactionLinkExpireDate(transactionLink.createdAt) - await dbTransactionLink.save() - } - - if (transactionLink.deletedAt) { - dbTransactionLink.deletedAt = new Date(dbTransactionLink.createdAt.getTime() + 1000) - await dbTransactionLink.save() - } - } +): Promise { + await transactionLinkFactoryDb(transactionLink) } diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts index 36d9adfeb..5308518cb 100644 --- a/backend/src/seeds/factory/user.ts +++ b/backend/src/seeds/factory/user.ts @@ -1,77 +1,35 @@ -import { ApolloServerTestClient } from 'apollo-server-testing' -import { User } from 'database' +import { User, userFactory as userFactoryDb, userFactoryBulk as userFactoryBulkDb, Community } from 'database' -import { RoleNames } from '@enum/RoleNames' - -import { setUserRole } from '@/graphql/resolver/util/modifyUserRole' import { writeHomeCommunityEntry } from '@/seeds/community' -import { createUser, setPassword } from '@/seeds/graphql/mutations' import { UserInterface } from '@/seeds/users/UserInterface' +import { encryptPassword } from '@/password/PasswordEncryptor' export const userFactory = async ( - client: ApolloServerTestClient, + _client: any, user: UserInterface, ): Promise => { - const { mutate } = client - const homeCom = await writeHomeCommunityEntry() - // console.log('call createUser with', JSON.stringify(user, null, 2)) - const response = await mutate({ mutation: createUser, variables: user }) - if (!response?.data?.createUser) { - // console.log(JSON.stringify(response, null, 2)) - throw new Error('createUser mutation returned unexpected response') - } - const { - data: { - createUser: { id }, - }, - } = response - // get user from database - let dbUser = await User.findOneOrFail({ where: { id }, relations: ['emailContact', 'userRoles'] }) - - const emailContact = dbUser.emailContact + const dbUser = await userFactoryDb(user, homeCom) if (user.emailChecked) { - await mutate({ - mutation: setPassword, - variables: { password: 'Aa12345_', code: emailContact.emailVerificationCode }, - }) - } - - // get last changes of user from database - dbUser = await User.findOneOrFail({ where: { id }, relations: { userRoles: true, emailContact: true } }) - - if (user.createdAt || user.deletedAt || user.role) { - if (user.createdAt) { - dbUser.createdAt = user.createdAt - // make sure emailContact is also updated for e2e test, prevent failing when time between seeding and test run is < 1 minute - dbUser.emailContact.createdAt = user.createdAt - dbUser.emailContact.updatedAt = user.createdAt - await dbUser.emailContact.save() - } - if (user.deletedAt) { - dbUser.deletedAt = user.deletedAt - } - const userRole = user.role as RoleNames - if (userRole && (userRole === RoleNames.ADMIN || userRole === RoleNames.MODERATOR)) { - await setUserRole(dbUser, user.role) - } + const passwortHash = await encryptPassword(dbUser, 'Aa12345_') + dbUser.password = passwortHash await dbUser.save() } - try { - if (homeCom.communityUuid) { - dbUser.communityUuid = homeCom.communityUuid - await User.save(dbUser) - } - } catch (_err) { - // no homeCommunity exists - } - - // get last changes of user from database - dbUser = await User.findOneOrFail({ - where: { id }, - withDeleted: true, - relations: ['emailContact', 'userRoles'], - }) return dbUser } + +export async function userFactoryBulk(users: UserInterface[], homeCommunity?: Community | null) { + if (!homeCommunity) { + homeCommunity = await writeHomeCommunityEntry() + } + const dbUsers = await userFactoryBulkDb(users, homeCommunity) + for (const dbUser of dbUsers) { + if (dbUser.emailContact.emailChecked) { + const passwortHash = await encryptPassword(dbUser, 'Aa12345_') + dbUser.password = passwortHash + await dbUser.save() + } + } + return dbUsers +} \ No newline at end of file diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index 3a1504262..4cc352ec7 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -1,10 +1,13 @@ -import { createTestClient } from 'apollo-server-testing' -import { entities } from 'database' -import { datatype, internet, name } from 'faker' +import { + AppDatabase, + User, + UserInterface, + creationFactoryBulk, + transactionLinkFactoryBulk +} from 'database' +import { internet, name } from 'faker' import { CONFIG } from '@/config' -import { CONFIG as CORE_CONFIG } from 'core' -import { createServer } from '@/server/createServer' import { initLogging } from '@/server/logger' import { getLogger } from 'log4js' @@ -12,95 +15,87 @@ import { writeHomeCommunityEntry } from './community' import { contributionLinks } from './contributionLink/index' import { creations } from './creation/index' import { contributionLinkFactory } from './factory/contributionLink' -import { creationFactory } from './factory/creation' -import { transactionLinkFactory } from './factory/transactionLink' -import { userFactory } from './factory/user' +import { userFactoryBulk } from './factory/user' import { transactionLinks } from './transactionLink/index' import { users } from './users/index' -CORE_CONFIG.EMAIL = false +const RANDOM_USER_COUNT = 100 const logger = getLogger('seed') -const context = { - token: '', - setHeaders: { - push: (value: { key: string; value: string }): void => { - context.token = value.value - }, - - forEach: (): void => { - // do nothing - }, - }, - clientTimezoneOffset: 0, -} - -export const cleanDB = async () => { - // this only works as long we do not have foreign key constraints - for (const entity of entities) { - if (entity.name !== 'Migration') { - 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 () => { initLogging() - const server = await createServer(getLogger('apollo'), context) - const seedClient = createTestClient(server.apollo) - const { con } = server - await cleanDB() - logger.info('##seed## clean database successful...') + const db = AppDatabase.getInstance() + await db.init() + await clearDatabase(db) + logger.info('clean database successful...') logger.info(`crypto worker enabled: ${CONFIG.USE_CRYPTO_WORKER}`) // seed home community - await writeHomeCommunityEntry() + const homeCommunity = await writeHomeCommunityEntry() // seed the standard users - for (const user of users) { - await userFactory(seedClient, user) + // put into map for later direct access + const userCreationIndexedByEmail = new Map() + const defaultUsers = await userFactoryBulk(users, homeCommunity) + for (const dbUser of defaultUsers) { + userCreationIndexedByEmail.set(dbUser.emailContact.email, dbUser) } - logger.info('##seed## seeding all standard users successful...') + logger.info('seeding all standard users successful...') // seed 100 random users - for (let i = 0; i < 100; i++) { - await userFactory(seedClient, { + const randomUsers = new Array(RANDOM_USER_COUNT) + for (let i = 0; i < RANDOM_USER_COUNT; i++) { + randomUsers[i] = { firstName: name.firstName(), lastName: name.lastName(), email: internet.email(), - language: datatype.boolean() ? 'en' : 'de', - }) - logger.info(`##seed## seed ${i}. random user`) + language: Math.random() < 0.5 ? 'en' : 'de', + } } - logger.info('##seed## seeding all random users successful...') + await userFactoryBulk(randomUsers, homeCommunity) + logger.info('seeding all random users successful...') // create GDD - for (const creation of creations) { - await creationFactory(seedClient, creation) - } - logger.info('##seed## seeding all creations successful...') + const moderatorUser = userCreationIndexedByEmail.get('peter@lustig.de')! + await creationFactoryBulk(creations, userCreationIndexedByEmail, moderatorUser) + logger.info('seeding all creations successful...') // create Transaction Links - for (const transactionLink of transactionLinks) { - await transactionLinkFactory(seedClient, transactionLink) - } - logger.info('##seed## seeding all transactionLinks successful...') + const movedTransactionLinks = transactionLinks.map(transactionLink => { + let createdAt = new Date(new Date().getTime() + 1000) + if (transactionLink.createdAt) { + createdAt = transactionLink.createdAt + } + return { + ...transactionLink, + createdAt: createdAt, + } + }) + await transactionLinkFactoryBulk(movedTransactionLinks, userCreationIndexedByEmail) + logger.info('seeding all transactionLinks successful...') // create Contribution Links for (const contributionLink of contributionLinks) { - await contributionLinkFactory(seedClient, contributionLink) + await contributionLinkFactory(null, contributionLink) } - logger.info('##seed## seeding all contributionLinks successful...') + logger.info('seeding all contributionLinks successful...') - await con.destroy() + await db.destroy() +} + +async function clearDatabase(db: AppDatabase) { + await db.getDataSource().transaction(async trx => { + await trx.query(`SET FOREIGN_KEY_CHECKS = 0`) + await trx.query(`TRUNCATE TABLE contributions`) + await trx.query(`TRUNCATE TABLE contribution_links`) + await trx.query(`TRUNCATE TABLE users`) + await trx.query(`TRUNCATE TABLE user_contacts`) + await trx.query(`TRUNCATE TABLE user_roles`) + await trx.query(`TRUNCATE TABLE transactions`) + await trx.query(`TRUNCATE TABLE transaction_links`) + await trx.query(`TRUNCATE TABLE communities`) + await trx.query(`SET FOREIGN_KEY_CHECKS = 1`) + }) } run().catch((err) => { diff --git a/backend/src/seeds/transactionLink/TransactionLinkInterface.ts b/backend/src/seeds/transactionLink/TransactionLinkInterface.ts index eaacfdf92..4b2e57fa3 100644 --- a/backend/src/seeds/transactionLink/TransactionLinkInterface.ts +++ b/backend/src/seeds/transactionLink/TransactionLinkInterface.ts @@ -1,3 +1,5 @@ +export { TransactionLinkInterface } from 'database' +/* export interface TransactionLinkInterface { email: string amount: number @@ -8,3 +10,4 @@ export interface TransactionLinkInterface { // redeemedBy?: number deletedAt?: boolean } +*/ \ No newline at end of file diff --git a/backend/src/seeds/transactionLink/index.ts b/backend/src/seeds/transactionLink/index.ts index 17683b580..c5d46b146 100644 --- a/backend/src/seeds/transactionLink/index.ts +++ b/backend/src/seeds/transactionLink/index.ts @@ -1,3 +1,5 @@ +export { transactionLinks } from 'database' +/* import { TransactionLinkInterface } from './TransactionLinkInterface' export const transactionLinks: TransactionLinkInterface[] = [ @@ -53,3 +55,4 @@ bei Gradidio sei dabei!`, deletedAt: true, }, ] +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/UserInterface.ts b/backend/src/seeds/users/UserInterface.ts index 1fb1b128a..abbecb254 100644 --- a/backend/src/seeds/users/UserInterface.ts +++ b/backend/src/seeds/users/UserInterface.ts @@ -1,3 +1,5 @@ +export { UserInterface } from 'database' +/* export interface UserInterface { alias?: string email?: string @@ -11,3 +13,4 @@ export interface UserInterface { publisherId?: number role?: string } +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/bibi-bloxberg.ts b/backend/src/seeds/users/bibi-bloxberg.ts index f78a3af1c..dda6b1525 100644 --- a/backend/src/seeds/users/bibi-bloxberg.ts +++ b/backend/src/seeds/users/bibi-bloxberg.ts @@ -1,3 +1,5 @@ +export { bibiBloxberg } from 'database' +/* import { UserInterface } from './UserInterface' export const bibiBloxberg: UserInterface = { @@ -12,3 +14,4 @@ export const bibiBloxberg: UserInterface = { // move user createdAt before transaction link createdAt: new Date(2021, 9, 17), } +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/bob-baumeister.ts b/backend/src/seeds/users/bob-baumeister.ts index b7902f7bc..61df64759 100644 --- a/backend/src/seeds/users/bob-baumeister.ts +++ b/backend/src/seeds/users/bob-baumeister.ts @@ -1,3 +1,5 @@ +export { bobBaumeister } from 'database' +/* import { UserInterface } from './UserInterface' export const bobBaumeister: UserInterface = { @@ -9,3 +11,4 @@ export const bobBaumeister: UserInterface = { emailChecked: true, language: 'de', } +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/garrick-ollivander.ts b/backend/src/seeds/users/garrick-ollivander.ts index 264a866bd..332f06157 100644 --- a/backend/src/seeds/users/garrick-ollivander.ts +++ b/backend/src/seeds/users/garrick-ollivander.ts @@ -1,3 +1,6 @@ + +export { garrickOllivander } from 'database' +/* import { UserInterface } from './UserInterface' export const garrickOllivander: UserInterface = { @@ -10,3 +13,4 @@ export const garrickOllivander: UserInterface = { emailChecked: false, language: 'en', } +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/peter-lustig.ts b/backend/src/seeds/users/peter-lustig.ts index ff1fe065e..6c86ef800 100644 --- a/backend/src/seeds/users/peter-lustig.ts +++ b/backend/src/seeds/users/peter-lustig.ts @@ -1,3 +1,5 @@ +export { peterLustig } from 'database' +/* import { RoleNames } from '@enum/RoleNames' import { UserInterface } from './UserInterface' @@ -12,3 +14,4 @@ export const peterLustig: UserInterface = { language: 'de', role: RoleNames.ADMIN, } +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/raeuber-hotzenplotz.ts b/backend/src/seeds/users/raeuber-hotzenplotz.ts index 62601efff..dc52f10b4 100644 --- a/backend/src/seeds/users/raeuber-hotzenplotz.ts +++ b/backend/src/seeds/users/raeuber-hotzenplotz.ts @@ -1,3 +1,6 @@ + +export { raeuberHotzenplotz } from 'database' +/* import { UserInterface } from './UserInterface' export const raeuberHotzenplotz: UserInterface = { @@ -8,3 +11,4 @@ export const raeuberHotzenplotz: UserInterface = { emailChecked: true, language: 'de', } +*/ \ No newline at end of file diff --git a/backend/src/seeds/users/stephen-hawking.ts b/backend/src/seeds/users/stephen-hawking.ts index a683b7579..a9a2085e6 100644 --- a/backend/src/seeds/users/stephen-hawking.ts +++ b/backend/src/seeds/users/stephen-hawking.ts @@ -1,3 +1,5 @@ +export { stephenHawking } from 'database' +/* import { UserInterface } from './UserInterface' export const stephenHawking: UserInterface = { @@ -10,3 +12,4 @@ export const stephenHawking: UserInterface = { deletedAt: new Date('2018-03-14T09:17:52'), language: 'en', } +*/ \ No newline at end of file diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 454d4853c..4cd3fc557 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -61,7 +61,7 @@ }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": [ /* List of folders to include type definitions from. */ - "@types", + "../@types", "node_modules/@types", "../node_modules/@types", ], diff --git a/bun.lock b/bun.lock index 6dd175b61..fa8cc8486 100644 --- a/bun.lock +++ b/bun.lock @@ -245,7 +245,7 @@ "@types/mysql": "^2.15.27", "@types/node": "^18.7.14", "await-semaphore": "^0.1.3", - "crypto-random-bigint": "^2.1.1", + "random-bigint": "^0.0.1", "ts-node": "^10.9.2", "typescript": "^4.9.5", }, @@ -634,6 +634,8 @@ "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.10.1", "", {}, "sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg=="], + "@cacheable/memoize": ["@cacheable/memoize@2.0.3", "", { "dependencies": { "@cacheable/utils": "^2.0.3" } }, "sha512-hl9wfQgpiydhQEIv7fkjEzTGE+tcosCXLKFDO707wYJ/78FVOlowb36djex5GdbSyeHnG62pomYLMuV/OT8Pbw=="], "@cacheable/memory": ["@cacheable/memory@2.0.3", "", { "dependencies": { "@cacheable/memoize": "^2.0.3", "@cacheable/utils": "^2.0.3", "@keyv/bigmap": "^1.0.2", "hookified": "^1.12.1", "keyv": "^5.5.3" } }, "sha512-R3UKy/CKOyb1LZG/VRCTMcpiMDyLH7SH3JrraRdK6kf3GweWCOU3sgvE13W3TiDRbxnDKylzKJvhUAvWl9LQOA=="], @@ -1628,6 +1630,8 @@ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "buffer-builder": ["buffer-builder@0.2.0", "", {}, "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg=="], + "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], @@ -1712,6 +1716,8 @@ "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + "colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="], + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -1768,8 +1774,6 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "crypto-random-bigint": ["crypto-random-bigint@2.1.1", "", { "dependencies": { "uint-rng": "^1.2.1" } }, "sha512-96+FDrenXybkpnLML/60S8NcG32KgJ5Y8yvNNCYPW02r+ssoXFR5XKenuIQcHLWumnGj8UPqUUHBzXNrDGkDmQ=="], - "css-functions-list": ["css-functions-list@3.2.3", "", {}, "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA=="], "css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="], @@ -3070,7 +3074,45 @@ "safety-catch": ["safety-catch@1.0.2", "", {}, "sha512-C1UYVZ4dtbBxEtvOcpjBaaD27nP8MlvyAQEp2fOTOEe6pfUpk1cDUxij6BR1jZup6rSyUTaBBplK7LanskrULA=="], - "sass": ["sass@1.93.2", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg=="], + "sass": ["sass@1.93.3", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg=="], + + "sass-embedded": ["sass-embedded@1.93.3", "", { "dependencies": { "@bufbuild/protobuf": "^2.5.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-all-unknown": "1.93.3", "sass-embedded-android-arm": "1.93.3", "sass-embedded-android-arm64": "1.93.3", "sass-embedded-android-riscv64": "1.93.3", "sass-embedded-android-x64": "1.93.3", "sass-embedded-darwin-arm64": "1.93.3", "sass-embedded-darwin-x64": "1.93.3", "sass-embedded-linux-arm": "1.93.3", "sass-embedded-linux-arm64": "1.93.3", "sass-embedded-linux-musl-arm": "1.93.3", "sass-embedded-linux-musl-arm64": "1.93.3", "sass-embedded-linux-musl-riscv64": "1.93.3", "sass-embedded-linux-musl-x64": "1.93.3", "sass-embedded-linux-riscv64": "1.93.3", "sass-embedded-linux-x64": "1.93.3", "sass-embedded-unknown-all": "1.93.3", "sass-embedded-win32-arm64": "1.93.3", "sass-embedded-win32-x64": "1.93.3" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw=="], + + "sass-embedded-all-unknown": ["sass-embedded-all-unknown@1.93.3", "", { "dependencies": { "sass": "1.93.3" }, "cpu": [ "!arm", "!x64", "!arm64", ] }, "sha512-3okGgnE41eg+CPLtAPletu6nQ4N0ij7AeW+Sl5Km4j29XcmqZQeFwYjHe1AlKTEgLi/UAONk1O8i8/lupeKMbw=="], + + "sass-embedded-android-arm": ["sass-embedded-android-arm@1.93.3", "", { "os": "android", "cpu": "arm" }, "sha512-8xOw9bywfOD6Wv24BgCmgjkk6tMrsOTTHcb28KDxeJtFtoxiUyMbxo0vChpPAfp2Hyg2tFFKS60s0s4JYk+Raw=="], + + "sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.93.3", "", { "os": "android", "cpu": "arm64" }, "sha512-uqUl3Kt1IqdGVAcAdbmC+NwuUJy8tM+2ZnB7/zrt6WxWVShVCRdFnWR9LT8HJr7eJN7AU8kSXxaVX/gedanPsg=="], + + "sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.93.3", "", { "os": "android", "cpu": "none" }, "sha512-2jNJDmo+3qLocjWqYbXiBDnfgwrUeZgZFHJIwAefU7Fn66Ot7rsXl+XPwlokaCbTpj7eMFIqsRAZ/uDueXNCJg=="], + + "sass-embedded-android-x64": ["sass-embedded-android-x64@1.93.3", "", { "os": "android", "cpu": "x64" }, "sha512-y0RoAU6ZenQFcjM9PjQd3cRqRTjqwSbtWLL/p68y2oFyh0QGN0+LQ826fc0ZvU/AbqCsAizkqjzOn6cRZJxTTQ=="], + + "sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.93.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7zb/hpdMOdKteK17BOyyypemglVURd1Hdz6QGsggy60aUFfptTLQftLRg8r/xh1RbQAUKWFbYTNaM47J9yPxYg=="], + + "sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.93.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ek1Vp8ZDQEe327Lz0b7h3hjvWH3u9XjJiQzveq74RPpJQ2q6d9LfWpjiRRohM4qK6o4XOHw1X10OMWPXJtdtWg=="], + + "sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.93.3", "", { "os": "linux", "cpu": "arm" }, "sha512-yeiv2y+dp8B4wNpd3+JsHYD0mvpXSfov7IGyQ1tMIR40qv+ROkRqYiqQvAOXf76Qwh4Y9OaYZtLpnsPjfeq6mA=="], + + "sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.93.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RBrHWgfd8Dd8w4fbmdRVXRrhh8oBAPyeWDTKAWw8ZEmuXfVl4ytjDuyxaVilh6rR1xTRTNpbaA/YWApBlLrrNw=="], + + "sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.93.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fU0fwAwbp7sBE3h5DVU5UPzvaLg7a4yONfFWkkcCp6ZrOiPuGRHXXYriWQ0TUnWy4wE+svsVuWhwWgvlb/tkKg=="], + + "sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.93.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-PS829l+eUng+9W4PFclXGb4uA2+965NHV3/Sa5U7qTywjeeUUYTZg70dJHSqvhrBEfCc2XJABeW3adLJbyQYkw=="], + + "sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.93.3", "", { "os": "linux", "cpu": "none" }, "sha512-cK1oBY+FWQquaIGEeQ5H74KTO8cWsSWwXb/WaildOO9U6wmUypTgUYKQ0o5o/29nZbWWlM1PHuwVYTSnT23Jjg=="], + + "sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.93.3", "", { "os": "linux", "cpu": "x64" }, "sha512-A7wkrsHu2/I4Zpa0NMuPGkWDVV7QGGytxGyUq3opSXgAexHo/vBPlGoDXoRlSdex0cV+aTMRPjoGIfdmNlHwyg=="], + + "sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.93.3", "", { "os": "linux", "cpu": "none" }, "sha512-vWkW1+HTF5qcaHa6hO80gx/QfB6GGjJUP0xLbnAoY4pwEnw5ulGv6RM8qYr8IDhWfVt/KH+lhJ2ZFxnJareisQ=="], + + "sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.93.3", "", { "os": "linux", "cpu": "x64" }, "sha512-k6uFxs+e5jSuk1Y0niCwuq42F9ZC5UEP7P+RIOurIm8w/5QFa0+YqeW+BPWEW5M1FqVOsNZH3qGn4ahqvAEjPA=="], + + "sass-embedded-unknown-all": ["sass-embedded-unknown-all@1.93.3", "", { "dependencies": { "sass": "1.93.3" }, "os": [ "!linux", "!win32", "!darwin", "!android", ] }, "sha512-o5wj2rLpXH0C+GJKt/VpWp6AnMsCCbfFmnMAttcrsa+U3yrs/guhZ3x55KAqqUsE8F47e3frbsDL+1OuQM5DAA=="], + + "sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.93.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-0dOfT9moy9YmBolodwYYXtLwNr4jL4HQC9rBfv6mVrD7ud8ue2kDbn+GVzj1hEJxvEexVSmDCf7MHUTLcGs9xQ=="], + + "sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.93.3", "", { "os": "win32", "cpu": "x64" }, "sha512-wHFVfxiS9hU/sNk7KReD+lJWRp3R0SLQEX4zfOnRP2zlvI2X4IQR5aZr9GNcuMP6TmNpX0nQPZTegS8+h9RrEg=="], "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], @@ -3246,6 +3288,10 @@ "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + "sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="], + + "sync-message-port": ["sync-message-port@1.1.3", "", {}, "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg=="], + "synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="], "table": ["table@6.9.0", "", { "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A=="], @@ -3278,8 +3324,6 @@ "tiny-case": ["tiny-case@1.0.3", "", {}, "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="], - "tiny-webcrypto": ["tiny-webcrypto@1.0.3", "", {}, "sha512-LQQdNMAgz9BXNT2SKbYh3eCb+fLV0p7JB7MwUjzY6IOlQLGIadfnFqRpshERsS5Dl2OM/hs0+4I/XmSrF+RBbw=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], "tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], @@ -3392,8 +3436,6 @@ "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], - "uint-rng": ["uint-rng@1.2.1", "", { "dependencies": { "tiny-webcrypto": "^1.0.2" } }, "sha512-swhDg5H+3DX2sIvnYA7VMBMXV/t8mPxvh49CjCDkwFmj/3OZIDOQwJANBgM1MPSUBrUHNIlXmU7/GcL7m4907g=="], - "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], @@ -3450,6 +3492,8 @@ "validator": ["validator@13.15.15", "", {}, "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A=="], + "varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], "vee-validate": ["vee-validate@4.15.1", "", { "dependencies": { "@vue/devtools-api": "^7.5.2", "type-fest": "^4.8.3" }, "peerDependencies": { "vue": "^3.4.26" } }, "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg=="], @@ -4112,6 +4156,8 @@ "safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "sass-embedded/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "schema-utils/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "seek-bzip/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], diff --git a/core/src/graphql/enum/TransactionTypeId.ts b/core/src/graphql/enum/TransactionTypeId.ts index a7e39eebc..c6c188c5f 100644 --- a/core/src/graphql/enum/TransactionTypeId.ts +++ b/core/src/graphql/enum/TransactionTypeId.ts @@ -1,13 +1,6 @@ import { registerEnumType } from 'type-graphql' - -export enum TransactionTypeId { - CREATION = 1, - SEND = 2, - RECEIVE = 3, - // This is a virtual property, never occurring on the database - DECAY = 4, - LINK_SUMMARY = 5, -} +import { TransactionTypeId } from 'database' +export { TransactionTypeId } registerEnumType(TransactionTypeId, { name: 'TransactionTypeId', // this one is mandatory diff --git a/core/src/util/utilities.ts b/core/src/util/utilities.ts index be9e3d898..cfec3d37a 100644 --- a/core/src/util/utilities.ts +++ b/core/src/util/utilities.ts @@ -2,6 +2,7 @@ import { promisify } from 'util' import { Decimal } from 'decimal.js-light' import { i18n } from '../locales/localization' +export { fullName } from 'shared' export const objectValuesToArray = (obj: Record): string[] => Object.keys(obj).map((key) => obj[key]) @@ -14,11 +15,7 @@ export const decimalSeparatorByLanguage = (a: Decimal, language: string): string return result } -export const fullName = (firstName: string, lastName: string): string => - [firstName, lastName].filter(Boolean).join(' ') - // Function to reset an interface by chatGPT - export function resetInterface>(obj: T): T { // Iterate over all properties of the object for (const key in obj) { diff --git a/core/tsconfig.json b/core/tsconfig.json index 155a31a7b..711c21cc9 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -49,8 +49,12 @@ // "paths": { }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ /* List of folders to include type definitions from. */ - "typeRoots": ["./node_modules/@types", "../node_modules/@types"], - // "types": ["bun-types"], /* Type declaration files to be included in compilation. */ + "typeRoots": [ + "./node_modules/@types", + "../node_modules/@types", + "../@types" + ], + // "types": ["bun-types", "../@types"], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/database/package.json b/database/package.json index b22f9d95f..03ba57869 100644 --- a/database/package.json +++ b/database/package.json @@ -38,7 +38,7 @@ "@types/mysql": "^2.15.27", "@types/node": "^18.7.14", "await-semaphore": "^0.1.3", - "crypto-random-bigint": "^2.1.1", + "random-bigint": "^0.0.1", "ts-node": "^10.9.2", "typescript": "^4.9.5" }, diff --git a/database/src/enum/ContributionCycleType.ts b/database/src/enum/ContributionCycleType.ts new file mode 100644 index 000000000..6974cb563 --- /dev/null +++ b/database/src/enum/ContributionCycleType.ts @@ -0,0 +1,22 @@ +// lowercase values are not implemented yet +export enum ContributionCycleType { + ONCE = 'ONCE', + HOUR = 'hour', + TWO_HOURS = 'two_hours', + FOUR_HOURS = 'four_hours', + EIGHT_HOURS = 'eight_hours', + HALF_DAY = 'half_day', + DAILY = 'DAILY', + TWO_DAYS = 'two_days', + THREE_DAYS = 'three_days', + FOUR_DAYS = 'four_days', + FIVE_DAYS = 'five_days', + SIX_DAYS = 'six_days', + WEEK = 'week', + TWO_WEEKS = 'two_weeks', + MONTH = 'month', + TWO_MONTH = 'two_month', + QUARTER = 'quarter', + HALF_YEAR = 'half_year', + YEAR = 'year', +} diff --git a/database/src/enum/ContributionStatus.ts b/database/src/enum/ContributionStatus.ts new file mode 100644 index 000000000..a4bbc55f3 --- /dev/null +++ b/database/src/enum/ContributionStatus.ts @@ -0,0 +1,7 @@ +export enum ContributionStatus { + PENDING = 'PENDING', + DELETED = 'DELETED', + IN_PROGRESS = 'IN_PROGRESS', + DENIED = 'DENIED', + CONFIRMED = 'CONFIRMED', +} diff --git a/database/src/enum/ContributionType.ts b/database/src/enum/ContributionType.ts new file mode 100644 index 000000000..3cc15c94c --- /dev/null +++ b/database/src/enum/ContributionType.ts @@ -0,0 +1,5 @@ +export enum ContributionType { + ADMIN = 'ADMIN', + USER = 'USER', + LINK = 'LINK', +} diff --git a/backend/src/seeds/graphql/enums.ts b/database/src/enum/GdtEntryType.ts similarity index 100% rename from backend/src/seeds/graphql/enums.ts rename to database/src/enum/GdtEntryType.ts diff --git a/database/src/enum/RoleNames.ts b/database/src/enum/RoleNames.ts new file mode 100644 index 000000000..dda8eaa8a --- /dev/null +++ b/database/src/enum/RoleNames.ts @@ -0,0 +1,8 @@ +export enum RoleNames { + UNAUTHORIZED = 'UNAUTHORIZED', + USER = 'USER', + MODERATOR = 'MODERATOR', + MODERATOR_AI = 'MODERATOR_AI', + ADMIN = 'ADMIN', + DLT_CONNECTOR = 'DLT_CONNECTOR_ROLE', +} \ No newline at end of file diff --git a/database/src/enum/TransactionTypeId.ts b/database/src/enum/TransactionTypeId.ts new file mode 100644 index 000000000..dc5fc25e0 --- /dev/null +++ b/database/src/enum/TransactionTypeId.ts @@ -0,0 +1,8 @@ +export enum TransactionTypeId { + CREATION = 1, + SEND = 2, + RECEIVE = 3, + // This is a virtual property, never occurring on the database + DECAY = 4, + LINK_SUMMARY = 5, +} diff --git a/database/src/enum/index.ts b/database/src/enum/index.ts index c1d445299..df243acf3 100644 --- a/database/src/enum/index.ts +++ b/database/src/enum/index.ts @@ -1 +1,6 @@ -export * from './CommunityHandshakeStateType' \ No newline at end of file +export * from './CommunityHandshakeStateType' +export * from './ContributionType' +export * from './ContributionStatus' +export * from './TransactionTypeId' +export * from './ContributionCycleType' +export * from './RoleNames' \ No newline at end of file diff --git a/database/src/index.ts b/database/src/index.ts index 45b60530c..2afe19cad 100644 --- a/database/src/index.ts +++ b/database/src/index.ts @@ -4,6 +4,7 @@ export { latestDbVersion } export * from './entity' export * from './logging' export * from './queries' +export * from './seeds' export * from './util' export * from './enum' export { AppDatabase } from './AppDatabase' diff --git a/database/src/seeds/contributionLink/ContributionLinkInterface.ts b/database/src/seeds/contributionLink/ContributionLinkInterface.ts new file mode 100644 index 000000000..15ba4b72d --- /dev/null +++ b/database/src/seeds/contributionLink/ContributionLinkInterface.ts @@ -0,0 +1,7 @@ +export interface ContributionLinkInterface { + amount: number + name: string + memo: string + validFrom?: Date + validTo?: Date +} diff --git a/database/src/seeds/contributionLink/index.ts b/database/src/seeds/contributionLink/index.ts new file mode 100644 index 000000000..15c34d32b --- /dev/null +++ b/database/src/seeds/contributionLink/index.ts @@ -0,0 +1,19 @@ +import { ContributionLinkInterface } from './ContributionLinkInterface' +export type { ContributionLinkInterface } + +export const contributionLinks: ContributionLinkInterface[] = [ + { + name: 'Dokumenta 2017', + memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2017', + amount: 200, + validFrom: new Date(2017, 3, 8), + validTo: new Date(2017, 6, 16), + }, + { + name: 'Dokumenta 2022', + memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022', + amount: 200, + validFrom: new Date(2022, 5, 18), + validTo: new Date(2022, 8, 25), + }, +] diff --git a/database/src/seeds/creation/CreationInterface.ts b/database/src/seeds/creation/CreationInterface.ts new file mode 100644 index 000000000..ee450bd93 --- /dev/null +++ b/database/src/seeds/creation/CreationInterface.ts @@ -0,0 +1,9 @@ +export interface CreationInterface { + email: string + amount: number + memo: string + contributionDate: string + confirmed?: boolean + // number of months to move the confirmed creation to the past + moveCreationDate?: number +} diff --git a/database/src/seeds/creation/index.ts b/database/src/seeds/creation/index.ts new file mode 100644 index 000000000..f20ec2bc5 --- /dev/null +++ b/database/src/seeds/creation/index.ts @@ -0,0 +1,156 @@ +import { nMonthsBefore } from '../factory/creation' + +import { CreationInterface } from './CreationInterface' + +export type { CreationInterface } + +const bobsSendings = [ + { + amount: 10, + memo: 'Herzlich Willkommen bei Gradido!', + }, + { + amount: 10, + memo: 'für deine Hilfe, Betty', + }, + { + amount: 23.37, + memo: 'für deine Hilfe, David', + }, + { + amount: 47, + memo: 'für deine Hilfe, Frau Holle', + }, + { + amount: 1.02, + memo: 'für deine Hilfe, Herr Müller', + }, + { + amount: 5.67, + memo: 'für deine Hilfe, Maier', + }, + { + amount: 72.93, + memo: 'für deine Hilfe, Elsbeth', + }, + { + amount: 5.6, + memo: 'für deine Hilfe, Daniel', + }, + { + amount: 8.87, + memo: 'für deine Hilfe, Yoda', + }, + { + amount: 7.56, + memo: 'für deine Hilfe, Sabine', + }, + { + amount: 7.89, + memo: 'für deine Hilfe, Karl', + }, + { + amount: 8.9, + memo: 'für deine Hilfe, Darth Vader', + }, + { + amount: 56.79, + memo: 'für deine Hilfe, Luci', + }, + { + amount: 3.45, + memo: 'für deine Hilfe, Hanne', + }, + { + amount: 8.74, + memo: 'für deine Hilfe, Luise', + }, + { + amount: 7.85, + memo: 'für deine Hilfe, Annegred', + }, + { + amount: 32.7, + memo: 'für deine Hilfe, Prinz von Zamunda', + }, + { + amount: 44.2, + memo: 'für deine Hilfe, Charly Brown', + }, + { + amount: 38.17, + memo: 'für deine Hilfe, Michael', + }, + { + amount: 5.72, + memo: 'für deine Hilfe, Kaja', + }, + { + amount: 3.99, + memo: 'für deine Hilfe, Maja', + }, + { + amount: 4.5, + memo: 'für deine Hilfe, Martha', + }, + { + amount: 8.3, + memo: 'für deine Hilfe, Ursula', + }, + { + amount: 2.9, + memo: 'für deine Hilfe, Urs', + }, + { + amount: 4.6, + memo: 'für deine Hilfe, Mecedes', + }, + { + amount: 74.1, + memo: 'für deine Hilfe, Heidi', + }, + { + amount: 4.5, + memo: 'für deine Hilfe, Peter', + }, + { + amount: 5.8, + memo: 'für deine Hilfe, Fräulein Rottenmeier', + }, +] +const bobsTransactions: CreationInterface[] = [] +bobsSendings.forEach((sending) => { + bobsTransactions.push({ + email: 'bob@baumeister.de', + amount: sending.amount, + memo: sending.memo, + contributionDate: nMonthsBefore(new Date()), + confirmed: true, + }) +}) + +export const creations: CreationInterface[] = [ + { + email: 'bibi@bloxberg.de', + amount: 1000, + memo: 'Herzlich Willkommen bei Gradido!', + contributionDate: nMonthsBefore(new Date()), + confirmed: true, + moveCreationDate: 12, + }, + { + email: 'bibi@bloxberg.de', + amount: 1000, + memo: '#Hexen', + contributionDate: nMonthsBefore(new Date()), + confirmed: true, + }, + ...bobsTransactions, + { + email: 'raeuber@hotzenplotz.de', + amount: 1000, + memo: 'Herzlich Willkommen bei Gradido!', + contributionDate: nMonthsBefore(new Date()), + confirmed: true, + }, +] diff --git a/database/src/seeds/factory/contributionLink.ts b/database/src/seeds/factory/contributionLink.ts new file mode 100644 index 000000000..c05888568 --- /dev/null +++ b/database/src/seeds/factory/contributionLink.ts @@ -0,0 +1,31 @@ +import Decimal from 'decimal.js-light' +import { ContributionLink } from '../../entity' + +import { ContributionLinkInterface } from '../contributionLink/ContributionLinkInterface' +import { transactionLinkCode } from './transactionLink' +import { ContributionCycleType } from '../../enum' + +export function contributionLinkFactory(contributionLink: ContributionLinkInterface): Promise { + return createContributionLink(contributionLink) +} + +export function createContributionLink(contributionLinkData: ContributionLinkInterface): Promise { + const contributionLink = new ContributionLink() + contributionLink.amount = new Decimal(contributionLinkData.amount) + contributionLink.name = contributionLinkData.name + contributionLink.memo = contributionLinkData.memo + contributionLink.createdAt = new Date() + contributionLink.code = transactionLinkCode(new Date()) + contributionLink.cycle = ContributionCycleType.ONCE + if (contributionLinkData.validFrom) { + contributionLink.validFrom = contributionLinkData.validFrom + } + if (contributionLinkData.validTo) { + contributionLink.validTo = contributionLinkData.validTo + } + contributionLink.maxAmountPerMonth = new Decimal(200) + contributionLink.maxPerCycle = 1 + + return contributionLink.save() +} + \ No newline at end of file diff --git a/database/src/seeds/factory/creation.ts b/database/src/seeds/factory/creation.ts new file mode 100644 index 000000000..8870a717f --- /dev/null +++ b/database/src/seeds/factory/creation.ts @@ -0,0 +1,134 @@ +import { Contribution, Transaction, User } from '../../entity' +import { Decimal } from 'decimal.js-light' +import { CreationInterface } from '../creation/CreationInterface' +import { ContributionType, ContributionStatus, TransactionTypeId } from '../../enum' +import { findUserByIdentifier } from '../../queries' +import { createTransaction } from './transaction' +import { AppDatabase } from '../../AppDatabase' + +export function nMonthsBefore(date: Date, months = 1): string { + return new Date(date.getFullYear(), date.getMonth() - months, 1).toISOString() +} + +export async function creationFactory( + creation: CreationInterface, + user?: User | null, + moderatorUser?: User | null, +): Promise { + if (!user) { + user = await findUserByIdentifier(creation.email) + } + if (!user) { + throw new Error(`User ${creation.email} not found`) + } + let contribution = await createContribution(creation, user) + if (creation.confirmed) { + if (!moderatorUser) { + moderatorUser = await findUserByIdentifier('peter@lustig.de') + } + if (!moderatorUser) { + throw new Error('Moderator user not found') + } + await confirmTransaction(creation, contribution, moderatorUser) + } + return contribution +} + +export async function creationFactoryBulk( + creations: CreationInterface[], + userCreationIndexedByEmail: Map, + moderatorUser: User, +): Promise { + const lastTransaction = await Transaction.findOne({ order: { id: 'DESC' }, select: ['id'], where: {} }) + let transactionId = lastTransaction ? lastTransaction.id + 1 : 1 + const dbContributions: Contribution[] = [] + const dbTransactions: Transaction[] = [] + + for (const creation of creations) { + const user = userCreationIndexedByEmail.get(creation.email) + if (!user) { + throw new Error(`User ${creation.email} not found`) + } + let contribution = await createContribution(creation, user, false) + if (creation.confirmed) { + const { contribution: _, transaction } = await confirmTransaction( + creation, + contribution, + moderatorUser, + transactionId, + false + ) + dbTransactions.push(transaction) + transactionId++ + } + dbContributions.push(contribution) + } + const dataSource = AppDatabase.getInstance().getDataSource() + await dataSource.transaction(async (transaction) => { + await dataSource.getRepository(Contribution).insert(dbContributions) + await dataSource.getRepository(Transaction).insert(dbTransactions) + }) + return dbContributions +} + +export async function createContribution(creation: CreationInterface, user: User, store: boolean = true): Promise { + const contribution = new Contribution() + contribution.user = user + contribution.userId = user.id + contribution.amount = new Decimal(creation.amount) + contribution.createdAt = new Date() + contribution.contributionDate = getContributionDate(creation) + contribution.memo = creation.memo + contribution.contributionType = ContributionType.USER + contribution.contributionStatus = ContributionStatus.PENDING + + return store ? contribution.save() : contribution +} + +export async function confirmTransaction( + creation: CreationInterface, + contribution: Contribution, + moderatorUser: User, + transactionId?: number, + store: boolean = true +): Promise<{ contribution: Contribution, transaction: Transaction }> { + const balanceDate = getBalanceDate(creation) + const transaction = await createTransaction( + contribution.amount, + contribution.memo, + contribution.user, + moderatorUser, + TransactionTypeId.CREATION, + balanceDate, + contribution.contributionDate, + transactionId, + store, + ) + contribution.confirmedAt = balanceDate + contribution.confirmedBy = moderatorUser.id + contribution.transactionId = transaction.id + contribution.transaction = transaction + contribution.contributionStatus = ContributionStatus.CONFIRMED + + if (store) { + await contribution.save() + await transaction.save() + } + + return { contribution, transaction } +} + +function getContributionDate(creation: CreationInterface): Date { + if (creation.moveCreationDate) { + return new Date(nMonthsBefore(new Date(creation.contributionDate), creation.moveCreationDate)) + } + return new Date(creation.contributionDate) +} + +function getBalanceDate(creation: CreationInterface): Date { + const now = new Date() + if (creation.moveCreationDate) { + return new Date(nMonthsBefore(now, creation.moveCreationDate)) + } + return now +} \ No newline at end of file diff --git a/database/src/seeds/factory/index.ts b/database/src/seeds/factory/index.ts new file mode 100644 index 000000000..1165a6f27 --- /dev/null +++ b/database/src/seeds/factory/index.ts @@ -0,0 +1,6 @@ +export * from './contributionLink' +export * from './creation' +export * from './pendingTransaction' +export * from './transaction' +export * from './transactionLink' +export * from './user' diff --git a/database/src/seeds/factory/transaction.ts b/database/src/seeds/factory/transaction.ts new file mode 100644 index 000000000..02a56723e --- /dev/null +++ b/database/src/seeds/factory/transaction.ts @@ -0,0 +1,59 @@ +import Decimal from 'decimal.js-light' +import { User, Transaction } from '../../entity' +import { TransactionTypeId } from '../../enum' +import { fullName } from 'shared' +import { getLastTransaction } from '../../queries' +import { calculateDecay, Decay } from 'shared' + +export async function createTransaction( + amount: Decimal, + memo: string, + user: User, + linkedUser: User, + type: TransactionTypeId, + balanceDate: Date, + creationDate?: Date, + id?: number, + store: boolean = true, +): Promise { + + const lastTransaction = await getLastTransaction(user.id) + // balance and decay calculation + let newBalance = new Decimal(0) + let decay: Decay | null = null + if (lastTransaction) { + decay = calculateDecay( + lastTransaction.balance, + lastTransaction.balanceDate, + balanceDate, + ) + newBalance = decay.balance + } + newBalance = newBalance.add(amount.toString()) + + const transaction = new Transaction() + if (id) { + transaction.id = id + } + transaction.typeId = type + transaction.memo = memo + transaction.userId = user.id + transaction.userGradidoID = user.gradidoID + transaction.userName = fullName(user.firstName, user.lastName) + transaction.userCommunityUuid = user.communityUuid + transaction.linkedUserId = linkedUser.id + transaction.linkedUserGradidoID = linkedUser.gradidoID + transaction.linkedUserName = fullName(linkedUser.firstName, linkedUser.lastName) + transaction.linkedUserCommunityUuid = linkedUser.communityUuid + transaction.previous = lastTransaction ? lastTransaction.id : null + transaction.amount = amount + if (creationDate) { + transaction.creationDate = creationDate + } + transaction.balance = newBalance + transaction.balanceDate = balanceDate + transaction.decay = decay ? decay.decay : new Decimal(0) + transaction.decayStart = decay ? decay.start : null + + return store ? transaction.save() : transaction +} \ No newline at end of file diff --git a/database/src/seeds/factory/transactionLink.ts b/database/src/seeds/factory/transactionLink.ts new file mode 100644 index 000000000..956e33fb5 --- /dev/null +++ b/database/src/seeds/factory/transactionLink.ts @@ -0,0 +1,77 @@ +import { TransactionLinkInterface } from '../transactionLink/TransactionLinkInterface' +import { TransactionLink, User } from '../../entity' +import { Decimal } from 'decimal.js-light' +import { findUserByIdentifier } from '../../queries' +import { compoundInterest } from 'shared' +import { randomBytes } from 'node:crypto' +import { AppDatabase } from '../../AppDatabase' + +export async function transactionLinkFactory( + transactionLinkData: TransactionLinkInterface, + userId?: number, +): Promise { + if (!userId) { + const user = await findUserByIdentifier(transactionLinkData.email) + if (!user) { + throw new Error(`User ${transactionLinkData.email} not found`) + } + userId = user.id + } + return createTransactionLink(transactionLinkData, userId) +} + +export async function transactionLinkFactoryBulk( + transactionLinks: TransactionLinkInterface[], + userCreationIndexedByEmail: Map +): Promise { + const dbTransactionLinks: TransactionLink[] = [] + for (const transactionLink of transactionLinks) { + const user = userCreationIndexedByEmail.get(transactionLink.email) + if (!user) { + throw new Error(`User ${transactionLink.email} not found`) + } + dbTransactionLinks.push(await createTransactionLink(transactionLink, user.id, false)) + } + const dataSource = AppDatabase.getInstance().getDataSource() + await dataSource.getRepository(TransactionLink).insert(dbTransactionLinks) + return dbTransactionLinks +} + +export async function createTransactionLink(transactionLinkData: TransactionLinkInterface, userId: number, store: boolean = true): Promise { + const holdAvailableAmount = compoundInterest(new Decimal(transactionLinkData.amount.toString()), CODE_VALID_DAYS_DURATION * 24 * 60 * 60) + let createdAt = transactionLinkData.createdAt || new Date() + const validUntil = transactionLinkExpireDate(createdAt) + + const transactionLink = new TransactionLink() + transactionLink.userId = userId + transactionLink.amount = new Decimal(transactionLinkData.amount) + transactionLink.memo = transactionLinkData.memo + transactionLink.holdAvailableAmount = holdAvailableAmount + transactionLink.code = transactionLinkCode(createdAt) + transactionLink.createdAt = createdAt + transactionLink.validUntil = validUntil + + if (transactionLinkData.deletedAt) { + transactionLink.deletedAt = new Date(createdAt.getTime() + 1000) + } + + return store ? transactionLink.save() : transactionLink +} + +////// Transaction Link BUSINESS LOGIC ////// +// TODO: move business logic to shared +export const CODE_VALID_DAYS_DURATION = 14 + +export const transactionLinkExpireDate = (date: Date): Date => { + const validUntil = new Date(date) + return new Date(validUntil.setDate(date.getDate() + CODE_VALID_DAYS_DURATION)) +} + +export const transactionLinkCode = (date: Date): string => { + const time = date.getTime().toString(16) + return ( + randomBytes(12) + .toString('hex') + .substring(0, 24 - time.length) + time + ) +} \ No newline at end of file diff --git a/database/src/seeds/factory/user.ts b/database/src/seeds/factory/user.ts index 3772fe66d..4d16c009e 100644 --- a/database/src/seeds/factory/user.ts +++ b/database/src/seeds/factory/user.ts @@ -1,16 +1,77 @@ import { UserInterface } from '../users/UserInterface' -import { User, UserContact } from '../../entity' +import { User, UserContact, UserRole } from '../../entity' import { v4 } from 'uuid' import { UserContactType, OptInType, PasswordEncryptionType } from 'shared' import { getHomeCommunity } from '../../queries/communities' -import random from 'crypto-random-bigint' +import random from 'random-bigint' +import { Community } from '../../entity' +import { AppDatabase } from '../..' +import { RoleNames } from '../../enum/RoleNames' -export const userFactory = async (user: UserInterface): Promise => { - let dbUserContact = new UserContact() +export async function userFactory(user: UserInterface, homeCommunity?: Community | null): Promise { + // TODO: improve with cascade + let dbUser = await createUser(user, homeCommunity) + let dbUserContact = await createUserContact(user, dbUser.id) + dbUser.emailId = dbUserContact.id + dbUser.emailContact = dbUserContact + dbUser = await dbUser.save() - dbUserContact.email = user.email ?? '' - dbUserContact.type = UserContactType.USER_CONTACT_EMAIL + const userRole = user.role as RoleNames + if (userRole && (userRole === RoleNames.ADMIN || userRole === RoleNames.MODERATOR)) { + dbUser.userRoles = [await createUserRole(dbUser.id, userRole)] + } + return dbUser +} + +// only use in non-parallel environment (seeding for example) +export async function userFactoryBulk(users: UserInterface[], homeCommunity?: Community | null): Promise { + const dbUsers: User[] = [] + const dbUserContacts: UserContact[] = [] + const dbUserRoles: UserRole[] = [] + const lastUser = await User.findOne({ order: { id: 'DESC' }, select: ['id'], where: {} }) + const lastUserContact = await UserContact.findOne({ order: { id: 'DESC' }, select: ['id'], where: {} }) + let userId = lastUser ? lastUser.id + 1 : 1 + let emailId = lastUserContact ? lastUserContact.id + 1 : 1 + // console.log(`start with userId: ${userId} and emailId: ${emailId}`) + for(const user of users) { + const dbUser = await createUser(user, homeCommunity, false) + dbUser.id = userId + dbUser.emailId = emailId + + const dbUserContact = await createUserContact(user, userId, false) + dbUserContact.id = emailId + dbUserContact.userId = userId + dbUser.emailContact = dbUserContact + + dbUsers.push(dbUser) + dbUserContacts.push(dbUserContact) + + const userRole = user.role as RoleNames + if (userRole && (userRole === RoleNames.ADMIN || userRole === RoleNames.MODERATOR)) { + dbUserRoles.push(await createUserRole(dbUser.id, userRole, false)) + } + + userId++ + emailId++ + } + const dataSource = AppDatabase.getInstance().getDataSource() + await dataSource.transaction(async transaction => { + // typeorm change my data what I don't want + // because of manuel id assignment + const dbUsersCopy = dbUsers.map(user => ({ ...user })) + const dbUserContactsCopy = dbUserContacts.map(userContact => ({ ...userContact })) + const dbUserRolesCopy = dbUserRoles.map(userRole => ({ ...userRole })) + await Promise.all([ + transaction.getRepository(User).insert(dbUsersCopy), + transaction.getRepository(UserContact).insert(dbUserContactsCopy), + transaction.getRepository(UserRole).insert(dbUserRolesCopy) + ]) + }) + return dbUsers +} + +export async function createUser(user: UserInterface, homeCommunity?: Community | null, store: boolean = true): Promise { let dbUser = new User() dbUser.firstName = user.firstName ?? '' dbUser.lastName = user.lastName ?? '' @@ -21,25 +82,50 @@ export const userFactory = async (user: UserInterface): Promise => { dbUser.createdAt = user.createdAt ?? new Date() dbUser.deletedAt = user.deletedAt ?? null dbUser.publisherId = user.publisherId ?? 0 + dbUser.humhubAllowed = true dbUser.gradidoID = v4() if (user.emailChecked) { - dbUserContact.emailVerificationCode = random(64).toString() - dbUserContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_REGISTER - dbUserContact.emailChecked = true - dbUser.password = random(64) + // dbUser.password = dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID } - const homeCommunity = await getHomeCommunity() + if (!homeCommunity) { + homeCommunity = await getHomeCommunity() + } if (homeCommunity) { dbUser.community = homeCommunity dbUser.communityUuid = homeCommunity.communityUuid! } - // TODO: improve with cascade - dbUser = await dbUser.save() - dbUserContact.userId = dbUser.id - dbUserContact = await dbUserContact.save() - dbUser.emailId = dbUserContact.id - dbUser.emailContact = dbUserContact - return dbUser.save() + + return store ? dbUser.save() : dbUser +} + +export async function createUserContact(user: UserInterface, userId?: number, store: boolean = true): Promise { + let dbUserContact = new UserContact() + + dbUserContact.email = user.email ?? '' + dbUserContact.type = UserContactType.USER_CONTACT_EMAIL + + if (user.createdAt) { + dbUserContact.createdAt = user.createdAt + dbUserContact.updatedAt = user.createdAt + } + if (user.emailChecked) { + dbUserContact.emailVerificationCode = random(64).toString() + dbUserContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_REGISTER + dbUserContact.emailChecked = true + } + + if (userId) { + dbUserContact.userId = userId + } + + return store ? dbUserContact.save() : dbUserContact +} + +export async function createUserRole(userId: number, role: RoleNames, store: boolean = true): Promise { + let dbUserRole = new UserRole() + dbUserRole.userId = userId + dbUserRole.role = role + return store ? dbUserRole.save() : dbUserRole } \ No newline at end of file diff --git a/database/src/seeds/index.ts b/database/src/seeds/index.ts new file mode 100644 index 000000000..61c60ba28 --- /dev/null +++ b/database/src/seeds/index.ts @@ -0,0 +1,5 @@ +export * from './contributionLink' +export * from './creation' +export * from './factory' +export * from './transactionLink' +export * from './users' diff --git a/database/src/seeds/transactionLink/TransactionLinkInterface.ts b/database/src/seeds/transactionLink/TransactionLinkInterface.ts new file mode 100644 index 000000000..eaacfdf92 --- /dev/null +++ b/database/src/seeds/transactionLink/TransactionLinkInterface.ts @@ -0,0 +1,10 @@ +export interface TransactionLinkInterface { + email: string + amount: number + memo: string + createdAt?: Date + // TODO: for testing + // redeemedAt?: Date + // redeemedBy?: number + deletedAt?: boolean +} diff --git a/database/src/seeds/transactionLink/index.ts b/database/src/seeds/transactionLink/index.ts new file mode 100644 index 000000000..2d5364aa5 --- /dev/null +++ b/database/src/seeds/transactionLink/index.ts @@ -0,0 +1,56 @@ +import { TransactionLinkInterface } from './TransactionLinkInterface' +export type { TransactionLinkInterface } + +export const transactionLinks: TransactionLinkInterface[] = [ + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: 'Leider wollte niemand meine Gradidos haben :(', + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + // TODO: for testing + // memo: `Yeah, eingelöst!`, + // redeemedAt: new Date(2022, 2, 2), + // redeemedBy: not null, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: true, + }, +] diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts index 9a40e922b..f78a3af1c 100644 --- a/database/src/seeds/users/bibi-bloxberg.ts +++ b/database/src/seeds/users/bibi-bloxberg.ts @@ -9,4 +9,6 @@ export const bibiBloxberg: UserInterface = { emailChecked: true, language: 'de', publisherId: 1234, + // move user createdAt before transaction link + createdAt: new Date(2021, 9, 17), } diff --git a/database/src/seeds/users/index.ts b/database/src/seeds/users/index.ts index beb6c6f25..8149ffd4c 100644 --- a/database/src/seeds/users/index.ts +++ b/database/src/seeds/users/index.ts @@ -4,8 +4,19 @@ import { garrickOllivander } from './garrick-ollivander' import { peterLustig } from './peter-lustig' import { raeuberHotzenplotz } from './raeuber-hotzenplotz' import { stephenHawking } from './stephen-hawking' +import { UserInterface } from './UserInterface' -export const users = [ +export { + type UserInterface, + bibiBloxberg, + bobBaumeister, + garrickOllivander, + peterLustig, + raeuberHotzenplotz, + stephenHawking +} + +export const users: UserInterface[] = [ peterLustig, bibiBloxberg, bobBaumeister, diff --git a/database/src/seeds/users/peter-lustig.ts b/database/src/seeds/users/peter-lustig.ts index 58b07fe99..e20bac5fe 100644 --- a/database/src/seeds/users/peter-lustig.ts +++ b/database/src/seeds/users/peter-lustig.ts @@ -1,4 +1,5 @@ import { UserInterface } from './UserInterface' +import { RoleNames } from '../../enum' export const peterLustig: UserInterface = { email: 'peter@lustig.de', @@ -7,5 +8,6 @@ export const peterLustig: UserInterface = { // description: 'Latzhose und Nickelbrille', createdAt: new Date('2020-11-25T10:48:43'), emailChecked: true, - language: 'de' + language: 'de', + role: RoleNames.ADMIN, } diff --git a/database/tsconfig.json b/database/tsconfig.json index 862e9bf94..bf09027c5 100644 --- a/database/tsconfig.json +++ b/database/tsconfig.json @@ -50,7 +50,11 @@ //"@/*": ["src/*"], /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ //}, // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */ + "typeRoots": [ + "./node_modules/@types", + "../node_modules/@types", + "../@types" + ], // "types": ["node"], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ diff --git a/dht-node/tsconfig.json b/dht-node/tsconfig.json index 9628b7c41..8d979c82b 100644 --- a/dht-node/tsconfig.json +++ b/dht-node/tsconfig.json @@ -52,9 +52,9 @@ }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": [ /* List of folders to include type definitions from. */ - "src/dht_node/@types", "node_modules/@types", - "../node_modules/@types" + "../node_modules/@types", + "../@types" ], // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ diff --git a/federation/tsconfig.json b/federation/tsconfig.json index 4082df448..a687b17a5 100644 --- a/federation/tsconfig.json +++ b/federation/tsconfig.json @@ -64,6 +64,7 @@ "typeRoots": [ /* List of folders to include type definitions from. */ "node_modules/@types", "../node_modules/@types", + "../@types" ], // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ diff --git a/shared/src/index.ts b/shared/src/index.ts index 1e44e2c48..9eb6655c5 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -3,6 +3,7 @@ export * from './enum' export * from './const' export * from './helper' export * from './logic/decay' +export * from './util' export * from './jwt/JWT' export * from './jwt/payloadtypes/AuthenticationJwtPayloadType' export * from './jwt/payloadtypes/AuthenticationResponseJwtPayloadType' diff --git a/shared/src/util/index.ts b/shared/src/util/index.ts new file mode 100644 index 000000000..c2a905916 --- /dev/null +++ b/shared/src/util/index.ts @@ -0,0 +1 @@ +export * from './utilities' \ No newline at end of file diff --git a/shared/src/util/utilities.ts b/shared/src/util/utilities.ts new file mode 100644 index 000000000..7473f5ed0 --- /dev/null +++ b/shared/src/util/utilities.ts @@ -0,0 +1,2 @@ +export const fullName = (firstName: string, lastName: string): string => + [firstName, lastName].filter(Boolean).join(' ')