Merge branch 'master' into admin_user_search_optimization

This commit is contained in:
Moriz Wahl 2022-02-15 14:13:33 +01:00 committed by GitHub
commit 488a113ae7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 196 additions and 37 deletions

View File

@ -4,8 +4,25 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [1.6.4](https://github.com/gradido/gradido/compare/1.6.3...1.6.4)
- fix: Admin Email Confirmation Date and Time [`#1448`](https://github.com/gradido/gradido/pull/1448)
- Fix: Do not log password or token to the console [`#1477`](https://github.com/gradido/gradido/pull/1477)
- Fix: Elopage Hook Crash [`#1474`](https://github.com/gradido/gradido/pull/1474)
- 538 unify all buttons [`#1455`](https://github.com/gradido/gradido/pull/1455)
- 833 old error is shown for a second even if transaction is successful [`#1460`](https://github.com/gradido/gradido/pull/1460)
- fix: Wrong Email Spelling in German [`#1446`](https://github.com/gradido/gradido/pull/1446)
- fix: Redirect to Login after Register [`#1445`](https://github.com/gradido/gradido/pull/1445)
- refactor: Split User Table Component in Admin Interface [`#1443`](https://github.com/gradido/gradido/pull/1443)
- remove user table [`c6a469e`](https://github.com/gradido/gradido/commit/c6a469e08f16101c8fb78958eda69b163b815ed3)
- open creations table [`9ef575d`](https://github.com/gradido/gradido/commit/9ef575d6b140a4c768e4330a18eaa3b04346b483)
- User Search Table [`ae2d535`](https://github.com/gradido/gradido/commit/ae2d5355d62f525187dd1cdb1448aec63fb05d3f)
#### [1.6.3](https://github.com/gradido/gradido/compare/1.6.2...1.6.3)
> 9 February 2022
- v1.6.3 [`#1447`](https://github.com/gradido/gradido/pull/1447)
- add .btn-outline-secondary in scss [`#1442`](https://github.com/gradido/gradido/pull/1442)
- Profil settings and footer refactor [`#1440`](https://github.com/gradido/gradido/pull/1440)
- profil settings header info refactor style [`cbaa016`](https://github.com/gradido/gradido/commit/cbaa0162b9366e5de722235aeb633908c59bb3e1)

View File

@ -3,7 +3,7 @@
"description": "Administraion Interface for Gradido",
"main": "index.js",
"author": "Moriz Wahl",
"version": "1.6.3",
"version": "1.6.4",
"license": "MIT",
"private": false,
"scripts": {

View File

@ -1,7 +1,7 @@
<template>
<div class="component-confirm-register-mail">
<div class="shadow p-3 mb-5 bg-white rounded">
<div v-if="checked">{{ $t('unregister_mail.text_true', { date: dateLastSend }) }}</div>
<div v-if="checked">{{ $t('unregister_mail.text_true') }}</div>
<div v-else>
{{ $t('unregister_mail.text_false', { date: dateLastSend, mail: email }) }}

View File

@ -63,7 +63,11 @@
<confirm-register-mail-formular
:checked="row.item.emailChecked"
:email="row.item.email"
:dateLastSend="$d(new Date(), 'long')"
:dateLastSend="
row.item.emailConfirmationSend
? $d(new Date(row.item.emailConfirmationSend), 'long')
: ''
"
/>
</template>
<template #show-transaction-list>

View File

@ -17,6 +17,7 @@ export const searchUsers = gql`
creation
emailChecked
hasElopage
emailConfirmationSend
}
}
}

View File

@ -54,9 +54,9 @@ const dateTimeFormats = {
},
long: {
year: 'numeric',
month: 'short',
month: 'long',
day: 'numeric',
weekday: 'short',
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
},
@ -78,9 +78,9 @@ const dateTimeFormats = {
},
long: {
day: 'numeric',
month: 'short',
month: 'long',
year: 'numeric',
weekday: 'short',
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
},

View File

@ -77,7 +77,7 @@
"info": "Email bestätigen, wiederholt senden an:",
"success": "Erfolgreiches Senden des Bestätigungs-Links an die E-Mail des Nutzers! ({email})",
"text_false": " Die letzte Email wurde am {date} Uhr an das Mitglied ({mail}) gesendet.",
"text_true": " Die Email wurde am {date} Uhr bestätigt."
"text_true": " Die Email wurde bestätigt."
},
"user_search": "Nutzer-Suche"
}

View File

@ -76,8 +76,8 @@
"error": "Error sending the confirmation link to the user: {message}",
"info": "Confirm email, send repeatedly to:",
"success": "Successfully send the confirmation link to the user's email! ({email})",
"text_false": "The last email was sent to the member ({mail}) on {date} clock.",
"text_true": "The email was confirmed on {date} clock."
"text_false": "The last email was sent to the member ({mail}) on {date}.",
"text_true": "The email was confirmed."
},
"user_search": "User search"
}

View File

@ -1,6 +1,6 @@
{
"name": "gradido-backend",
"version": "1.6.3",
"version": "1.6.4",
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
"main": "src/index.ts",
"repository": "https://github.com/gradido/gradido/backend",
@ -18,6 +18,7 @@
},
"dependencies": {
"@types/jest": "^27.0.2",
"@types/lodash.clonedeep": "^4.5.6",
"apollo-log": "^1.1.0",
"apollo-server-express": "^2.25.2",
"apollo-server-testing": "^2.25.2",
@ -29,6 +30,7 @@
"graphql": "^15.5.1",
"jest": "^27.2.4",
"jsonwebtoken": "^8.5.1",
"lodash.clonedeep": "^4.5.0",
"module-alias": "^2.2.2",
"moment": "^2.29.1",
"mysql2": "^2.3.0",

View File

@ -4,7 +4,7 @@ import dotenv from 'dotenv'
dotenv.config()
const constants = {
DB_VERSION: '0020-rename_and_clean_state_users',
DB_VERSION: '0021-elopagebuys_fields_nullable',
}
const server = {

View File

@ -22,6 +22,9 @@ export class UserAdmin {
@Field(() => Boolean)
hasElopage: boolean
@Field(() => String, { nullable: true })
emailConfirmationSend?: string
}
@ObjectType()

View File

@ -22,7 +22,10 @@ import { BalanceRepository } from '../../typeorm/repository/Balance'
import { calculateDecay } from '../../util/decay'
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
import { hasElopageBuys } from '../../util/hasElopageBuys'
import { User as dbUser } from '@entity/User'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
// const EMAIL_OPT_IN_REGISTER = 1
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
@Resolver()
export class AdminResolver {
@ -57,6 +60,26 @@ export class AdminResolver {
adminUser.creation = await getUserCreations(user.id)
adminUser.emailChecked = user.emailChecked
adminUser.hasElopage = await hasElopageBuys(user.email)
if (!user.emailChecked) {
const emailOptIn = await LoginEmailOptIn.findOne(
{
userId: user.id,
},
{
order: {
updatedAt: 'DESC',
createdAt: 'DESC',
},
},
)
if (emailOptIn) {
if (emailOptIn.updatedAt) {
adminUser.emailConfirmationSend = emailOptIn.updatedAt.toISOString()
} else {
adminUser.emailConfirmationSend = emailOptIn.createdAt.toISOString()
}
}
}
return adminUser
}),
)
@ -73,8 +96,7 @@ export class AdminResolver {
): Promise<number[]> {
const userRepository = getCustomRepository(UserRepository)
const user = await userRepository.findByEmail(email)
const isActivated = await hasActivatedEmail(user.email)
if (!isActivated) {
if (!user.emailChecked) {
throw new Error('Creation could not be saved, Email is not activated')
}
const creations = await getUserCreations(user.id)
@ -386,8 +408,3 @@ function isCreationValid(creations: number[], amount: number, creationDate: Date
}
return true
}
async function hasActivatedEmail(email: string): Promise<boolean> {
const user = await dbUser.findOne({ email })
return user ? user.emailChecked : false
}

View File

@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ApolloLogPlugin } from 'apollo-log'
import { ApolloLogPlugin, LogMutateData } from 'apollo-log'
import cloneDeep from 'lodash.clonedeep'
const plugins = [
{
@ -21,7 +22,22 @@ const plugins = [
}
},
},
ApolloLogPlugin(),
ApolloLogPlugin({
mutate: (data: LogMutateData) => {
// We need to deep clone the object in order to not modify the actual request
const dataCopy = cloneDeep(data)
// mask password if part of the query
if (dataCopy.context.request.variables && dataCopy.context.request.variables.password) {
dataCopy.context.request.variables.password = '***'
}
// mask token at all times
dataCopy.context.context.token = '***'
return dataCopy
},
}),
]
export default plugins

View File

@ -53,12 +53,13 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
membership,
} = req.body
loginElopageBuy.affiliateProgramId = parseInt(product.affiliate_program_id)
loginElopageBuy.publisherId = parseInt(publisher.id)
loginElopageBuy.orderId = parseInt(order_id)
loginElopageBuy.productId = parseInt(product_id)
loginElopageBuy.affiliateProgramId = parseInt(product.affiliate_program_id) || null
loginElopageBuy.publisherId = parseInt(publisher.id) || null
loginElopageBuy.orderId = parseInt(order_id) || null
loginElopageBuy.productId = parseInt(product_id) || null
// TODO: WHAT THE ACTUAL FUK? Please save this as float in the future directly in the database
loginElopageBuy.productPrice = Math.trunc(parseFloat(product.price) * 100)
const productPrice = parseFloat(product.price)
loginElopageBuy.productPrice = productPrice ? Math.trunc(productPrice * 100) : 0
loginElopageBuy.payerEmail = payer.email
loginElopageBuy.publisherEmail = publisher.email
// eslint-disable-next-line camelcase
@ -66,7 +67,7 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
loginElopageBuy.successDate = new Date(success_date)
loginElopageBuy.event = event
// TODO this was never set on login_server - its unclear if this is the correct value
loginElopageBuy.elopageUserId = parseInt(membership.id)
loginElopageBuy.elopageUserId = parseInt(membership.id) || null
const firstName = payer.first_name
const lastName = payer.last_name
@ -79,7 +80,13 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
}
// Save the hook data
await LoginElopageBuys.save(loginElopageBuy)
try {
await LoginElopageBuys.save(loginElopageBuy)
} catch (error) {
// eslint-disable-next-line no-console
console.log('Error saving LoginElopageBuy', error)
return
}
// create user for certain products
/*
@ -90,7 +97,10 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
Business-Mitgliedschaft, 43960
Förderbeitrag: 49106
*/
if ([36001, 43741, 43870, 43944, 43960, 49106].includes(loginElopageBuy.productId)) {
if (
loginElopageBuy.productId &&
[36001, 43741, 43870, 43944, 43960, 49106].includes(loginElopageBuy.productId)
) {
const email = loginElopageBuy.payerEmail
const VALIDATE_EMAIL = /^[a-zA-Z0-9.!#$%&?*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
@ -123,7 +133,7 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
email,
firstName,
lastName,
publisherId: loginElopageBuy.publisherId,
publisherId: loginElopageBuy.publisherId || 0, // This seemed to be the default value if not set
})
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -913,6 +913,18 @@
"@types/koa-compose" "*"
"@types/node" "*"
"@types/lodash.clonedeep@^4.5.6":
version "4.5.6"
resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz#3b6c40a0affe0799a2ce823b440a6cf33571d32b"
integrity sha512-cE1jYr2dEg1wBImvXlNtp0xDoS79rfEdGozQVgliDZj1uERH4k+rmEMTudP9b4VQ8O6nRb5gPqft0QzEQGMQgA==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.178"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==
"@types/long@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"

View File

@ -5,8 +5,8 @@ export class LoginElopageBuys extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'elopage_user_id', nullable: false })
elopageUserId: number
@Column({ type: 'int', width: 11, name: 'elopage_user_id', nullable: true, default: null })
elopageUserId: number | null
@Column({ name: 'affiliate_program_id', nullable: false })
affiliateProgramId: number

View File

@ -0,0 +1,52 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('login_elopage_buys')
export class LoginElopageBuys extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ type: 'int', width: 11, name: 'elopage_user_id', nullable: true, default: null })
elopageUserId: number | null
@Column({ type: 'int', width: 11, name: 'affiliate_program_id', nullable: true, default: null })
affiliateProgramId: number | null
@Column({ type: 'int', width: 11, name: 'publisher_id', nullable: true, default: null })
publisherId: number | null
@Column({ type: 'int', width: 11, name: 'order_id', nullable: true, default: null })
orderId: number | null
@Column({ type: 'int', width: 11, name: 'product_id', nullable: true, default: null })
productId: number | null
@Column({ name: 'product_price', nullable: false })
productPrice: number
@Column({
name: 'payer_email',
length: 255,
nullable: false,
charset: 'utf8',
collation: 'utf8_bin',
})
payerEmail: string
@Column({
name: 'publisher_email',
length: 255,
nullable: false,
charset: 'utf8',
collation: 'utf8_bin',
})
publisherEmail: string
@Column({ nullable: false })
payed: boolean
@Column({ name: 'success_date', nullable: false })
successDate: Date
@Column({ length: 255, nullable: false })
event: string
}

View File

@ -1 +1 @@
export { LoginElopageBuys } from './0003-login_server_tables/LoginElopageBuys'
export { LoginElopageBuys } from './0021-elopagebuys_fields_nullable/LoginElopageBuys'

View File

@ -0,0 +1,25 @@
/* MIGRATION TO ALLOW NULL FIELDS ON ELOPAGEBUYS
*
* This migration allows null on `affiliate_program_id`,
* `publisher_id`, `order_id`. `product_id`.
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(
'ALTER TABLE `login_elopage_buys` MODIFY COLUMN `affiliate_program_id` int(11) DEFAULT NULL;',
)
await queryFn(
'ALTER TABLE `login_elopage_buys` MODIFY COLUMN `publisher_id` int(11) DEFAULT NULL;',
)
await queryFn('ALTER TABLE `login_elopage_buys` MODIFY COLUMN `order_id` int(11) DEFAULT NULL;')
await queryFn('ALTER TABLE `login_elopage_buys` MODIFY COLUMN `product_id` int(11) DEFAULT NULL;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(
'ALTER TABLE `login_elopage_buys` MODIFY COLUMN `affiliate_program_id` int(11) NOT NULL;',
)
await queryFn('ALTER TABLE `login_elopage_buys` MODIFY COLUMN `publisher_id` int(11) NOT NULL;')
await queryFn('ALTER TABLE `login_elopage_buys` MODIFY COLUMN `order_id` int(11) NOT NULL;')
await queryFn('ALTER TABLE `login_elopage_buys` MODIFY COLUMN `product_id` int(11) NOT NULL;')
}

View File

@ -1,6 +1,6 @@
{
"name": "gradido-database",
"version": "1.6.3",
"version": "1.6.4",
"description": "Gradido Database Tool to execute database migrations",
"main": "src/index.ts",
"repository": "https://github.com/gradido/gradido/database",

View File

@ -1,6 +1,6 @@
{
"name": "bootstrap-vue-gradido-wallet",
"version": "1.6.3",
"version": "1.6.4",
"private": true,
"scripts": {
"start": "node run/server.js",

View File

@ -1,6 +1,6 @@
{
"name": "gradido",
"version": "1.6.3",
"version": "1.6.4",
"description": "Gradido",
"main": "index.js",
"repository": "git@github.com:gradido/gradido.git",