From d6dbc50c925c6ee2015fca43683848d5fb39542d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 13:46:09 +0200 Subject: [PATCH 1/7] check db version with typeORM --- backend/src/database/connection.ts | 18 -------------- backend/src/index.ts | 33 +++++++++++-------------- backend/src/typeorm/entity/Migration.ts | 21 ++++++++++++++++ backend/src/typeorm/getDBVersion.ts | 15 +++++++++++ 4 files changed, 51 insertions(+), 36 deletions(-) delete mode 100644 backend/src/database/connection.ts create mode 100644 backend/src/typeorm/entity/Migration.ts create mode 100644 backend/src/typeorm/getDBVersion.ts diff --git a/backend/src/database/connection.ts b/backend/src/database/connection.ts deleted file mode 100644 index 584b657d2..000000000 --- a/backend/src/database/connection.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createConnection, Connection } from 'mysql2/promise' -import CONFIG from '../config' - -const connection = async (): Promise => { - const con = await createConnection({ - host: CONFIG.DB_HOST, - port: CONFIG.DB_PORT, - user: CONFIG.DB_USER, - password: CONFIG.DB_PASSWORD, - database: CONFIG.DB_DATABASE, - }) - - await con.connect() - - return con -} - -export default connection diff --git a/backend/src/index.ts b/backend/src/index.ts index bd58f8c86..36442a4ca 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -5,12 +5,12 @@ import express from 'express' import cors from 'cors' import { buildSchema } from 'type-graphql' import { ApolloServer } from 'apollo-server-express' -import { RowDataPacket } from 'mysql2/promise' -import connection from './database/connection' -import typeOrmConnection from './typeorm/connection' import CONFIG from './config' +import connection from './typeorm/connection' +import getDBVersion from './typeorm/getDBVersion' + // TODO move to extern import { UserResolver } from './graphql/resolvers/UserResolver' import { BalanceResolver } from './graphql/resolvers/BalanceResolver' @@ -39,20 +39,20 @@ const context = (args: any) => { } async function main() { - // check for correct database version + // open mysql connection const con = await connection() - const [rows] = await con.query(`SELECT * FROM migrations ORDER BY version DESC LIMIT 1;`) - if ( - (rows).length === 0 || - !(rows)[0].fileName || - (rows)[0].fileName.indexOf(DB_VERSION) === -1 - ) { - throw new Error(`Wrong database version - the backend requires '${DB_VERSION}'`) + if (!con.isConnected) { + throw new Error(`Couldn't open connection to database`) } - const toCon = await typeOrmConnection() - if (!toCon.isConnected) { - throw new Error(`Couldn't open typeorm db connection`) + // check for correct database version + const dbVersion = await getDBVersion() + if (!dbVersion || dbVersion.indexOf(DB_VERSION) === -1) { + throw new Error( + `Wrong database version - the backend requires '${DB_VERSION}' but found '${ + dbVersion || 'None' + }'`, + ) } const schema = await buildSchema({ @@ -61,10 +61,7 @@ async function main() { }) // Graphiql interface - let playground = false - if (CONFIG.GRAPHIQL) { - playground = true - } + const playground = CONFIG.GRAPHIQL // Express Server const server = express() diff --git a/backend/src/typeorm/entity/Migration.ts b/backend/src/typeorm/entity/Migration.ts new file mode 100644 index 000000000..fbeeca14d --- /dev/null +++ b/backend/src/typeorm/entity/Migration.ts @@ -0,0 +1,21 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' + +@Entity('migrations') +export class Migration extends BaseEntity { + @PrimaryGeneratedColumn() // This is actually not a primary column + version: number + + @Column({ length: 256, nullable: true, default: null }) + fileName: string + + @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) + date: Date + + /* + static findByUser(userId: number): Promise { + return this.createQueryBuilder('balance') + .where('balance.userId = :userId', { userId }) + .getOneOrFail() + } + */ +} diff --git a/backend/src/typeorm/getDBVersion.ts b/backend/src/typeorm/getDBVersion.ts new file mode 100644 index 000000000..497a6da5e --- /dev/null +++ b/backend/src/typeorm/getDBVersion.ts @@ -0,0 +1,15 @@ +import { getConnection } from 'typeorm' +import { Migration } from './entity/Migration' + +const getDBVersion = async (): Promise => { + const connection = getConnection() + const migrations = connection.getRepository(Migration) + try { + const dbVersion = await migrations.findOne({ order: { version: 'DESC' } }) + return dbVersion ? dbVersion.fileName : null + } catch (error) { + return null + } +} + +export default getDBVersion From 91849b5130066efbe52bb2e1f503d8ae5c95d626 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 14:02:01 +0200 Subject: [PATCH 2/7] externalize resolver import --- backend/src/graphql/resolvers/index.ts | 19 +++++++++++++++++++ backend/src/index.ts | 13 ++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 backend/src/graphql/resolvers/index.ts diff --git a/backend/src/graphql/resolvers/index.ts b/backend/src/graphql/resolvers/index.ts new file mode 100644 index 000000000..f42829645 --- /dev/null +++ b/backend/src/graphql/resolvers/index.ts @@ -0,0 +1,19 @@ +import { UserResolver } from './UserResolver' +import { BalanceResolver } from './BalanceResolver' +import { GdtResolver } from './GdtResolver' +import { TransactionResolver } from './TransactionResolver' +import { KlicktippResolver } from './KlicktippResolver' +import { NonEmptyArray } from 'type-graphql' + +export { UserResolver, BalanceResolver, GdtResolver, TransactionResolver, KlicktippResolver } + +// eslint-disable-next-line @typescript-eslint/ban-types +const resolvers = (): NonEmptyArray => [ + UserResolver, + BalanceResolver, + GdtResolver, + TransactionResolver, + KlicktippResolver, +] + +export default resolvers diff --git a/backend/src/index.ts b/backend/src/index.ts index 36442a4ca..679221691 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -6,18 +6,17 @@ import cors from 'cors' import { buildSchema } from 'type-graphql' import { ApolloServer } from 'apollo-server-express' +// config import CONFIG from './config' +// database import connection from './typeorm/connection' import getDBVersion from './typeorm/getDBVersion' -// TODO move to extern -import { UserResolver } from './graphql/resolvers/UserResolver' -import { BalanceResolver } from './graphql/resolvers/BalanceResolver' -import { GdtResolver } from './graphql/resolvers/GdtResolver' -import { TransactionResolver } from './graphql/resolvers/TransactionResolver' -import { KlicktippResolver } from './graphql/resolvers/KlicktippResolver' +// graphql +import resolvers from './graphql/resolvers' +// auth import { isAuthorized } from './auth/auth' // TODO implement @@ -56,7 +55,7 @@ async function main() { } const schema = await buildSchema({ - resolvers: [UserResolver, BalanceResolver, TransactionResolver, GdtResolver, KlicktippResolver], + resolvers: resolvers(), authChecker: isAuthorized, }) From 4601c3df345d4c9e0b33108f36387814a3e9e603 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 14:10:54 +0200 Subject: [PATCH 3/7] build schema in graphql folder not in index --- backend/src/graphql/index.ts | 14 ++++++++++++++ backend/src/index.ts | 21 +++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 backend/src/graphql/index.ts diff --git a/backend/src/graphql/index.ts b/backend/src/graphql/index.ts new file mode 100644 index 000000000..a36d1dcdf --- /dev/null +++ b/backend/src/graphql/index.ts @@ -0,0 +1,14 @@ +import { GraphQLSchema } from 'graphql' +import { buildSchema } from 'type-graphql' + +import resolvers from './resolvers' +import { isAuthorized } from '../auth/auth' + +const schema = async (): Promise => { + return buildSchema({ + resolvers: resolvers(), + authChecker: isAuthorized, + }) +} + +export { schema } diff --git a/backend/src/index.ts b/backend/src/index.ts index 679221691..c6121aeee 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -3,7 +3,6 @@ import 'reflect-metadata' import express from 'express' import cors from 'cors' -import { buildSchema } from 'type-graphql' import { ApolloServer } from 'apollo-server-express' // config @@ -14,10 +13,7 @@ import connection from './typeorm/connection' import getDBVersion from './typeorm/getDBVersion' // graphql -import resolvers from './graphql/resolvers' - -// auth -import { isAuthorized } from './auth/auth' +import { schema } from './graphql' // TODO implement // import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity"; @@ -54,14 +50,6 @@ async function main() { ) } - const schema = await buildSchema({ - resolvers: resolvers(), - authChecker: isAuthorized, - }) - - // Graphiql interface - const playground = CONFIG.GRAPHIQL - // Express Server const server = express() @@ -89,7 +77,12 @@ async function main() { ] // Apollo Server - const apollo = new ApolloServer({ schema, playground, context, plugins }) + const apollo = new ApolloServer({ + schema: await schema(), + playground: CONFIG.GRAPHIQL, + context, + plugins, + }) apollo.applyMiddleware({ app: server }) // Start Server From 255244fbecdc7f9173381e36c36b6c66f2149f0b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 14:21:14 +0200 Subject: [PATCH 4/7] have schema extern have cors extern have plugins exter have context extern --- backend/src/graphql/{index.ts => schema.ts} | 2 +- backend/src/index.ts | 45 ++++----------------- backend/src/server/context.ts | 14 +++++++ backend/src/server/cors.ts | 8 ++++ backend/src/server/plugins.ts | 17 ++++++++ 5 files changed, 48 insertions(+), 38 deletions(-) rename backend/src/graphql/{index.ts => schema.ts} (93%) create mode 100644 backend/src/server/context.ts create mode 100644 backend/src/server/cors.ts create mode 100644 backend/src/server/plugins.ts diff --git a/backend/src/graphql/index.ts b/backend/src/graphql/schema.ts similarity index 93% rename from backend/src/graphql/index.ts rename to backend/src/graphql/schema.ts index a36d1dcdf..f18a3bea6 100644 --- a/backend/src/graphql/index.ts +++ b/backend/src/graphql/schema.ts @@ -11,4 +11,4 @@ const schema = async (): Promise => { }) } -export { schema } +export default schema diff --git a/backend/src/index.ts b/backend/src/index.ts index c6121aeee..c138deba7 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,7 +2,6 @@ import 'reflect-metadata' import express from 'express' -import cors from 'cors' import { ApolloServer } from 'apollo-server-express' // config @@ -12,27 +11,19 @@ import CONFIG from './config' import connection from './typeorm/connection' import getDBVersion from './typeorm/getDBVersion' +// server +import cors from './server/cors' +import context from './server/context' +import plugins from './server/plugins' + // graphql -import { schema } from './graphql' +import schema from './graphql/schema' // TODO implement // import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity"; const DB_VERSION = '0001-init_db' -const context = (args: any) => { - const authorization = args.req.headers.authorization - let token = null - if (authorization) { - token = authorization.replace(/^Bearer /, '') - } - const context = { - token, - setHeaders: [], - } - return context -} - async function main() { // open mysql connection const con = await connection() @@ -53,28 +44,8 @@ async function main() { // Express Server const server = express() - const corsOptions = { - origin: '*', - exposedHeaders: ['token'], - } - - server.use(cors(corsOptions)) - - const plugins = [ - { - requestDidStart() { - return { - willSendResponse(requestContext: any) { - const { setHeaders = [] } = requestContext.context - setHeaders.forEach(({ key, value }: { [key: string]: string }) => { - requestContext.response.http.headers.append(key, value) - }) - return requestContext - }, - } - }, - }, - ] + // cors + server.use(cors) // Apollo Server const apollo = new ApolloServer({ diff --git a/backend/src/server/context.ts b/backend/src/server/context.ts new file mode 100644 index 000000000..2ad4b520d --- /dev/null +++ b/backend/src/server/context.ts @@ -0,0 +1,14 @@ +const context = (args: any) => { + const authorization = args.req.headers.authorization + let token = null + if (authorization) { + token = authorization.replace(/^Bearer /, '') + } + const context = { + token, + setHeaders: [], + } + return context +} + +export default context diff --git a/backend/src/server/cors.ts b/backend/src/server/cors.ts new file mode 100644 index 000000000..e76ed1591 --- /dev/null +++ b/backend/src/server/cors.ts @@ -0,0 +1,8 @@ +import cors from 'cors' + +const corsOptions = { + origin: '*', + exposedHeaders: ['token'], +} + +export default cors(corsOptions) diff --git a/backend/src/server/plugins.ts b/backend/src/server/plugins.ts new file mode 100644 index 000000000..6b27d19ea --- /dev/null +++ b/backend/src/server/plugins.ts @@ -0,0 +1,17 @@ +const plugins = [ + { + requestDidStart() { + return { + willSendResponse(requestContext: any) { + const { setHeaders = [] } = requestContext.context + setHeaders.forEach(({ key, value }: { [key: string]: string }) => { + requestContext.response.http.headers.append(key, value) + }) + return requestContext + }, + } + }, + }, +] + +export default plugins From 8326eaac95f0f231bfa22e0cdf92422d63f06a72 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 14:32:33 +0200 Subject: [PATCH 5/7] removed dead code --- backend/src/typeorm/entity/Migration.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/backend/src/typeorm/entity/Migration.ts b/backend/src/typeorm/entity/Migration.ts index fbeeca14d..f1163cfbc 100644 --- a/backend/src/typeorm/entity/Migration.ts +++ b/backend/src/typeorm/entity/Migration.ts @@ -10,12 +10,4 @@ export class Migration extends BaseEntity { @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) date: Date - - /* - static findByUser(userId: number): Promise { - return this.createQueryBuilder('balance') - .where('balance.userId = :userId', { userId }) - .getOneOrFail() - } - */ } From 66324fe1ca35d9f1760e8245d8f426600f3f5964 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 14:42:07 +0200 Subject: [PATCH 6/7] fix connection error for case database not running --- backend/src/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index c138deba7..18ac9d9c1 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -26,8 +26,11 @@ const DB_VERSION = '0001-init_db' async function main() { // open mysql connection - const con = await connection() - if (!con.isConnected) { + let con = null + try { + con = await connection() + } catch (error) {} + if (!con || !con.isConnected) { throw new Error(`Couldn't open connection to database`) } From e092af22e3f2edd51039353ee98e182daea48d12 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 24 Sep 2021 14:44:18 +0200 Subject: [PATCH 7/7] removed some logic from index --- backend/src/index.ts | 5 +---- backend/src/typeorm/connection.ts | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 18ac9d9c1..1c3814096 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -26,10 +26,7 @@ const DB_VERSION = '0001-init_db' async function main() { // open mysql connection - let con = null - try { - con = await connection() - } catch (error) {} + const con = await connection() if (!con || !con.isConnected) { throw new Error(`Couldn't open connection to database`) } diff --git a/backend/src/typeorm/connection.ts b/backend/src/typeorm/connection.ts index b108c5ef5..db7dbcb29 100644 --- a/backend/src/typeorm/connection.ts +++ b/backend/src/typeorm/connection.ts @@ -2,18 +2,21 @@ import { createConnection, Connection } from 'typeorm' import CONFIG from '../config' import path from 'path' -const connection = async (): Promise => { - const con = await createConnection({ - name: 'default', - type: 'mysql', - host: CONFIG.DB_HOST, - port: CONFIG.DB_PORT, - username: CONFIG.DB_USER, - password: CONFIG.DB_PASSWORD, - database: CONFIG.DB_DATABASE, - entities: [path.join(__dirname, 'entity', '*.ts')], - synchronize: false, - }) +const connection = async (): Promise => { + let con = null + try { + con = await createConnection({ + name: 'default', + type: 'mysql', + host: CONFIG.DB_HOST, + port: CONFIG.DB_PORT, + username: CONFIG.DB_USER, + password: CONFIG.DB_PASSWORD, + database: CONFIG.DB_DATABASE, + entities: [path.join(__dirname, 'entity', '*.ts')], + synchronize: false, + }) + } catch (error) {} return con }