mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'master' into backend_email_fix_contribution_link_message
This commit is contained in:
commit
9acb2c7938
@ -90,7 +90,7 @@
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typed-rest-client": "^1.8.11",
|
||||
"typeorm": "^0.3.16",
|
||||
"typeorm": "^0.3.22",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^8.3.2",
|
||||
"workerpool": "^9.2.0",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Transaction as DbTransaction } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
@ -10,10 +10,10 @@ import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { DltConnectorClient } from './DltConnectorClient'
|
||||
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
|
||||
let testEnv: {
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
// Mock the GraphQLClient
|
||||
@ -83,7 +83,7 @@ describe('transmitTransaction', () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
const transaction = new DbTransaction()
|
||||
|
||||
@ -8,8 +8,7 @@ import { getHomeCommunity } from '@/graphql/resolver/util/communities'
|
||||
import { sendUserToGms } from '@/graphql/resolver/util/sendUserToGms'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { checkDBVersion } from '@/typeorm/DBVersion'
|
||||
import { Connection } from '@/typeorm/connection'
|
||||
import { AppDatabase } from 'database'
|
||||
|
||||
CONFIG.EMAIL = false
|
||||
// use force to copy over all user even if gmsRegistered is set to true
|
||||
@ -17,18 +16,8 @@ const forceMode = process.argv.includes('--force')
|
||||
|
||||
async function main() {
|
||||
// open mysql connection
|
||||
const con = await Connection.getInstance()
|
||||
if (!con?.isConnected) {
|
||||
logger.fatal(`Couldn't open connection to database!`)
|
||||
throw new Error(`Fatal: Couldn't open connection to database`)
|
||||
}
|
||||
|
||||
// check for correct database version
|
||||
const dbVersion = await checkDBVersion(CONFIG.DB_VERSION)
|
||||
if (!dbVersion) {
|
||||
logger.fatal('Fatal: Database Version incorrect')
|
||||
throw new Error('Fatal: Database Version incorrect')
|
||||
}
|
||||
const con = AppDatabase.getInstance()
|
||||
await con.init()
|
||||
|
||||
const homeCom = await getHomeCommunity()
|
||||
if (homeCom.gmsApiKey === null) {
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import { User } from 'database'
|
||||
import { AppDatabase, User } from 'database'
|
||||
import { IsNull, Not } from 'typeorm'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { checkDBVersion } from '@/typeorm/DBVersion'
|
||||
import { Connection } from '@/typeorm/connection'
|
||||
|
||||
import { HumHubClient } from './HumHubClient'
|
||||
import { GetUser } from './model/GetUser'
|
||||
@ -66,18 +63,8 @@ async function main() {
|
||||
const start = new Date().getTime()
|
||||
|
||||
// open mysql connection
|
||||
const con = await Connection.getInstance()
|
||||
if (!con?.isConnected) {
|
||||
logger.fatal(`Couldn't open connection to database!`)
|
||||
throw new Error(`Fatal: Couldn't open connection to database`)
|
||||
}
|
||||
|
||||
// check for correct database version
|
||||
const dbVersion = await checkDBVersion(CONFIG.DB_VERSION)
|
||||
if (!dbVersion) {
|
||||
logger.fatal('Fatal: Database Version incorrect')
|
||||
throw new Error('Fatal: Database Version incorrect')
|
||||
}
|
||||
const con = AppDatabase.getInstance()
|
||||
await con.init()
|
||||
|
||||
let userCount = 0
|
||||
let page = 0
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
|
||||
import { validate } from 'config-schema'
|
||||
import { latestDbVersion } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
@ -15,8 +14,6 @@ Decimal.set({
|
||||
})
|
||||
|
||||
const constants = {
|
||||
// DB_VERSION: '0087-add_index_on_user_roles',
|
||||
DB_VERSION: latestDbVersion,
|
||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
||||
LOG4JS_CONFIG: 'log4js-config.json',
|
||||
}
|
||||
@ -34,21 +31,6 @@ const server = {
|
||||
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
|
||||
}
|
||||
|
||||
const database = {
|
||||
DB_CONNECT_RETRY_COUNT: process.env.DB_CONNECT_RETRY_COUNT
|
||||
? Number.parseInt(process.env.DB_CONNECT_RETRY_COUNT)
|
||||
: 15,
|
||||
DB_CONNECT_RETRY_DELAY_MS: process.env.DB_CONNECT_RETRY_DELAY_MS
|
||||
? Number.parseInt(process.env.DB_CONNECT_RETRY_DELAY_MS)
|
||||
: 500,
|
||||
DB_HOST: process.env.DB_HOST ?? 'localhost',
|
||||
DB_PORT: process.env.DB_PORT ? Number.parseInt(process.env.DB_PORT) : 3306,
|
||||
DB_USER: process.env.DB_USER ?? 'root',
|
||||
DB_PASSWORD: process.env.DB_PASSWORD ?? '',
|
||||
DB_DATABASE: process.env.DB_DATABASE ?? 'gradido_community',
|
||||
TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH ?? 'typeorm.backend.log',
|
||||
}
|
||||
|
||||
const klicktipp = {
|
||||
KLICKTIPP: process.env.KLICKTIPP === 'true' || false,
|
||||
KLICKTTIPP_API_URL: process.env.KLICKTIPP_API_URL ?? 'https://api.klicktipp.com',
|
||||
@ -163,7 +145,6 @@ const openai = {
|
||||
export const CONFIG = {
|
||||
...constants,
|
||||
...server,
|
||||
...database,
|
||||
...klicktipp,
|
||||
...dltConnector,
|
||||
...community,
|
||||
|
||||
@ -3,14 +3,6 @@ import {
|
||||
COMMUNITY_NAME,
|
||||
COMMUNITY_SUPPORT_MAIL,
|
||||
COMMUNITY_URL,
|
||||
DB_CONNECT_RETRY_COUNT,
|
||||
DB_CONNECT_RETRY_DELAY_MS,
|
||||
DB_DATABASE,
|
||||
DB_HOST,
|
||||
DB_PASSWORD,
|
||||
DB_PORT,
|
||||
DB_USER,
|
||||
DB_VERSION,
|
||||
DECAY_START_TIME,
|
||||
GDT_ACTIVE,
|
||||
GDT_API_URL,
|
||||
@ -25,7 +17,6 @@ import {
|
||||
NODE_ENV,
|
||||
OPENAI_ACTIVE,
|
||||
PRODUCTION,
|
||||
TYPEORM_LOGGING_RELATIVE_PATH,
|
||||
} from 'config-schema'
|
||||
import Joi from 'joi'
|
||||
|
||||
@ -34,14 +25,6 @@ export const schema = Joi.object({
|
||||
COMMUNITY_URL,
|
||||
COMMUNITY_DESCRIPTION,
|
||||
COMMUNITY_SUPPORT_MAIL,
|
||||
DB_HOST,
|
||||
DB_PASSWORD,
|
||||
DB_PORT,
|
||||
DB_USER,
|
||||
DB_VERSION,
|
||||
DB_DATABASE,
|
||||
DB_CONNECT_RETRY_COUNT,
|
||||
DB_CONNECT_RETRY_DELAY_MS,
|
||||
DECAY_START_TIME,
|
||||
GDT_API_URL,
|
||||
GDT_ACTIVE,
|
||||
@ -56,7 +39,6 @@ export const schema = Joi.object({
|
||||
NODE_ENV,
|
||||
OPENAI_ACTIVE,
|
||||
PRODUCTION,
|
||||
TYPEORM_LOGGING_RELATIVE_PATH,
|
||||
|
||||
COMMUNITY_REDEEM_URL: Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
@ -45,11 +45,11 @@ jest.mock('nodemailer', () => {
|
||||
}
|
||||
})
|
||||
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -58,7 +58,7 @@ beforeAll(async () => {
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
const sendEmailTranslatedSpy = jest.spyOn(sendEmailTranslatedApi, 'sendEmailTranslated')
|
||||
|
||||
@ -2,18 +2,18 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { Response } from 'graphql-request/dist/types'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
|
||||
import { validateCommunities } from './validateCommunities'
|
||||
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -24,7 +24,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
// await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('validate Communities', () => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLError } from 'graphql/error/GraphQLError'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
@ -25,12 +25,12 @@ jest.mock('@/password/EncryptorUtils')
|
||||
// to do: We need a setup for the tests that closes the connection
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
const peterLoginData = {
|
||||
@ -49,7 +49,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
// real valid ed25519 key pairs
|
||||
|
||||
@ -2,7 +2,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { ContributionLink as DbContributionLink, Event as DbEvent } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
@ -23,11 +23,11 @@ jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -42,7 +42,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('Contribution Links', () => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Contribution as DbContribution, Event as DbEvent } from 'database'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
@ -34,11 +34,11 @@ jest.mock('@/emails/sendEmailVariants', () => {
|
||||
})
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
let result: any
|
||||
|
||||
@ -51,7 +51,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('ContributionMessageResolver', () => {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import {
|
||||
AppDatabase,
|
||||
Contribution as DbContribution,
|
||||
ContributionMessage as DbContributionMessage,
|
||||
User as DbUser,
|
||||
} from 'database'
|
||||
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { EntityManager, FindOptionsRelations, getConnection } from 'typeorm'
|
||||
import { EntityManager, FindOptionsRelations } from 'typeorm'
|
||||
|
||||
import { ContributionMessageArgs } from '@arg/ContributionMessageArgs'
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
@ -26,6 +27,8 @@ import { backendLogger as logger } from '@/server/logger'
|
||||
import { contributionFrontendLink } from './util/contributions'
|
||||
import { findContributionMessages } from './util/findContributionMessages'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
@Resolver()
|
||||
export class ContributionMessageResolver {
|
||||
@Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE])
|
||||
@ -44,9 +47,9 @@ export class ContributionMessageResolver {
|
||||
let finalContributionMessage: DbContributionMessage | undefined
|
||||
|
||||
try {
|
||||
await getConnection().transaction(
|
||||
'REPEATABLE READ',
|
||||
async (transactionalEntityManager: EntityManager) => {
|
||||
await db
|
||||
.getDataSource()
|
||||
.transaction('REPEATABLE READ', async (transactionalEntityManager: EntityManager) => {
|
||||
const { contribution, contributionMessage, contributionChanged } =
|
||||
await updateUnconfirmedContributionContext.run(transactionalEntityManager)
|
||||
|
||||
@ -63,8 +66,7 @@ export class ContributionMessageResolver {
|
||||
|
||||
finalContribution = contribution
|
||||
finalContributionMessage = contributionMessage
|
||||
},
|
||||
)
|
||||
})
|
||||
} catch (e) {
|
||||
throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e)
|
||||
}
|
||||
@ -138,9 +140,9 @@ export class ContributionMessageResolver {
|
||||
let finalContributionMessage: DbContributionMessage | undefined
|
||||
|
||||
try {
|
||||
await getConnection().transaction(
|
||||
'REPEATABLE READ',
|
||||
async (transactionalEntityManager: EntityManager) => {
|
||||
await db
|
||||
.getDataSource()
|
||||
.transaction('REPEATABLE READ', async (transactionalEntityManager: EntityManager) => {
|
||||
const { contribution, contributionMessage, contributionChanged } =
|
||||
await updateUnconfirmedContributionContext.run(transactionalEntityManager, relations)
|
||||
if (contributionChanged) {
|
||||
@ -160,8 +162,7 @@ export class ContributionMessageResolver {
|
||||
}
|
||||
finalContribution = contribution
|
||||
finalContributionMessage = contributionMessage
|
||||
},
|
||||
)
|
||||
})
|
||||
} catch (e) {
|
||||
throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e)
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { UserInputError } from 'apollo-server-express'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Contribution, Event as DbEvent, Transaction as DbTransaction, User } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection, Equal } from 'typeorm'
|
||||
import { DataSource, Equal } from 'typeorm'
|
||||
|
||||
import { ContributionMessageType } from '@enum/ContributionMessageType'
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
@ -57,11 +56,11 @@ jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
let creation: Contribution | null
|
||||
let admin: User
|
||||
@ -82,7 +81,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('ContributionResolver', () => {
|
||||
|
||||
@ -48,7 +48,7 @@ import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
import { fullName } from '@/util/utilities'
|
||||
|
||||
import { start } from 'repl'
|
||||
import { AppDatabase } from 'database'
|
||||
import { ContributionMessageType } from '../enum/ContributionMessageType'
|
||||
import {
|
||||
contributionFrontendLink,
|
||||
@ -61,6 +61,8 @@ import { findContributions } from './util/findContributions'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
@Resolver(() => Contribution)
|
||||
export class ContributionResolver {
|
||||
@Authorized([RIGHTS.ADMIN_LIST_CONTRIBUTIONS])
|
||||
@ -195,7 +197,7 @@ export class ContributionResolver {
|
||||
context,
|
||||
)
|
||||
const { contribution, contributionMessage } = await updateUnconfirmedContributionContext.run()
|
||||
await getConnection().transaction(async (transactionalEntityManager: EntityManager) => {
|
||||
await db.getDataSource().transaction(async (transactionalEntityManager: EntityManager) => {
|
||||
await transactionalEntityManager.save(contribution)
|
||||
if (contributionMessage) {
|
||||
await transactionalEntityManager.save(contributionMessage)
|
||||
@ -271,7 +273,7 @@ export class ContributionResolver {
|
||||
)
|
||||
const { contribution, contributionMessage, createdByUserChangedByModerator } =
|
||||
await updateUnconfirmedContributionContext.run()
|
||||
await getConnection().transaction(async (transactionalEntityManager: EntityManager) => {
|
||||
await db.getDataSource().transaction(async (transactionalEntityManager: EntityManager) => {
|
||||
await transactionalEntityManager.save(contribution)
|
||||
// TODO: move into specialized view or formatting for logging class
|
||||
logger.debug('saved changed contribution', {
|
||||
@ -461,7 +463,7 @@ export class ContributionResolver {
|
||||
)
|
||||
|
||||
const receivedCallDate = new Date()
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { User as DbUser } from 'database'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
@ -12,11 +12,11 @@ import { queryOptIn } from '@/seeds/graphql/queries'
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
CONFIG.EMAIL_CODE_VALID_TIME = 1440
|
||||
@ -33,7 +33,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('EmailOptinCodes', () => {
|
||||
|
||||
@ -24,7 +24,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('KlicktippResolver', () => {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { Transaction as DbTransaction, User as DbUser } from 'database'
|
||||
import { AppDatabase, Transaction as DbTransaction, User as DbUser } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Authorized, FieldResolver, Query, Resolver } from 'type-graphql'
|
||||
import { getConnection } from 'typeorm'
|
||||
|
||||
import { CommunityStatistics, DynamicStatisticsFields } from '@model/CommunityStatistics'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
@Resolver(() => CommunityStatistics)
|
||||
export class StatisticsResolver {
|
||||
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
||||
@ -33,7 +34,7 @@ export class StatisticsResolver {
|
||||
|
||||
@FieldResolver()
|
||||
async totalGradidoCreated(): Promise<Decimal> {
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
try {
|
||||
await queryRunner.connect()
|
||||
const { totalGradidoCreated } = await queryRunner.manager
|
||||
@ -50,7 +51,7 @@ export class StatisticsResolver {
|
||||
|
||||
@FieldResolver()
|
||||
async totalGradidoDecayed(): Promise<Decimal> {
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
try {
|
||||
await queryRunner.connect()
|
||||
const { totalGradidoDecayed } = await queryRunner.manager
|
||||
@ -72,7 +73,7 @@ export class StatisticsResolver {
|
||||
|
||||
const receivedCallDate = new Date()
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
try {
|
||||
await queryRunner.connect()
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||
import { cleanDB, resetEntity, resetToken, testEnvironment } from '@test/helpers'
|
||||
@ -45,11 +45,11 @@ TRANSACTIONS_LOCK.acquire = jest.fn().mockResolvedValue(jest.fn())
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
let user: User
|
||||
@ -66,7 +66,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('TransactionLinkResolver', () => {
|
||||
|
||||
@ -15,6 +15,7 @@ import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
|
||||
import { User } from '@model/User'
|
||||
import { QueryLinkResult } from '@union/QueryLinkResult'
|
||||
import {
|
||||
AppDatabase,
|
||||
Contribution as DbContribution,
|
||||
ContributionLink as DbContributionLink,
|
||||
Transaction as DbTransaction,
|
||||
@ -66,6 +67,7 @@ export const transactionLinkCode = (date: Date): string => {
|
||||
}
|
||||
|
||||
const CODE_VALID_DAYS_DURATION = 14
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
export const transactionLinkExpireDate = (date: Date): Date => {
|
||||
const validUntil = new Date(date)
|
||||
@ -203,7 +205,7 @@ export class TransactionLinkResolver {
|
||||
try {
|
||||
logger.info('redeem contribution link...')
|
||||
const now = new Date()
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
try {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
AppDatabase,
|
||||
Community as DbCommunity,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
Transaction as dbTransaction,
|
||||
@ -7,7 +8,7 @@ import {
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { In, IsNull, getConnection } from 'typeorm'
|
||||
import { In, IsNull } from 'typeorm'
|
||||
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
import { TransactionSendArgs } from '@arg/TransactionSendArgs'
|
||||
@ -49,6 +50,8 @@ import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConn
|
||||
import { storeForeignUser } from './util/storeForeignUser'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
export const executeTransaction = async (
|
||||
amount: Decimal,
|
||||
memo: string,
|
||||
@ -96,7 +99,7 @@ export const executeTransaction = async (
|
||||
throw new LogError('User has not enough GDD or amount is < 0', sendBalance)
|
||||
}
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
logger.debug(`open Transaction to write...`)
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
UserRole,
|
||||
} from 'database'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4, validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
|
||||
import { GmsPublishLocationType } from '@enum/GmsPublishLocationType'
|
||||
@ -99,11 +99,11 @@ let admin: User
|
||||
let user: User
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -117,7 +117,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('UserResolver', () => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
AppDatabase,
|
||||
ContributionLink as DbContributionLink,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
@ -22,7 +23,7 @@ import {
|
||||
Root,
|
||||
} from 'type-graphql'
|
||||
import { IRestResponse } from 'typed-rest-client'
|
||||
import { In, Point, getConnection } from 'typeorm'
|
||||
import { In, Point } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { UserArgs } from '@arg//UserArgs'
|
||||
@ -105,6 +106,7 @@ import { validateAlias } from './util/validateAlias'
|
||||
|
||||
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
|
||||
const DEFAULT_LANGUAGE = 'de'
|
||||
const db = AppDatabase.getInstance()
|
||||
const isLanguage = (language: string): boolean => {
|
||||
return LANGUAGES.includes(language)
|
||||
}
|
||||
@ -394,7 +396,7 @@ export class UserResolver {
|
||||
}
|
||||
}
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
let projectBranding: ProjectBranding | null | undefined
|
||||
@ -566,7 +568,7 @@ export class UserResolver {
|
||||
user.password = await encryptPassword(user, password)
|
||||
logger.debug('User credentials updated ...')
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
|
||||
@ -735,7 +737,7 @@ export class UserResolver {
|
||||
// } catch (err) {
|
||||
// console.log('error:', err)
|
||||
// }
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community as DbCommunity } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, contributionDateFormatter, testEnvironment } from '@test/helpers'
|
||||
@ -25,11 +25,11 @@ import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -41,7 +41,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('semaphore', () => {
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import {
|
||||
AppDatabase,
|
||||
Community as DbCommunity,
|
||||
FederatedCommunity as DbFederatedCommunity,
|
||||
} from 'database'
|
||||
import { FindOneOptions, IsNull, Not } from 'typeorm'
|
||||
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Connection } from '@/typeorm/connection'
|
||||
|
||||
function findWithCommunityIdentifier(communityIdentifier: string): FindOneOptions<DbCommunity> {
|
||||
return {
|
||||
@ -115,14 +118,15 @@ export async function getAllCommunities({
|
||||
pageSize = 25,
|
||||
currentPage = 1,
|
||||
}: Paginated): Promise<DbCommunity[]> {
|
||||
const connection = await Connection.getInstance()
|
||||
if (!connection) {
|
||||
const connection = AppDatabase.getInstance()
|
||||
if (!connection.isConnected()) {
|
||||
throw new LogError('Cannot connect to db')
|
||||
}
|
||||
// foreign: 'ASC',
|
||||
// createdAt: 'DESC',
|
||||
// lastAnnouncedAt: 'DESC',
|
||||
const result = await connection
|
||||
.getDataSource()
|
||||
.getRepository(DbFederatedCommunity)
|
||||
.createQueryBuilder('federatedCommunity')
|
||||
.leftJoinAndSelect('federatedCommunity.community', 'community')
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Contribution, User } from 'database'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, contributionDateFormatter, testEnvironment } from '@test/helpers'
|
||||
|
||||
@ -17,11 +17,11 @@ jest.mock('@/password/EncryptorUtils')
|
||||
CONFIG.HUMHUB_ACTIVE = false
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -33,7 +33,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
const setZeroHours = (date: Date): Date => {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Contribution } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { getConnection } from 'typeorm'
|
||||
|
||||
import { OpenCreation } from '@model/OpenCreation'
|
||||
|
||||
@ -8,6 +7,9 @@ import { FULL_CREATION_AVAILABLE, MAX_CREATION_AMOUNT } from '@/graphql/resolver
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getFirstDayOfPreviousNMonth } from '@/util/utilities'
|
||||
import { AppDatabase } from 'database'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
interface CreationMap {
|
||||
id: number
|
||||
@ -46,7 +48,7 @@ export const getUserCreations = async (
|
||||
const months = getCreationMonths(timezoneOffset)
|
||||
logger.trace('getUserCreations months', months)
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
|
||||
const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day'
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { Contribution as DbContribution } from 'database'
|
||||
import { AppDatabase, Contribution as DbContribution } from 'database'
|
||||
import { Brackets, In, IsNull, LessThanOrEqual, Like, Not, SelectQueryBuilder } from 'typeorm'
|
||||
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
import { SearchContributionsFilterArgs } from '@arg/SearchContributionsFilterArgs'
|
||||
import { Connection } from '@typeorm/connection'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
@ -32,11 +31,14 @@ export const findContributions = async (
|
||||
relations: Relations | undefined = undefined,
|
||||
countOnly = false,
|
||||
): Promise<[DbContribution[], number]> => {
|
||||
const connection = await Connection.getInstance()
|
||||
if (!connection) {
|
||||
const connection = AppDatabase.getInstance()
|
||||
if (!connection.isConnected()) {
|
||||
throw new LogError('Cannot connect to db')
|
||||
}
|
||||
const queryBuilder = connection.getRepository(DbContribution).createQueryBuilder('Contribution')
|
||||
const queryBuilder = connection
|
||||
.getDataSource()
|
||||
.getRepository(DbContribution)
|
||||
.createQueryBuilder('Contribution')
|
||||
if (relations) {
|
||||
joinRelationsRecursive(relations, queryBuilder, 'Contribution')
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community as DbCommunity, User as DbUser } from 'database'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
@ -14,11 +14,11 @@ import { findUserByIdentifier } from './findUserByIdentifier'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -29,7 +29,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('graphql/resolver/util/findUserByIdentifier', () => {
|
||||
|
||||
@ -5,7 +5,7 @@ import { Decimal } from 'decimal.js-light'
|
||||
// import { Response } from 'graphql-request/dist/types'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { Response } from 'graphql-request/dist/types'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
@ -328,11 +328,11 @@ async function createTxReceive1FromSend3(verified: boolean): Promise<Transaction
|
||||
}
|
||||
*/
|
||||
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -343,7 +343,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('create and send Transactions to DltConnector', () => {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import {
|
||||
AppDatabase,
|
||||
Community as DbCommunity,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
User as DbUser,
|
||||
Transaction as dbTransaction,
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { getConnection } from 'typeorm'
|
||||
|
||||
import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState'
|
||||
import { LogError } from '@/server/LogError'
|
||||
@ -15,6 +15,8 @@ import { calculateSenderBalance } from '@/util/calculateSenderBalance'
|
||||
|
||||
import { getLastTransaction } from './getLastTransaction'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
export async function settlePendingSenderTransaction(
|
||||
homeCom: DbCommunity,
|
||||
senderUser: DbUser,
|
||||
@ -23,7 +25,7 @@ export async function settlePendingSenderTransaction(
|
||||
// TODO: synchronisation with TRANSACTION_LOCK of federation-modul necessary!!!
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
logger.debug(`start Transaction for write-access...`)
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { TransactionLink as DbTransactionLink } from 'database'
|
||||
import { AppDatabase, TransactionLink as DbTransactionLink } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { getConnection } from 'typeorm'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
export const transactionLinkSummary = async (
|
||||
userId: number,
|
||||
date: Date,
|
||||
@ -14,7 +15,7 @@ export const transactionLinkSummary = async (
|
||||
firstDate: Date | null
|
||||
transactionLinkcount: number
|
||||
}> => {
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
try {
|
||||
await queryRunner.connect()
|
||||
const { sumHoldAvailableAmount, sumAmount, lastDate, firstDate, count } =
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { User } from 'database'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
@ -10,11 +10,11 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
|
||||
import { validateAlias } from './validateAlias'
|
||||
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -25,7 +25,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('validate alias', () => {
|
||||
|
||||
@ -93,7 +93,7 @@ const run = async () => {
|
||||
}
|
||||
logger.info('##seed## seeding all contributionLinks successful...')
|
||||
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
|
||||
@ -1,29 +1,27 @@
|
||||
import { CONFIG } from '@/config'
|
||||
import { schema } from '@/graphql/schema'
|
||||
import { elopageWebhook } from '@/webhook/elopage'
|
||||
import { gmsWebhook } from '@/webhook/gms'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import express, { Express, json, urlencoded } from 'express'
|
||||
import { slowDown } from 'express-slow-down'
|
||||
import helmet from 'helmet'
|
||||
import { Logger } from 'log4js'
|
||||
import { Connection as DbConnection } from 'typeorm'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { schema } from '@/graphql/schema'
|
||||
import { checkDBVersionUntil } from '@/typeorm/DBVersion'
|
||||
import { elopageWebhook } from '@/webhook/elopage'
|
||||
import { gmsWebhook } from '@/webhook/gms'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { AppDatabase } from 'database'
|
||||
import { context as serverContext } from './context'
|
||||
import { cors } from './cors'
|
||||
import { i18n } from './localization'
|
||||
import { apolloLogger } from './logger'
|
||||
import { plugins } from './plugins'
|
||||
|
||||
// TODO implement
|
||||
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
|
||||
|
||||
interface ServerDef {
|
||||
apollo: ApolloServer
|
||||
app: Express
|
||||
con: DbConnection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
export const createServer = async (
|
||||
@ -37,10 +35,8 @@ export const createServer = async (
|
||||
// open mariadb connection, retry connecting with mariadb
|
||||
// check for correct database version
|
||||
// retry max CONFIG.DB_CONNECT_RETRY_COUNT times, wait CONFIG.DB_CONNECT_RETRY_DELAY ms between tries
|
||||
const con = await checkDBVersionUntil(
|
||||
CONFIG.DB_CONNECT_RETRY_COUNT,
|
||||
CONFIG.DB_CONNECT_RETRY_DELAY_MS,
|
||||
)
|
||||
const db = AppDatabase.getInstance()
|
||||
await db.init()
|
||||
|
||||
// Express Server
|
||||
const app = express()
|
||||
@ -103,5 +99,5 @@ export const createServer = async (
|
||||
)
|
||||
logger.debug('createServer...successful')
|
||||
|
||||
return { apollo, app, con }
|
||||
return { apollo, app, con: db.getDataSource() }
|
||||
}
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
import { Migration } from 'database'
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { Connection } from '@/typeorm/connection'
|
||||
import { Connection as DbConnection } from 'typeorm'
|
||||
|
||||
async function checkDBVersionUntil(maxRetries: number, delayMs: number): Promise<DbConnection> {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
const connection = await Connection.getInstance()
|
||||
if (connection?.isInitialized) {
|
||||
const dbVersion = await checkDBVersion(CONFIG.DB_VERSION)
|
||||
if (dbVersion) {
|
||||
logger.info('Database connection and version check succeeded.')
|
||||
return connection
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn(`Attempt ${attempt}: Waiting for DB...`, err)
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs))
|
||||
}
|
||||
|
||||
logger.fatal(
|
||||
`Fatal: Could not connect to database or version check failed after ${maxRetries} attempts.`,
|
||||
)
|
||||
throw new Error('Fatal: Database not ready.')
|
||||
}
|
||||
|
||||
const getDBVersion = async (): Promise<string | null> => {
|
||||
try {
|
||||
const [dbVersion] = await Migration.find({ order: { version: 'DESC' }, take: 1 })
|
||||
return dbVersion ? dbVersion.fileName : null
|
||||
} catch (error) {
|
||||
logger.error(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const checkDBVersion = async (DB_VERSION: string): Promise<boolean> => {
|
||||
const dbVersion = await getDBVersion()
|
||||
if (!dbVersion?.includes(DB_VERSION)) {
|
||||
logger.error(
|
||||
`Wrong database version detected - the backend requires '${DB_VERSION}' but found '${
|
||||
dbVersion ?? 'None'
|
||||
}`,
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export { checkDBVersion, getDBVersion, checkDBVersionUntil }
|
||||
@ -1,55 +0,0 @@
|
||||
// TODO This is super weird - since the entities are defined in another project they have their own globals.
|
||||
// We cannot use our connection here, but must use the external typeorm installation
|
||||
import { entities } from 'database'
|
||||
import { Connection as DbConnection, FileLogger, createConnection } from 'typeorm'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
export class Connection {
|
||||
private static instance: DbConnection
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static async getInstance(): Promise<DbConnection | null> {
|
||||
if (Connection.instance) {
|
||||
return Connection.instance
|
||||
}
|
||||
try {
|
||||
Connection.instance = await createConnection({
|
||||
name: 'default',
|
||||
type: 'mysql',
|
||||
legacySpatialSupport: false,
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
username: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE,
|
||||
entities,
|
||||
synchronize: false,
|
||||
logging: true,
|
||||
logger: new FileLogger('all', {
|
||||
// workaround to let previous path working, because with esbuild the script root path has changed
|
||||
logPath: (CONFIG.PRODUCTION ? '../' : '') + CONFIG.TYPEORM_LOGGING_RELATIVE_PATH,
|
||||
}),
|
||||
extra: {
|
||||
charset: 'utf8mb4_unicode_ci',
|
||||
},
|
||||
})
|
||||
return Connection.instance
|
||||
} catch (error) {
|
||||
// biome-ignore lint/suspicious/noConsole: maybe logger isn't initialized yet
|
||||
console.log(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
import { Connection } from '@/typeorm/connection'
|
||||
import { AppDatabase } from 'database'
|
||||
|
||||
import { exportEventDataToKlickTipp } from './klicktipp'
|
||||
|
||||
async function executeKlicktipp(): Promise<boolean> {
|
||||
const connection = await Connection.getInstance()
|
||||
if (connection) {
|
||||
const connection = AppDatabase.getInstance()
|
||||
await connection.init()
|
||||
if (connection.isConnected()) {
|
||||
await exportEventDataToKlickTipp()
|
||||
await connection.close()
|
||||
await connection.destroy()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Event as DbEvent } from 'database'
|
||||
import { Connection } from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
|
||||
@ -18,11 +18,11 @@ jest.mock('@/apis/KlicktippController')
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -34,7 +34,7 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('klicktipp', () => {
|
||||
|
||||
36
bun.lock
36
bun.lock
@ -15,7 +15,7 @@
|
||||
},
|
||||
"admin": {
|
||||
"name": "admin",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
@ -84,7 +84,7 @@
|
||||
},
|
||||
"backend": {
|
||||
"name": "backend",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"email-templates": "^10.0.1",
|
||||
@ -148,7 +148,7 @@
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typed-rest-client": "^1.8.11",
|
||||
"typeorm": "^0.3.16",
|
||||
"typeorm": "^0.3.22",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^8.3.2",
|
||||
"workerpool": "^9.2.0",
|
||||
@ -170,7 +170,7 @@
|
||||
},
|
||||
"database": {
|
||||
"name": "database",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"@types/uuid": "^8.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -178,11 +178,13 @@
|
||||
"dotenv": "^10.0.0",
|
||||
"esbuild": "^0.25.2",
|
||||
"geojson": "^0.5.0",
|
||||
"joi-extract-type": "^15.0.8",
|
||||
"log4js": "^6.9.1",
|
||||
"mysql2": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-mysql-migrate": "^1.0.2",
|
||||
"tsx": "^4.19.4",
|
||||
"typeorm": "^0.3.16",
|
||||
"typeorm": "^0.3.22",
|
||||
"uuid": "^8.3.2",
|
||||
"wkx": "^0.5.0",
|
||||
},
|
||||
@ -196,7 +198,7 @@
|
||||
},
|
||||
"dht-node": {
|
||||
"name": "dht-node",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"dht-rpc": "6.18.1",
|
||||
@ -227,7 +229,7 @@
|
||||
},
|
||||
"federation": {
|
||||
"name": "federation",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"sodium-native": "^3.4.1",
|
||||
@ -270,14 +272,14 @@
|
||||
"ts-jest": "27.0.5",
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typeorm": "^0.3.16",
|
||||
"typeorm": "^0.3.22",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "8.3.2",
|
||||
},
|
||||
},
|
||||
"frontend": {
|
||||
"name": "frontend",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"@morev/vue-transitions": "^3.0.2",
|
||||
"@types/leaflet": "^1.9.12",
|
||||
@ -560,10 +562,16 @@
|
||||
|
||||
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
||||
|
||||
"@hapi/address": ["@hapi/address@2.1.4", "", {}, "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ=="],
|
||||
|
||||
"@hapi/boom": ["@hapi/boom@10.0.1", "", { "dependencies": { "@hapi/hoek": "^11.0.2" } }, "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA=="],
|
||||
|
||||
"@hapi/bourne": ["@hapi/bourne@1.3.2", "", {}, "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA=="],
|
||||
|
||||
"@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="],
|
||||
|
||||
"@hapi/joi": ["@hapi/joi@15.1.1", "", { "dependencies": { "@hapi/address": "2.x.x", "@hapi/bourne": "1.x.x", "@hapi/hoek": "8.x.x", "@hapi/topo": "3.x.x" } }, "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ=="],
|
||||
|
||||
"@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="],
|
||||
|
||||
"@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="],
|
||||
@ -922,6 +930,8 @@
|
||||
|
||||
"@types/graceful-fs": ["@types/graceful-fs@4.1.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ=="],
|
||||
|
||||
"@types/hapi__joi": ["@types/hapi__joi@15.0.4", "", { "dependencies": { "@types/hapi__joi": "*" } }, "sha512-VSS6zc7AIOdHVXmqKaGNPYl8eGrMvWi0R5pt3evJL3UdxO8XS28/XAkBXNyLQoymHxhMd4bF3o1U9mZkWDeN8w=="],
|
||||
|
||||
"@types/html-to-text": ["@types/html-to-text@9.0.4", "", {}, "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ=="],
|
||||
|
||||
"@types/http-assert": ["@types/http-assert@1.5.6", "", {}, "sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw=="],
|
||||
@ -2188,6 +2198,8 @@
|
||||
|
||||
"joi": ["joi@17.13.3", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA=="],
|
||||
|
||||
"joi-extract-type": ["joi-extract-type@15.0.8", "", { "dependencies": { "@hapi/joi": "~15", "@types/hapi__joi": "~15" } }, "sha512-Or97aW6QN6YJq0B+x/vYs65+nmcPvYDE7xhlwRl7yHzY+7Z8pVaj0zxjdJlXmIA9zRcbbYQKCGvW+I4g0kUHgA=="],
|
||||
|
||||
"jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
||||
|
||||
"js-beautify": ["js-beautify@1.15.4", "", { "dependencies": { "config-chain": "^1.1.13", "editorconfig": "^1.0.4", "glob": "^10.4.2", "js-cookie": "^3.0.5", "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", "html-beautify": "js/bin/html-beautify.js", "js-beautify": "js/bin/js-beautify.js" } }, "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA=="],
|
||||
@ -3280,6 +3292,8 @@
|
||||
|
||||
"zen-observable-ts": ["zen-observable-ts@1.2.5", "", { "dependencies": { "zen-observable": "0.8.15" } }, "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg=="],
|
||||
|
||||
"zod": ["zod@3.25.55", "", {}, "sha512-219huNnkSLQnLsQ3uaRjXsxMrVm5C9W3OOpEVt2k5tvMKuA8nBSu38e0B//a+he9Iq2dvmk2VyYVlHqiHa4YBA=="],
|
||||
|
||||
"@apollo/protobufjs/@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="],
|
||||
|
||||
"@apollographql/graphql-upload-8-fork/http-errors": ["http-errors@1.8.1", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.1" } }, "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g=="],
|
||||
@ -3302,6 +3316,10 @@
|
||||
|
||||
"@hapi/boom/@hapi/hoek": ["@hapi/hoek@11.0.7", "", {}, "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ=="],
|
||||
|
||||
"@hapi/joi/@hapi/hoek": ["@hapi/hoek@8.5.1", "", {}, "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow=="],
|
||||
|
||||
"@hapi/joi/@hapi/topo": ["@hapi/topo@3.1.6", "", { "dependencies": { "@hapi/hoek": "^8.3.0" } }, "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ=="],
|
||||
|
||||
"@hyperswarm/secret-stream/noise-handshake": ["noise-handshake@4.1.0", "", { "dependencies": { "b4a": "^1.1.0", "nanoassert": "^2.0.0", "sodium-universal": "^5.0.0" } }, "sha512-ZHt2+mOXTvjtaWS2h/JPvQjmknfKrEld2xdSsRYWXnYiJmK/N+dtxrDVSt1cr9wGAlhH7Ek43lIZNsL5bVeX9A=="],
|
||||
|
||||
"@hyperswarm/secret-stream/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="],
|
||||
|
||||
@ -27,8 +27,8 @@
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"joi": "^17.13.3",
|
||||
"esbuild": "^0.25.2"
|
||||
"esbuild": "^0.25.2",
|
||||
"joi": "^17.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
87
config-schema/src/DatabaseConfigSchema.ts
Normal file
87
config-schema/src/DatabaseConfigSchema.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import Joi from 'joi'
|
||||
|
||||
export const DatabaseConfigSchema = Joi.object({
|
||||
DB_CONNECT_RETRY_COUNT: Joi.number()
|
||||
.default(15)
|
||||
.min(1)
|
||||
.max(1000)
|
||||
.description('Number of retries to connect to the database')
|
||||
.optional(),
|
||||
|
||||
DB_CONNECT_RETRY_DELAY_MS: Joi.number()
|
||||
.default(500)
|
||||
.min(100)
|
||||
.max(10000)
|
||||
.description('Delay in milliseconds between retries to connect to the database')
|
||||
.optional(),
|
||||
|
||||
TYPEORM_LOGGING_RELATIVE_PATH: Joi.string()
|
||||
.pattern(/^[a-zA-Z0-9-_\.\/]+\.log$/)
|
||||
.message('TYPEORM_LOGGING_RELATIVE_PATH must be a valid filename ending with .log')
|
||||
.description('log file name for logging typeorm activities')
|
||||
.default('typeorm.log')
|
||||
.optional(),
|
||||
|
||||
DB_HOST: Joi.string()
|
||||
.hostname()
|
||||
.message('must be a valid host with alphanumeric characters, numbers, points and -')
|
||||
.description("database host like 'localhost' or 'mariadb' in docker setup")
|
||||
.default('localhost')
|
||||
.optional(),
|
||||
|
||||
DB_LOGGING_ACTIVE: Joi.boolean()
|
||||
.default(false)
|
||||
.description('Enable sql query logging, only for debug, because produce many log entries')
|
||||
.optional(),
|
||||
|
||||
DB_LOG_LEVEL: Joi.string()
|
||||
.valid('all', 'query', 'schema', 'error', 'warn', 'info', 'log', 'migration')
|
||||
.description('set log level')
|
||||
.default('info')
|
||||
.optional(),
|
||||
|
||||
DB_PORT: Joi.number()
|
||||
.integer()
|
||||
.min(1024)
|
||||
.max(49151)
|
||||
.description('database port, default: 3306')
|
||||
.default(3306)
|
||||
.optional(),
|
||||
|
||||
DB_USER: Joi.string()
|
||||
.pattern(/^[A-Za-z0-9]([A-Za-z0-9-_\.]*[A-Za-z0-9])?$/) // Validates MariaDB username rules
|
||||
.min(1) // Minimum length 1
|
||||
.max(16) // Maximum length 16
|
||||
.message(
|
||||
'Valid database username (letters, numbers, hyphens, underscores, dots allowed; no spaces, must not start or end with hyphen, dot, or underscore)',
|
||||
)
|
||||
.description('database username for mariadb')
|
||||
.default('root')
|
||||
.optional(),
|
||||
|
||||
DB_PASSWORD: Joi.string()
|
||||
.when(Joi.ref('NODE_ENV'), {
|
||||
is: 'development',
|
||||
then: Joi.string().allow(''),
|
||||
otherwise: Joi.string()
|
||||
.min(8)
|
||||
.max(32)
|
||||
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).+$/)
|
||||
.message(
|
||||
'Password must be between 8 and 32 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character (e.g., !@#$%^&*).',
|
||||
),
|
||||
})
|
||||
.description(
|
||||
'Password for the database user. In development mode, an empty password is allowed. In other environments, a complex password is required.',
|
||||
)
|
||||
.default('')
|
||||
.optional(),
|
||||
|
||||
DB_DATABASE: Joi.string()
|
||||
.pattern(/^[a-zA-Z][a-zA-Z0-9_-]{1,63}$/)
|
||||
.description(
|
||||
'Database name like gradido_community (must start with a letter, and can only contain letters, numbers, underscores, or dashes)',
|
||||
)
|
||||
.default('gradido_community')
|
||||
.optional(),
|
||||
})
|
||||
@ -27,30 +27,6 @@ export const DECAY_START_TIME = Joi.date()
|
||||
.default(new Date('2021-05-13T17:46:31Z')) // default to the specified date if not provided
|
||||
.required()
|
||||
|
||||
export const DB_VERSION = Joi.string()
|
||||
.pattern(/^\d{4}-[a-z0-9-_]+$/)
|
||||
.message(
|
||||
'DB_VERSION must be in the format: YYYY-description, e.g. "0087-add_index_on_user_roles".',
|
||||
)
|
||||
.description(
|
||||
'db version string, last migration file name without ending or last folder in entity',
|
||||
)
|
||||
.required()
|
||||
|
||||
export const DB_CONNECT_RETRY_COUNT = Joi.number()
|
||||
.default(15)
|
||||
.min(1)
|
||||
.max(1000)
|
||||
.description('Number of retries to connect to the database')
|
||||
.optional()
|
||||
|
||||
export const DB_CONNECT_RETRY_DELAY_MS = Joi.number()
|
||||
.default(500)
|
||||
.min(100)
|
||||
.max(10000)
|
||||
.description('Delay in milliseconds between retries to connect to the database')
|
||||
.optional()
|
||||
|
||||
export const COMMUNITY_URL = Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.custom((value: string, helpers: Joi.CustomHelpers<string>) => {
|
||||
@ -177,65 +153,6 @@ export const OPENAI_ACTIVE = Joi.boolean()
|
||||
.description('Flag to enable or disable OpenAI API')
|
||||
.required()
|
||||
|
||||
export const TYPEORM_LOGGING_RELATIVE_PATH = Joi.string()
|
||||
.pattern(/^[a-zA-Z0-9-_\.\/]+\.log$/)
|
||||
.message('TYPEORM_LOGGING_RELATIVE_PATH must be a valid filename ending with .log')
|
||||
.description('log file name for logging typeorm activities')
|
||||
.default('typeorm.log')
|
||||
.required()
|
||||
|
||||
export const DB_HOST = Joi.string()
|
||||
.hostname()
|
||||
.message('must be a valid host with alphanumeric characters, numbers, points and -')
|
||||
.description("database host like 'localhost' or 'mariadb' in docker setup")
|
||||
.default('localhost')
|
||||
.required()
|
||||
|
||||
export const DB_PORT = Joi.number()
|
||||
.integer()
|
||||
.min(1024)
|
||||
.max(49151)
|
||||
.description('database port, default: 3306')
|
||||
.default(3306)
|
||||
.required()
|
||||
|
||||
export const DB_USER = Joi.string()
|
||||
.pattern(/^[A-Za-z0-9]([A-Za-z0-9-_\.]*[A-Za-z0-9])?$/) // Validates MariaDB username rules
|
||||
.min(1) // Minimum length 1
|
||||
.max(16) // Maximum length 16
|
||||
.message(
|
||||
'Valid database username (letters, numbers, hyphens, underscores, dots allowed; no spaces, must not start or end with hyphen, dot, or underscore)',
|
||||
)
|
||||
.description('database username for mariadb')
|
||||
.default('root')
|
||||
.required()
|
||||
|
||||
export const DB_PASSWORD = Joi.string()
|
||||
.when(Joi.ref('NODE_ENV'), {
|
||||
is: 'development',
|
||||
then: Joi.string().allow(''),
|
||||
otherwise: Joi.string()
|
||||
.min(8)
|
||||
.max(32)
|
||||
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).+$/)
|
||||
.message(
|
||||
'Password must be between 8 and 32 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character (e.g., !@#$%^&*).',
|
||||
),
|
||||
})
|
||||
.description(
|
||||
'Password for the database user. In development mode, an empty password is allowed. In other environments, a complex password is required.',
|
||||
)
|
||||
.default('')
|
||||
.required()
|
||||
|
||||
export const DB_DATABASE = Joi.string()
|
||||
.pattern(/^[a-zA-Z][a-zA-Z0-9_-]{1,63}$/)
|
||||
.description(
|
||||
'Database name like gradido_community (must start with a letter, and can only contain letters, numbers, underscores, or dashes)',
|
||||
)
|
||||
.default('gradido_community')
|
||||
.required()
|
||||
|
||||
export const APP_VERSION = Joi.string()
|
||||
.pattern(/^\d+\.\d+\.\d+$/)
|
||||
.message('Version must be in the format "major.minor.patch" (e.g., "2.4.1")')
|
||||
|
||||
@ -1,35 +1,3 @@
|
||||
import { ObjectSchema } from 'joi'
|
||||
export * from './commonSchema'
|
||||
|
||||
export function validate(schema: ObjectSchema, data: any) {
|
||||
const { error } = schema.validate(data)
|
||||
const schemaJson = schema.describe()
|
||||
if (error) {
|
||||
error.details.forEach((err) => {
|
||||
const details = JSON.stringify(err, null, 2)
|
||||
if (!err.context) {
|
||||
throw new Error('missing context in config validation with joi: ' + details)
|
||||
}
|
||||
if (!schemaJson) {
|
||||
throw new Error('invalid schema in config validation with joi: ' + details)
|
||||
}
|
||||
const key = err.context.key
|
||||
if (key === undefined) {
|
||||
throw new Error('missing key in config validation with joi: ' + details)
|
||||
}
|
||||
const value = err.context.value
|
||||
const description = schemaJson.keys[key]
|
||||
? schema.describe().keys[key].flags.description
|
||||
: 'No description available'
|
||||
if (data[key] === undefined) {
|
||||
throw new Error(
|
||||
`Environment Variable '${key}' is missing. ${description}, details: ${details}`,
|
||||
)
|
||||
} else {
|
||||
throw new Error(
|
||||
`Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
export { DatabaseConfigSchema } from './DatabaseConfigSchema'
|
||||
export { validate } from './validate'
|
||||
|
||||
34
config-schema/src/validate.ts
Normal file
34
config-schema/src/validate.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { ObjectSchema } from 'joi'
|
||||
|
||||
export function validate(schema: ObjectSchema, data: any) {
|
||||
const { error } = schema.validate(data)
|
||||
const schemaJson = schema.describe()
|
||||
if (error) {
|
||||
error.details.forEach((err) => {
|
||||
const details = JSON.stringify(err, null, 2)
|
||||
if (!err.context) {
|
||||
throw new Error('missing context in config validation with joi: ' + details)
|
||||
}
|
||||
if (!schemaJson) {
|
||||
throw new Error('invalid schema in config validation with joi: ' + details)
|
||||
}
|
||||
const key = err.context.key
|
||||
if (key === undefined) {
|
||||
throw new Error('missing key in config validation with joi: ' + details)
|
||||
}
|
||||
const value = err.context.value
|
||||
const description = schemaJson.keys[key]
|
||||
? schema.describe().keys[key].flags.description
|
||||
: 'No description available'
|
||||
if (data[key] === undefined) {
|
||||
throw new Error(
|
||||
`Environment Variable '${key}' is missing. ${description}, details: ${details}`,
|
||||
)
|
||||
} else {
|
||||
throw new Error(
|
||||
`Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
import { build } from 'esbuild'
|
||||
import fs from 'node:fs'
|
||||
import { latestDbVersion } from './src/config/detectLastDBVersion'
|
||||
import { latestDbVersion } from './src/detectLastDBVersion'
|
||||
|
||||
build({
|
||||
entryPoints: ['entity/index.ts'],
|
||||
entryPoints: ['src/index.ts'],
|
||||
bundle: true,
|
||||
target: 'node18.20.7',
|
||||
platform: 'node',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Connection } from 'mysql2/promise'
|
||||
import { CONFIG } from './config'
|
||||
import { CONFIG } from '../src/config'
|
||||
import { connectToDatabaseServer } from './prepare'
|
||||
import { MIGRATIONS_TABLE } from '../src/config/const'
|
||||
|
||||
export async function truncateTables(connection: Connection) {
|
||||
const [tables] = await connection.query('SHOW TABLES')
|
||||
@ -16,7 +17,7 @@ export async function truncateTables(connection: Connection) {
|
||||
|
||||
// Truncating all tables...
|
||||
for (const tableName of tableNames) {
|
||||
if (tableName === CONFIG.MIGRATIONS_TABLE) {
|
||||
if (tableName === MIGRATIONS_TABLE) {
|
||||
continue
|
||||
}
|
||||
await connection.query(`TRUNCATE TABLE \`${tableName}\``)
|
||||
103
database/migration/index.ts
Normal file
103
database/migration/index.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { CONFIG } from '../src/config'
|
||||
import { DatabaseState, getDatabaseState } from './prepare'
|
||||
|
||||
import path from 'node:path'
|
||||
import { createPool } from 'mysql'
|
||||
import { Migration } from 'ts-mysql-migrate'
|
||||
import { clearDatabase } from './clear'
|
||||
import { latestDbVersion } from '../src/detectLastDBVersion'
|
||||
import { MIGRATIONS_TABLE } from '../src/config/const'
|
||||
|
||||
const run = async (command: string) => {
|
||||
if (command === 'clear') {
|
||||
if (CONFIG.NODE_ENV === 'production') {
|
||||
throw new Error('Clearing database in production is not allowed')
|
||||
}
|
||||
await clearDatabase()
|
||||
return
|
||||
}
|
||||
// Database actions not supported by our migration library
|
||||
// await createDatabase()
|
||||
const state = await getDatabaseState()
|
||||
if (state === DatabaseState.NOT_CONNECTED) {
|
||||
throw new Error(
|
||||
`Database not connected, is database server running?
|
||||
host: ${CONFIG.DB_HOST}
|
||||
port: ${CONFIG.DB_PORT}
|
||||
user: ${CONFIG.DB_USER}
|
||||
password: ${CONFIG.DB_PASSWORD.slice(-2)}
|
||||
database: ${CONFIG.DB_DATABASE}`,
|
||||
)
|
||||
}
|
||||
if (state === DatabaseState.HIGHER_VERSION) {
|
||||
throw new Error('Database version is higher than required, please switch to the correct branch')
|
||||
}
|
||||
if (state === DatabaseState.SAME_VERSION) {
|
||||
if (command === 'up') {
|
||||
// biome-ignore lint/suspicious/noConsole: no logger present
|
||||
console.log('Database is up to date')
|
||||
return
|
||||
}
|
||||
}
|
||||
// Initialize Migrations
|
||||
const pool = createPool({
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
user: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE,
|
||||
})
|
||||
const migration = new Migration({
|
||||
conn: pool,
|
||||
tableName: MIGRATIONS_TABLE,
|
||||
silent: true,
|
||||
dir: path.join(__dirname, 'migrations'),
|
||||
})
|
||||
await migration.initialize()
|
||||
|
||||
// Execute command
|
||||
switch (command) {
|
||||
case 'up':
|
||||
await migration.up() // use for upgrade script
|
||||
break
|
||||
case 'down':
|
||||
await migration.down() // use for downgrade script
|
||||
break
|
||||
case 'reset':
|
||||
if (CONFIG.NODE_ENV === 'production') {
|
||||
throw new Error('Resetting database in production is not allowed')
|
||||
}
|
||||
await migration.reset()
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported command ${command}`)
|
||||
}
|
||||
if (command === 'reset') {
|
||||
// biome-ignore lint/suspicious/noConsole: no logger present
|
||||
console.log('Database was reset')
|
||||
} else {
|
||||
const currentDbVersion = await migration.getLastVersion()
|
||||
// biome-ignore lint/suspicious/noConsole: no logger present
|
||||
console.log(`Database was ${command} migrated to version: ${currentDbVersion.fileName}`)
|
||||
if (latestDbVersion === currentDbVersion.fileName.split('.')[0]) {
|
||||
// biome-ignore lint/suspicious/noConsole: no logger present
|
||||
console.log('Database is now up to date')
|
||||
} else {
|
||||
// biome-ignore lint/suspicious/noConsole: no logger present
|
||||
console.log('The latest database version is: ', latestDbVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate connections gracefully
|
||||
pool.end()
|
||||
}
|
||||
|
||||
run(process.argv[2])
|
||||
.catch((err) => {
|
||||
// biome-ignore lint/suspicious/noConsole: no logger present
|
||||
console.log(err)
|
||||
process.exit(1)
|
||||
})
|
||||
.then(() => {
|
||||
process.exit()
|
||||
})
|
||||
@ -11,15 +11,15 @@ import path from 'path'
|
||||
const TARGET_MNEMONIC_TYPE = 2
|
||||
const PHRASE_WORD_COUNT = 24
|
||||
const WORDS_MNEMONIC_0 = fs
|
||||
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer18112.txt'))
|
||||
.readFileSync(path.resolve(__dirname, '../../src/config/mnemonic.uncompressed_buffer18112.txt'))
|
||||
.toString()
|
||||
.split(',')
|
||||
const WORDS_MNEMONIC_1 = fs
|
||||
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer18113.txt'))
|
||||
.readFileSync(path.resolve(__dirname, '../../src/config/mnemonic.uncompressed_buffer18113.txt'))
|
||||
.toString()
|
||||
.split(',')
|
||||
const WORDS_MNEMONIC_2 = fs
|
||||
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer13116.txt'))
|
||||
.readFileSync(path.resolve(__dirname, '../../src/config/mnemonic.uncompressed_buffer13116.txt'))
|
||||
.toString()
|
||||
.split(',')
|
||||
const WORDS_MNEMONIC = [WORDS_MNEMONIC_0, WORDS_MNEMONIC_1, WORDS_MNEMONIC_2]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user