mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into refactor_remove_community_server
This commit is contained in:
commit
a7544a5151
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -422,7 +422,7 @@ jobs:
|
|||||||
report_name: Coverage Admin Interface
|
report_name: Coverage Admin Interface
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./coverage/lcov.info
|
||||||
min_coverage: 81
|
min_coverage: 93
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
@ -13,3 +13,11 @@ export default {
|
|||||||
components: { defaultLayout },
|
components: { defaultLayout },
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.pointer:hover {
|
||||||
|
background-color: rgb(216, 213, 213);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -11,22 +11,36 @@ describe('UserTable', () => {
|
|||||||
|
|
||||||
const defaultItemsUser = [
|
const defaultItemsUser = [
|
||||||
{
|
{
|
||||||
email: 'bibi@bloxberg.de',
|
userId: 1,
|
||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
creation: [1000, 1000, 1000],
|
email: 'bibi@bloxberg.de',
|
||||||
|
creation: [200, 400, 600],
|
||||||
|
emailChecked: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
email: 'bibi@bloxberg.de',
|
userId: 2,
|
||||||
firstName: 'Bibi',
|
firstName: 'Benjamin',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Blümchen',
|
||||||
|
email: 'benjamin@bluemchen.de',
|
||||||
creation: [1000, 1000, 1000],
|
creation: [1000, 1000, 1000],
|
||||||
|
emailChecked: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
email: 'bibi@bloxberg.de',
|
userId: 3,
|
||||||
firstName: 'Bibi',
|
firstName: 'Peter',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
creation: [0, 0, 0],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 4,
|
||||||
|
firstName: 'New',
|
||||||
|
lastName: 'User',
|
||||||
|
email: 'new@user.ch',
|
||||||
creation: [1000, 1000, 1000],
|
creation: [1000, 1000, 1000],
|
||||||
|
emailChecked: false,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -107,7 +121,7 @@ describe('UserTable', () => {
|
|||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => String(d)),
|
||||||
$apollo: {
|
$apollo: {
|
||||||
query: apolloQueryMock,
|
query: apolloQueryMock,
|
||||||
},
|
},
|
||||||
@ -122,7 +136,7 @@ describe('UserTable', () => {
|
|||||||
|
|
||||||
describe('mount', () => {
|
describe('mount', () => {
|
||||||
describe('type PageUserSearch', () => {
|
describe('type PageUserSearch', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
wrapper = Wrapper(propsDataPageUserSearch)
|
wrapper = Wrapper(propsDataPageUserSearch)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -175,12 +189,12 @@ describe('UserTable', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('content', () => {
|
describe('content', () => {
|
||||||
it('has 3 rows', () => {
|
it('has 4 rows', () => {
|
||||||
expect(wrapper.findAll('tbody tr').length).toBe(3)
|
expect(wrapper.findAll('tbody tr')).toHaveLength(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has 7 columns', () => {
|
it('has 7 columns', () => {
|
||||||
expect(wrapper.findAll('tr:nth-child(1) > td').length).toBe(7)
|
expect(wrapper.findAll('tr:nth-child(1) > td')).toHaveLength(7)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('find button on fifth column', () => {
|
it('find button on fifth column', () => {
|
||||||
@ -189,6 +203,110 @@ describe('UserTable', () => {
|
|||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('row toggling', () => {
|
||||||
|
describe('user with email not activated', () => {
|
||||||
|
it('has no details button', () => {
|
||||||
|
expect(
|
||||||
|
wrapper.findAll('tbody > tr').at(3).findAll('td').at(4).find('button').exists(),
|
||||||
|
).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a red confirmed button with envelope item', () => {
|
||||||
|
const row = wrapper.findAll('tbody > tr').at(3)
|
||||||
|
expect(row.findAll('td').at(5).find('button').exists()).toBeTruthy()
|
||||||
|
expect(row.findAll('td').at(5).find('button').classes('btn-danger')).toBeTruthy()
|
||||||
|
expect(row.findAll('td').at(5).find('svg').classes('bi-envelope')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on envelope', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper
|
||||||
|
.findAll('tbody > tr')
|
||||||
|
.at(3)
|
||||||
|
.findAll('td')
|
||||||
|
.at(5)
|
||||||
|
.find('button')
|
||||||
|
.trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('opens the details', async () => {
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(6)
|
||||||
|
expect(wrapper.findAll('tbody > tr').at(5).find('input').element.value).toBe(
|
||||||
|
'new@user.ch',
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('tbody > tr').at(5).text()).toContain(
|
||||||
|
'unregister_mail.text_false',
|
||||||
|
)
|
||||||
|
// HACK: for some reason we need to close the row details after this test
|
||||||
|
await wrapper
|
||||||
|
.findAll('tbody > tr')
|
||||||
|
.at(3)
|
||||||
|
.findAll('td')
|
||||||
|
.at(5)
|
||||||
|
.find('button')
|
||||||
|
.trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on envelope again', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper
|
||||||
|
.findAll('tbody > tr')
|
||||||
|
.at(3)
|
||||||
|
.findAll('td')
|
||||||
|
.at(5)
|
||||||
|
.find('button')
|
||||||
|
.trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('closes the details', () => {
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on close details', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.findAll('tbody > tr').at(5).findAll('button').at(1).trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('closes the details', () => {
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('different details', () => {
|
||||||
|
it.skip('shows the creation formular for second user', async () => {
|
||||||
|
await wrapper
|
||||||
|
.findAll('tbody > tr')
|
||||||
|
.at(1)
|
||||||
|
.findAll('td')
|
||||||
|
.at(4)
|
||||||
|
.find('button')
|
||||||
|
.trigger('click')
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(6)
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.findAll('tbody > tr')
|
||||||
|
.at(3)
|
||||||
|
.find('div.component-creation-formular')
|
||||||
|
.exists(),
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it.skip('shows the transactions for third user', async () => {
|
||||||
|
await wrapper
|
||||||
|
.findAll('tbody > tr')
|
||||||
|
.at(4)
|
||||||
|
.findAll('td')
|
||||||
|
.at(6)
|
||||||
|
.find('button')
|
||||||
|
.trigger('click')
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(6)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -27,15 +27,7 @@
|
|||||||
</b-button>
|
</b-button>
|
||||||
</b-jumbotron>
|
</b-jumbotron>
|
||||||
</div>
|
</div>
|
||||||
<b-table-lite
|
<b-table-lite :items="itemsUser" :fields="fieldsTable" caption-top striped hover stacked="md">
|
||||||
:items="itemsUser"
|
|
||||||
:fields="fieldsTable"
|
|
||||||
:filter="criteria"
|
|
||||||
caption-top
|
|
||||||
striped
|
|
||||||
hover
|
|
||||||
stacked="md"
|
|
||||||
>
|
|
||||||
<template #cell(creation)="data">
|
<template #cell(creation)="data">
|
||||||
<div v-html="data.value"></div>
|
<div v-html="data.value"></div>
|
||||||
</template>
|
</template>
|
||||||
@ -125,7 +117,7 @@
|
|||||||
</row-details>
|
</row-details>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(bookmark)="row">
|
<template #cell(bookmark)="row">
|
||||||
<div v-show="type === 'UserListSearch'">
|
<div v-if="type === 'UserListSearch'">
|
||||||
<b-button
|
<b-button
|
||||||
v-if="row.item.emailChecked"
|
v-if="row.item.emailChecked"
|
||||||
variant="warning"
|
variant="warning"
|
||||||
@ -141,7 +133,7 @@
|
|||||||
variant="danger"
|
variant="danger"
|
||||||
v-show="type === 'UserListMassCreation' || type === 'PageCreationConfirm'"
|
v-show="type === 'UserListMassCreation' || type === 'PageCreationConfirm'"
|
||||||
size="md"
|
size="md"
|
||||||
@click="overlayShow('remove', row.item)"
|
@click="bookmarkRemove(row.item)"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
<b-icon icon="x" variant="light"></b-icon>
|
<b-icon icon="x" variant="light"></b-icon>
|
||||||
@ -187,15 +179,6 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
criteria: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
creation: {
|
|
||||||
type: Array,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
CreationFormular,
|
CreationFormular,
|
||||||
@ -259,13 +242,6 @@ export default {
|
|||||||
this.overlayBookmarkType = bookmarkType
|
this.overlayBookmarkType = bookmarkType
|
||||||
this.overlayItem = item
|
this.overlayItem = item
|
||||||
|
|
||||||
if (bookmarkType === 'remove') {
|
|
||||||
this.overlayText.header = this.$t('overlay.remove.title')
|
|
||||||
this.overlayText.text1 = this.$t('overlay.remove.text')
|
|
||||||
this.overlayText.text2 = this.$t('overlay.remove.question')
|
|
||||||
this.overlayText.button_ok = this.$t('overlay.remove.yes')
|
|
||||||
this.overlayText.button_cancel = this.$t('overlay.remove.no')
|
|
||||||
}
|
|
||||||
if (bookmarkType === 'confirm') {
|
if (bookmarkType === 'confirm') {
|
||||||
this.overlayText.header = this.$t('overlay.confirm.title')
|
this.overlayText.header = this.$t('overlay.confirm.title')
|
||||||
this.overlayText.text1 = this.$t('overlay.confirm.text')
|
this.overlayText.text1 = this.$t('overlay.confirm.text')
|
||||||
@ -275,9 +251,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
overlayOK(bookmarkType, item) {
|
overlayOK(bookmarkType, item) {
|
||||||
if (bookmarkType === 'remove') {
|
|
||||||
this.bookmarkRemove(item)
|
|
||||||
}
|
|
||||||
if (bookmarkType === 'confirm') {
|
if (bookmarkType === 'confirm') {
|
||||||
this.$emit('confirm-creation', item)
|
this.$emit('confirm-creation', item)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,6 +54,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remove": "Entfernen",
|
"remove": "Entfernen",
|
||||||
|
"remove_all": "alle Nutzer entfernen",
|
||||||
"transaction": "Transaktion",
|
"transaction": "Transaktion",
|
||||||
"transactionlist": {
|
"transactionlist": {
|
||||||
"amount": "Betrag",
|
"amount": "Betrag",
|
||||||
|
|||||||
@ -54,6 +54,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
|
"remove_all": "Remove all users",
|
||||||
"transaction": "Transaction",
|
"transaction": "Transaction",
|
||||||
"transactionlist": {
|
"transactionlist": {
|
||||||
"amount": "Amount",
|
"amount": "Amount",
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
export const creationMonths = {
|
export const creationMonths = {
|
||||||
props: {
|
props: {
|
||||||
creation: [1000, 1000, 1000],
|
creation: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [1000, 1000, 1000],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
creationDates() {
|
creationDates() {
|
||||||
@ -31,5 +34,8 @@ export const creationMonths = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
creationLabel() {
|
||||||
|
return this.creationDates.map((date) => this.$d(date, 'monthShort')).join(' | ')
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { shallowMount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Creation from './Creation.vue'
|
import Creation from './Creation.vue'
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
@ -14,6 +14,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
|
|||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
creation: [200, 400, 600],
|
creation: [200, 400, 600],
|
||||||
|
emailChecked: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
userId: 2,
|
userId: 2,
|
||||||
@ -21,6 +22,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
|
|||||||
lastName: 'Blümchen',
|
lastName: 'Blümchen',
|
||||||
email: 'benjamin@bluemchen.de',
|
email: 'benjamin@bluemchen.de',
|
||||||
creation: [800, 600, 400],
|
creation: [800, 600, 400],
|
||||||
|
emailChecked: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -51,10 +53,10 @@ describe('Creation', () => {
|
|||||||
let wrapper
|
let wrapper
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = () => {
|
||||||
return shallowMount(Creation, { localVue, mocks })
|
return mount(Creation, { localVue, mocks })
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('shallowMount', () => {
|
describe('mount', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
@ -77,64 +79,66 @@ describe('Creation', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets the data of itemsList', () => {
|
it('has two rows in the left table', () => {
|
||||||
expect(wrapper.vm.itemsList).toEqual([
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
||||||
{
|
})
|
||||||
userId: 1,
|
|
||||||
firstName: 'Bibi',
|
it('has nwo rows in the right table', () => {
|
||||||
lastName: 'Bloxberg',
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
||||||
email: 'bibi@bloxberg.de',
|
})
|
||||||
creation: [200, 400, 600],
|
|
||||||
showDetails: false,
|
it('has correct data in first row ', () => {
|
||||||
},
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain('Bibi')
|
||||||
{
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
userId: 2,
|
'Bloxberg',
|
||||||
firstName: 'Benjamin',
|
)
|
||||||
lastName: 'Blümchen',
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
email: 'benjamin@bluemchen.de',
|
'200 | 400 | 600',
|
||||||
creation: [800, 600, 400],
|
)
|
||||||
showDetails: false,
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
},
|
'bibi@bloxberg.de',
|
||||||
])
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has correct data in second row ', () => {
|
||||||
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||||
|
'Benjamin',
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||||
|
'Blümchen',
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||||
|
'800 | 600 | 400',
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||||
|
'benjamin@bluemchen.de',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('push item', () => {
|
describe('push item', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
|
wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).find('button').trigger('click')
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('removes the pushed item from itemsList', () => {
|
it('has one item in left table', () => {
|
||||||
expect(wrapper.vm.itemsList).toEqual([
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
||||||
{
|
|
||||||
userId: 1,
|
|
||||||
firstName: 'Bibi',
|
|
||||||
lastName: 'Bloxberg',
|
|
||||||
email: 'bibi@bloxberg.de',
|
|
||||||
creation: [200, 400, 600],
|
|
||||||
showDetails: false,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('adds the pushed item to itemsMassCreation', () => {
|
it('has one item in right table', () => {
|
||||||
expect(wrapper.vm.itemsMassCreation).toEqual([
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
||||||
{
|
})
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
it('has the correct user in left table', () => {
|
||||||
lastName: 'Blümchen',
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
email: 'benjamin@bluemchen.de',
|
'bibi@bloxberg.de',
|
||||||
creation: [800, 600, 400],
|
)
|
||||||
showDetails: false,
|
})
|
||||||
},
|
|
||||||
])
|
it('has the correct user in right table', () => {
|
||||||
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
|
'benjamin@bluemchen.de',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates userSelectedInMassCreation in store', () => {
|
it('updates userSelectedInMassCreation in store', () => {
|
||||||
@ -146,88 +150,58 @@ describe('Creation', () => {
|
|||||||
email: 'benjamin@bluemchen.de',
|
email: 'benjamin@bluemchen.de',
|
||||||
creation: [800, 600, 400],
|
creation: [800, 600, 400],
|
||||||
showDetails: false,
|
showDetails: false,
|
||||||
},
|
emailChecked: true,
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('remove item', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
})
|
|
||||||
await wrapper
|
|
||||||
.findAllComponents({ name: 'UserTable' })
|
|
||||||
.at(1)
|
|
||||||
.vm.$emit('remove-item', {
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('adds the removed item to itemsList', () => {
|
|
||||||
expect(wrapper.vm.itemsList).toEqual([
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 1,
|
|
||||||
firstName: 'Bibi',
|
|
||||||
lastName: 'Bloxberg',
|
|
||||||
email: 'bibi@bloxberg.de',
|
|
||||||
creation: [200, 400, 600],
|
|
||||||
showDetails: false,
|
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('removes the item from itemsMassCreation', () => {
|
describe('remove item', () => {
|
||||||
expect(wrapper.vm.itemsMassCreation).toEqual([])
|
beforeEach(async () => {
|
||||||
})
|
await wrapper
|
||||||
|
.findAll('table')
|
||||||
it('commits empty array as userSelectedInMassCreation', () => {
|
.at(1)
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
.findAll('tbody > tr')
|
||||||
})
|
.at(0)
|
||||||
})
|
.find('button')
|
||||||
|
.trigger('click')
|
||||||
describe('remove all bookmarks', () => {
|
})
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
|
it('has two items in left table', () => {
|
||||||
userId: 2,
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
||||||
firstName: 'Benjamin',
|
})
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
it('has the removed user in first row', () => {
|
||||||
creation: [800, 600, 400],
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
showDetails: false,
|
'benjamin@bluemchen.de',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has no items in right table', () => {
|
||||||
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('commits empty array as userSelectedInMassCreation', () => {
|
||||||
|
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
||||||
})
|
})
|
||||||
jest.clearAllMocks()
|
|
||||||
wrapper.findComponent({ name: 'CreationFormular' }).vm.$emit('remove-all-bookmark')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('removes all items from itemsMassCreation', () => {
|
describe('remove all bookmarks', () => {
|
||||||
expect(wrapper.vm.itemsMassCreation).toEqual([])
|
beforeEach(async () => {
|
||||||
})
|
jest.clearAllMocks()
|
||||||
|
await wrapper.find('button.btn-light').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
it('commits empty array to userSelectedInMassCreation', () => {
|
it('has no items in right table', () => {
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls searchUsers', () => {
|
it('commits empty array to userSelectedInMassCreation', () => {
|
||||||
expect(apolloQueryMock).toBeCalled()
|
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls searchUsers', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -241,22 +215,24 @@ describe('Creation', () => {
|
|||||||
email: 'benjamin@bluemchen.de',
|
email: 'benjamin@bluemchen.de',
|
||||||
creation: [800, 600, 400],
|
creation: [800, 600, 400],
|
||||||
showDetails: false,
|
showDetails: false,
|
||||||
|
emailChecked: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has only one item itemsList', () => {
|
it('has one item in left table', () => {
|
||||||
expect(wrapper.vm.itemsList).toEqual([
|
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
||||||
{
|
})
|
||||||
userId: 1,
|
|
||||||
firstName: 'Bibi',
|
it('has one item in right table', () => {
|
||||||
lastName: 'Bloxberg',
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
||||||
email: 'bibi@bloxberg.de',
|
})
|
||||||
creation: [200, 400, 600],
|
|
||||||
showDetails: false,
|
it('has the stored user in second row', () => {
|
||||||
},
|
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
||||||
])
|
'benjamin@bluemchen.de',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -265,17 +241,38 @@ describe('Creation', () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls API when criteria changes', async () => {
|
describe('search criteria', () => {
|
||||||
await wrapper.setData({ criteria: 'XX' })
|
beforeEach(async () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
await wrapper.setData({ criteria: 'XX' })
|
||||||
expect.objectContaining({
|
})
|
||||||
variables: {
|
|
||||||
searchText: 'XX',
|
it('calls API when criteria changes', async () => {
|
||||||
currentPage: 1,
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
pageSize: 25,
|
expect.objectContaining({
|
||||||
},
|
variables: {
|
||||||
}),
|
searchText: 'XX',
|
||||||
)
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('reset search criteria', () => {
|
||||||
|
it('calls the API', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.find('.test-click-clear-criteria').trigger('click')
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: '',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls API when currentPage changes', async () => {
|
it('calls API when currentPage changes', async () => {
|
||||||
|
|||||||
@ -3,19 +3,25 @@
|
|||||||
<b-row>
|
<b-row>
|
||||||
<b-col cols="12" lg="6">
|
<b-col cols="12" lg="6">
|
||||||
<label>Usersuche</label>
|
<label>Usersuche</label>
|
||||||
<b-input
|
<b-input-group>
|
||||||
type="text"
|
<b-form-input
|
||||||
v-model="criteria"
|
type="text"
|
||||||
class="shadow p-3 mb-5 bg-white rounded"
|
class="test-input-criteria"
|
||||||
placeholder="User suche"
|
v-model="criteria"
|
||||||
></b-input>
|
:placeholder="$t('user_search')"
|
||||||
|
></b-form-input>
|
||||||
|
|
||||||
|
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
|
||||||
|
<b-input-group-text class="pointer">
|
||||||
|
<b-icon icon="x" />
|
||||||
|
</b-input-group-text>
|
||||||
|
</b-input-group-append>
|
||||||
|
</b-input-group>
|
||||||
<user-table
|
<user-table
|
||||||
v-if="itemsList.length > 0"
|
v-if="itemsList.length > 0"
|
||||||
type="UserListSearch"
|
type="UserListSearch"
|
||||||
:itemsUser="itemsList"
|
:itemsUser="itemsList"
|
||||||
:fieldsTable="Searchfields"
|
:fieldsTable="Searchfields"
|
||||||
:criteria="criteria"
|
|
||||||
:creation="creation"
|
|
||||||
@push-item="pushItem"
|
@push-item="pushItem"
|
||||||
/>
|
/>
|
||||||
<b-pagination
|
<b-pagination
|
||||||
@ -27,16 +33,22 @@
|
|||||||
></b-pagination>
|
></b-pagination>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="12" lg="6" class="shadow p-3 mb-5 rounded bg-info">
|
<b-col cols="12" lg="6" class="shadow p-3 mb-5 rounded bg-info">
|
||||||
<user-table
|
<div v-show="itemsMassCreation.length > 0">
|
||||||
v-show="itemsMassCreation.length > 0"
|
<div class="text-right pr-4 mb-1">
|
||||||
class="shadow p-3 mb-5 bg-white rounded"
|
<b-button @click="removeAllBookmarks()" variant="light">
|
||||||
type="UserListMassCreation"
|
<b-icon icon="x" scale="2" variant="danger"></b-icon>
|
||||||
:itemsUser="itemsMassCreation"
|
|
||||||
:fieldsTable="fields"
|
{{ $t('remove_all') }}
|
||||||
:criteria="null"
|
</b-button>
|
||||||
:creation="creation"
|
</div>
|
||||||
@remove-item="removeItem"
|
<user-table
|
||||||
/>
|
class="shadow p-3 mb-5 bg-white rounded"
|
||||||
|
type="UserListMassCreation"
|
||||||
|
:itemsUser="itemsMassCreation"
|
||||||
|
:fieldsTable="fields"
|
||||||
|
@remove-item="removeItem"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div v-if="itemsMassCreation.length === 0">
|
<div v-if="itemsMassCreation.length === 0">
|
||||||
{{ $t('multiple_creation_text') }}
|
{{ $t('multiple_creation_text') }}
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +57,7 @@
|
|||||||
type="massCreation"
|
type="massCreation"
|
||||||
:creation="creation"
|
:creation="creation"
|
||||||
:items="itemsMassCreation"
|
:items="itemsMassCreation"
|
||||||
@remove-all-bookmark="removeAllBookmark"
|
@remove-all-bookmark="removeAllBookmarks"
|
||||||
/>
|
/>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
@ -55,9 +67,11 @@
|
|||||||
import CreationFormular from '../components/CreationFormular.vue'
|
import CreationFormular from '../components/CreationFormular.vue'
|
||||||
import UserTable from '../components/UserTable.vue'
|
import UserTable from '../components/UserTable.vue'
|
||||||
import { searchUsers } from '../graphql/searchUsers'
|
import { searchUsers } from '../graphql/searchUsers'
|
||||||
|
import { creationMonths } from '../mixins/creationMonths'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Creation',
|
name: 'Creation',
|
||||||
|
mixins: [creationMonths],
|
||||||
components: {
|
components: {
|
||||||
CreationFormular,
|
CreationFormular,
|
||||||
UserTable,
|
UserTable,
|
||||||
@ -69,7 +83,6 @@ export default {
|
|||||||
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
|
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
|
||||||
radioSelectedMass: '',
|
radioSelectedMass: '',
|
||||||
criteria: '',
|
criteria: '',
|
||||||
creation: [null, null, null],
|
|
||||||
rows: 0,
|
rows: 0,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
perPage: 25,
|
perPage: 25,
|
||||||
@ -126,7 +139,7 @@ export default {
|
|||||||
)
|
)
|
||||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
||||||
},
|
},
|
||||||
removeAllBookmark() {
|
removeAllBookmarks() {
|
||||||
this.itemsMassCreation = []
|
this.itemsMassCreation = []
|
||||||
this.$store.commit('setUserSelectedInMassCreation', [])
|
this.$store.commit('setUserSelectedInMassCreation', [])
|
||||||
this.getUsers()
|
this.getUsers()
|
||||||
@ -163,16 +176,6 @@ export default {
|
|||||||
{ key: 'bookmark', label: this.$t('remove') },
|
{ key: 'bookmark', label: this.$t('remove') },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
creationLabel() {
|
|
||||||
const now = new Date(this.now)
|
|
||||||
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1)
|
|
||||||
const beforeLastMonth = new Date(now.getFullYear(), now.getMonth() - 2, 1)
|
|
||||||
return [
|
|
||||||
this.$d(beforeLastMonth, 'monthShort'),
|
|
||||||
this.$d(lastMonth, 'monthShort'),
|
|
||||||
this.$d(now, 'monthShort'),
|
|
||||||
].join(' | ')
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentPage() {
|
currentPage() {
|
||||||
|
|||||||
@ -78,6 +78,7 @@ describe('CreationConfirm', () => {
|
|||||||
it('commits resetOpenCreations to store', () => {
|
it('commits resetOpenCreations to store', () => {
|
||||||
expect(storeCommitMock).toBeCalledWith('resetOpenCreations')
|
expect(storeCommitMock).toBeCalledWith('resetOpenCreations')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits setOpenCreations to store', () => {
|
it('commits setOpenCreations to store', () => {
|
||||||
expect(storeCommitMock).toBeCalledWith('setOpenCreations', 2)
|
expect(storeCommitMock).toBeCalledWith('setOpenCreations', 2)
|
||||||
})
|
})
|
||||||
@ -85,7 +86,7 @@ describe('CreationConfirm', () => {
|
|||||||
|
|
||||||
describe('remove creation with success', () => {
|
describe('remove creation with success', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('remove-creation', { id: 1 })
|
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the deletePendingCreation mutation', () => {
|
it('calls the deletePendingCreation mutation', () => {
|
||||||
@ -107,7 +108,7 @@ describe('CreationConfirm', () => {
|
|||||||
describe('remove creation with error', () => {
|
describe('remove creation with error', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
apolloMutateMock.mockRejectedValue({ message: 'Ouchhh!' })
|
apolloMutateMock.mockRejectedValue({ message: 'Ouchhh!' })
|
||||||
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('remove-creation', { id: 1 })
|
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toasts an error message', () => {
|
it('toasts an error message', () => {
|
||||||
@ -118,22 +119,52 @@ describe('CreationConfirm', () => {
|
|||||||
describe('confirm creation with success', () => {
|
describe('confirm creation with success', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
apolloMutateMock.mockResolvedValue({})
|
apolloMutateMock.mockResolvedValue({})
|
||||||
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('confirm-creation', { id: 2 })
|
await wrapper.findAll('tr').at(2).findAll('button').at(2).trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the confirmPendingCreation mutation', () => {
|
describe('overlay', () => {
|
||||||
expect(apolloMutateMock).toBeCalledWith({
|
it('opens the overlay', () => {
|
||||||
mutation: confirmPendingCreation,
|
expect(wrapper.find('#overlay').isVisible()).toBeTruthy()
|
||||||
variables: { id: 2 },
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
it('commits openCreationsMinus to store', () => {
|
describe('cancel confirmation', () => {
|
||||||
expect(storeCommitMock).toBeCalledWith('openCreationsMinus', 1)
|
beforeEach(async () => {
|
||||||
})
|
await wrapper.find('#overlay').findAll('button').at(0).trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
it('toasts a success message', () => {
|
it('closes the overlay', () => {
|
||||||
expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_created')
|
expect(wrapper.find('#overlay').isVisible()).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('still has 2 items in the table', () => {
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('confirm creation', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('#overlay').findAll('button').at(1).trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the confirmPendingCreation mutation', () => {
|
||||||
|
expect(apolloMutateMock).toBeCalledWith({
|
||||||
|
mutation: confirmPendingCreation,
|
||||||
|
variables: { id: 2 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('commits openCreationsMinus to store', () => {
|
||||||
|
expect(storeCommitMock).toBeCalledWith('openCreationsMinus', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts a success message', () => {
|
||||||
|
expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_created')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has 1 item left in the table', () => {
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -9,10 +9,35 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
|
|||||||
userCount: 1,
|
userCount: 1,
|
||||||
userList: [
|
userList: [
|
||||||
{
|
{
|
||||||
|
userId: 1,
|
||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
creation: [200, 400, 600],
|
creation: [200, 400, 600],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 2,
|
||||||
|
firstName: 'Benjamin',
|
||||||
|
lastName: 'Blümchen',
|
||||||
|
email: 'benjamin@bluemchen.de',
|
||||||
|
creation: [1000, 1000, 1000],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 3,
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
creation: [0, 0, 0],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 4,
|
||||||
|
firstName: 'New',
|
||||||
|
lastName: 'User',
|
||||||
|
email: 'new@user.ch',
|
||||||
|
creation: [1000, 1000, 1000],
|
||||||
emailChecked: false,
|
emailChecked: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -24,7 +49,7 @@ const toastErrorMock = jest.fn()
|
|||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => String(d)),
|
||||||
$apollo: {
|
$apollo: {
|
||||||
query: apolloQueryMock,
|
query: apolloQueryMock,
|
||||||
},
|
},
|
||||||
@ -42,6 +67,7 @@ describe('UserSearch', () => {
|
|||||||
|
|
||||||
describe('mount', () => {
|
describe('mount', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -49,13 +75,90 @@ describe('UserSearch', () => {
|
|||||||
expect(wrapper.find('div.user-search').exists()).toBeTruthy()
|
expect(wrapper.find('div.user-search').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('calls the API', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: '',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
notActivated: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
describe('unconfirmed emails', () => {
|
describe('unconfirmed emails', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.find('button.btn-block').trigger('click')
|
await wrapper.find('button.btn-block').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('filters the users by unconfirmed emails', () => {
|
it('calls API with filter', () => {
|
||||||
expect(wrapper.vm.searchResult).toHaveLength(1)
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: '',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
notActivated: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('pagination', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({ currentPage: 2 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API with new page', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: '',
|
||||||
|
currentPage: 2,
|
||||||
|
pageSize: 25,
|
||||||
|
notActivated: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user search', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({ criteria: 'search string' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API with search string', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: 'search string',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
notActivated: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('reset the search field', () => {
|
||||||
|
it('calls the API with empty criteria', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.find('.test-click-clear-criteria').trigger('click')
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: '',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
notActivated: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -7,20 +7,22 @@
|
|||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
<label>{{ $t('user_search') }}</label>
|
<label>{{ $t('user_search') }}</label>
|
||||||
<b-input
|
<div>
|
||||||
type="text"
|
<b-input-group>
|
||||||
v-model="criteria"
|
<b-form-input
|
||||||
class="shadow p-3 mb-3 bg-white rounded"
|
type="text"
|
||||||
:placeholder="$t('user_search')"
|
class="test-input-criteria"
|
||||||
@input="getUsers"
|
v-model="criteria"
|
||||||
></b-input>
|
:placeholder="$t('user_search')"
|
||||||
|
></b-form-input>
|
||||||
<user-table
|
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
|
||||||
type="PageUserSearch"
|
<b-input-group-text class="pointer">
|
||||||
:itemsUser="searchResult"
|
<b-icon icon="x" />
|
||||||
:fieldsTable="fields"
|
</b-input-group-text>
|
||||||
:criteria="criteria"
|
</b-input-group-append>
|
||||||
/>
|
</b-input-group>
|
||||||
|
</div>
|
||||||
|
<user-table type="PageUserSearch" :itemsUser="searchResult" :fieldsTable="fields" />
|
||||||
<b-pagination
|
<b-pagination
|
||||||
pills
|
pills
|
||||||
size="lg"
|
size="lg"
|
||||||
@ -35,9 +37,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import UserTable from '../components/UserTable.vue'
|
import UserTable from '../components/UserTable.vue'
|
||||||
import { searchUsers } from '../graphql/searchUsers'
|
import { searchUsers } from '../graphql/searchUsers'
|
||||||
|
import { creationMonths } from '../mixins/creationMonths'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UserSearch',
|
name: 'UserSearch',
|
||||||
|
mixins: [creationMonths],
|
||||||
components: {
|
components: {
|
||||||
UserTable,
|
UserTable,
|
||||||
},
|
},
|
||||||
@ -83,16 +87,11 @@ export default {
|
|||||||
currentPage() {
|
currentPage() {
|
||||||
this.getUsers()
|
this.getUsers()
|
||||||
},
|
},
|
||||||
|
criteria() {
|
||||||
|
this.getUsers()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
lastMonthDate() {
|
|
||||||
const now = new Date(this.now)
|
|
||||||
return new Date(now.getFullYear(), now.getMonth() - 1, 1)
|
|
||||||
},
|
|
||||||
beforeLastMonthDate() {
|
|
||||||
const now = new Date(this.now)
|
|
||||||
return new Date(now.getFullYear(), now.getMonth() - 2, 1)
|
|
||||||
},
|
|
||||||
fields() {
|
fields() {
|
||||||
return [
|
return [
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
{ key: 'email', label: this.$t('e_mail') },
|
||||||
@ -100,11 +99,7 @@ export default {
|
|||||||
{ key: 'lastName', label: this.$t('lastname') },
|
{ key: 'lastName', label: this.$t('lastname') },
|
||||||
{
|
{
|
||||||
key: 'creation',
|
key: 'creation',
|
||||||
label: [
|
label: this.creationLabel,
|
||||||
this.$d(this.beforeLastMonthDate, 'monthShort'),
|
|
||||||
this.$d(this.lastMonthDate, 'monthShort'),
|
|
||||||
this.$d(this.now, 'monthShort'),
|
|
||||||
].join(' | '),
|
|
||||||
formatter: (value, key, item) => {
|
formatter: (value, key, item) => {
|
||||||
return value.join(' | ')
|
return value.join(' | ')
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import dotenv from 'dotenv'
|
|||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0016-transaction_signatures',
|
DB_VERSION: '0019-replace_login_user_id_with_state_user_id',
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = {
|
const server = {
|
||||||
|
|||||||
@ -21,8 +21,8 @@ import { UserTransaction } from '@entity/UserTransaction'
|
|||||||
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
||||||
import { BalanceRepository } from '../../typeorm/repository/Balance'
|
import { BalanceRepository } from '../../typeorm/repository/Balance'
|
||||||
import { calculateDecay } from '../../util/decay'
|
import { calculateDecay } from '../../util/decay'
|
||||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
|
||||||
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
||||||
|
import { User as dbUser } from '@entity/User'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class AdminResolver {
|
export class AdminResolver {
|
||||||
@ -378,7 +378,6 @@ function isCreationValid(creations: number[], amount: number, creationDate: Date
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function hasActivatedEmail(email: string): Promise<boolean> {
|
async function hasActivatedEmail(email: string): Promise<boolean> {
|
||||||
const repository = getCustomRepository(LoginUserRepository)
|
const user = await dbUser.findOne({ email })
|
||||||
const user = await repository.findByEmail(email)
|
|
||||||
return user ? user.emailChecked : false
|
return user ? user.emailChecked : false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,6 @@ import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
|
|||||||
import { TransactionTypeId } from '../enum/TransactionTypeId'
|
import { TransactionTypeId } from '../enum/TransactionTypeId'
|
||||||
import { TransactionType } from '../enum/TransactionType'
|
import { TransactionType } from '../enum/TransactionType'
|
||||||
import { hasUserAmount, isHexPublicKey } from '../../util/validate'
|
import { hasUserAmount, isHexPublicKey } from '../../util/validate'
|
||||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
|
||||||
import { RIGHTS } from '../../auth/RIGHTS'
|
import { RIGHTS } from '../../auth/RIGHTS'
|
||||||
|
|
||||||
// Helper function
|
// Helper function
|
||||||
@ -290,14 +289,13 @@ async function addUserTransaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getPublicKey(email: string): Promise<string | null> {
|
async function getPublicKey(email: string): Promise<string | null> {
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
const user = await dbUser.findOne({ email: email })
|
||||||
const loginUser = await loginUserRepository.findOne({ email: email })
|
|
||||||
// User not found
|
// User not found
|
||||||
if (!loginUser) {
|
if (!user) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return loginUser.pubKey.toString('hex')
|
return user.pubKey.toString('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
@ -364,7 +362,7 @@ export class TransactionResolver {
|
|||||||
// validate sender user (logged in)
|
// validate sender user (logged in)
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
|
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
if (senderUser.pubkey.length !== 32) {
|
if (senderUser.pubKey.length !== 32) {
|
||||||
throw new Error('invalid sender public key')
|
throw new Error('invalid sender public key')
|
||||||
}
|
}
|
||||||
if (!hasUserAmount(senderUser, amount)) {
|
if (!hasUserAmount(senderUser, amount)) {
|
||||||
@ -454,7 +452,7 @@ export class TransactionResolver {
|
|||||||
const transactionSendCoin = new dbTransactionSendCoin()
|
const transactionSendCoin = new dbTransactionSendCoin()
|
||||||
transactionSendCoin.transactionId = transaction.id
|
transactionSendCoin.transactionId = transaction.id
|
||||||
transactionSendCoin.userId = senderUser.id
|
transactionSendCoin.userId = senderUser.id
|
||||||
transactionSendCoin.senderPublic = senderUser.pubkey
|
transactionSendCoin.senderPublic = senderUser.pubKey
|
||||||
transactionSendCoin.recipiantUserId = recipiantUser.id
|
transactionSendCoin.recipiantUserId = recipiantUser.id
|
||||||
transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex')
|
transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex')
|
||||||
transactionSendCoin.amount = centAmount
|
transactionSendCoin.amount = centAmount
|
||||||
|
|||||||
@ -14,11 +14,8 @@ import UnsecureLoginArgs from '../arg/UnsecureLoginArgs'
|
|||||||
import UpdateUserInfosArgs from '../arg/UpdateUserInfosArgs'
|
import UpdateUserInfosArgs from '../arg/UpdateUserInfosArgs'
|
||||||
import { klicktippNewsletterStateMiddleware } from '../../middleware/klicktippMiddleware'
|
import { klicktippNewsletterStateMiddleware } from '../../middleware/klicktippMiddleware'
|
||||||
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
|
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
|
||||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
|
||||||
import { Setting } from '../enum/Setting'
|
import { Setting } from '../enum/Setting'
|
||||||
import { UserRepository } from '../../typeorm/repository/User'
|
import { UserRepository } from '../../typeorm/repository/User'
|
||||||
import { LoginUser } from '@entity/LoginUser'
|
|
||||||
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
|
||||||
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||||
import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail'
|
import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail'
|
||||||
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
|
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
|
||||||
@ -27,7 +24,6 @@ import { klicktippSignIn } from '../../apis/KlicktippController'
|
|||||||
import { RIGHTS } from '../../auth/RIGHTS'
|
import { RIGHTS } from '../../auth/RIGHTS'
|
||||||
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
|
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
|
||||||
import { ROLE_ADMIN } from '../../auth/ROLES'
|
import { ROLE_ADMIN } from '../../auth/ROLES'
|
||||||
import { randomBytes } from 'crypto'
|
|
||||||
|
|
||||||
const EMAIL_OPT_IN_RESET_PASSWORD = 2
|
const EMAIL_OPT_IN_RESET_PASSWORD = 2
|
||||||
const EMAIL_OPT_IN_REGISTER = 1
|
const EMAIL_OPT_IN_REGISTER = 1
|
||||||
@ -186,10 +182,10 @@ const createEmailOptIn = async (
|
|||||||
return emailOptIn
|
return emailOptIn
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOptInCode = async (loginUser: LoginUser): Promise<LoginEmailOptIn> => {
|
const getOptInCode = async (loginUserId: number): Promise<LoginEmailOptIn> => {
|
||||||
const loginEmailOptInRepository = await getRepository(LoginEmailOptIn)
|
const loginEmailOptInRepository = await getRepository(LoginEmailOptIn)
|
||||||
let optInCode = await loginEmailOptInRepository.findOne({
|
let optInCode = await loginEmailOptInRepository.findOne({
|
||||||
userId: loginUser.id,
|
userId: loginUserId,
|
||||||
emailOptInTypeId: EMAIL_OPT_IN_RESET_PASSWORD,
|
emailOptInTypeId: EMAIL_OPT_IN_RESET_PASSWORD,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -207,7 +203,7 @@ const getOptInCode = async (loginUser: LoginUser): Promise<LoginEmailOptIn> => {
|
|||||||
} else {
|
} else {
|
||||||
optInCode = new LoginEmailOptIn()
|
optInCode = new LoginEmailOptIn()
|
||||||
optInCode.verificationCode = random(64)
|
optInCode.verificationCode = random(64)
|
||||||
optInCode.userId = loginUser.id
|
optInCode.userId = loginUserId
|
||||||
optInCode.emailOptInTypeId = EMAIL_OPT_IN_RESET_PASSWORD
|
optInCode.emailOptInTypeId = EMAIL_OPT_IN_RESET_PASSWORD
|
||||||
}
|
}
|
||||||
await loginEmailOptInRepository.save(optInCode)
|
await loginEmailOptInRepository.save(optInCode)
|
||||||
@ -223,17 +219,15 @@ export class UserResolver {
|
|||||||
// TODO refactor and do not have duplicate code with login(see below)
|
// TODO refactor and do not have duplicate code with login(see below)
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
|
||||||
const loginUser = await loginUserRepository.findByEmail(userEntity.email)
|
|
||||||
const user = new User()
|
const user = new User()
|
||||||
user.id = userEntity.id
|
user.id = userEntity.id
|
||||||
user.email = userEntity.email
|
user.email = userEntity.email
|
||||||
user.firstName = userEntity.firstName
|
user.firstName = userEntity.firstName
|
||||||
user.lastName = userEntity.lastName
|
user.lastName = userEntity.lastName
|
||||||
user.username = userEntity.username
|
user.username = userEntity.username
|
||||||
user.description = loginUser.description
|
user.description = userEntity.description
|
||||||
user.pubkey = userEntity.pubkey.toString('hex')
|
user.pubkey = userEntity.pubKey.toString('hex')
|
||||||
user.language = loginUser.language
|
user.language = userEntity.language
|
||||||
|
|
||||||
// Elopage Status & Stored PublisherId
|
// Elopage Status & Stored PublisherId
|
||||||
user.hasElopage = await this.hasElopage(context)
|
user.hasElopage = await this.hasElopage(context)
|
||||||
@ -259,76 +253,50 @@ export class UserResolver {
|
|||||||
@Ctx() context: any,
|
@Ctx() context: any,
|
||||||
): Promise<User> {
|
): Promise<User> {
|
||||||
email = email.trim().toLowerCase()
|
email = email.trim().toLowerCase()
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
const dbUser = await DbUser.findOneOrFail({ email }).catch(() => {
|
||||||
const loginUser = await loginUserRepository.findByEmail(email).catch(() => {
|
|
||||||
throw new Error('No user with this credentials')
|
throw new Error('No user with this credentials')
|
||||||
})
|
})
|
||||||
if (!loginUser.emailChecked) {
|
if (!dbUser.emailChecked) {
|
||||||
throw new Error('User email not validated')
|
throw new Error('User email not validated')
|
||||||
}
|
}
|
||||||
if (loginUser.password === BigInt(0)) {
|
if (dbUser.password === BigInt(0)) {
|
||||||
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
||||||
throw new Error('User has no password set yet')
|
throw new Error('User has no password set yet')
|
||||||
}
|
}
|
||||||
if (!loginUser.pubKey || !loginUser.privKey) {
|
if (!dbUser.pubKey || !dbUser.privKey) {
|
||||||
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
||||||
throw new Error('User has no private or publicKey')
|
throw new Error('User has no private or publicKey')
|
||||||
}
|
}
|
||||||
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
|
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
|
||||||
const loginUserPassword = BigInt(loginUser.password.toString())
|
const loginUserPassword = BigInt(dbUser.password.toString())
|
||||||
if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) {
|
if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) {
|
||||||
throw new Error('No user with this credentials')
|
throw new Error('No user with this credentials')
|
||||||
}
|
}
|
||||||
// TODO: If user has no pubKey Create it again and update user.
|
|
||||||
|
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
|
||||||
let userEntity: void | DbUser
|
|
||||||
const loginUserPubKey = loginUser.pubKey
|
|
||||||
const loginUserPubKeyString = loginUserPubKey.toString('hex')
|
|
||||||
userEntity = await userRepository.findByPubkeyHex(loginUserPubKeyString).catch(() => {
|
|
||||||
// User not stored in state_users
|
|
||||||
// TODO: Check with production data - email is unique which can cause problems
|
|
||||||
userEntity = new DbUser()
|
|
||||||
userEntity.firstName = loginUser.firstName
|
|
||||||
userEntity.lastName = loginUser.lastName
|
|
||||||
userEntity.username = loginUser.username
|
|
||||||
userEntity.email = loginUser.email
|
|
||||||
userEntity.pubkey = loginUser.pubKey
|
|
||||||
|
|
||||||
userRepository.save(userEntity).catch(() => {
|
|
||||||
throw new Error('error by save userEntity')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (!userEntity) {
|
|
||||||
throw new Error('error with cannot happen')
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = new User()
|
const user = new User()
|
||||||
user.id = userEntity.id
|
user.id = dbUser.id
|
||||||
user.email = email
|
user.email = email
|
||||||
user.firstName = loginUser.firstName
|
user.firstName = dbUser.firstName
|
||||||
user.lastName = loginUser.lastName
|
user.lastName = dbUser.lastName
|
||||||
user.username = loginUser.username
|
user.username = dbUser.username
|
||||||
user.description = loginUser.description
|
user.description = dbUser.description
|
||||||
user.pubkey = loginUserPubKeyString
|
user.pubkey = dbUser.pubKey.toString('hex')
|
||||||
user.language = loginUser.language
|
user.language = dbUser.language
|
||||||
|
|
||||||
// Elopage Status & Stored PublisherId
|
// Elopage Status & Stored PublisherId
|
||||||
user.hasElopage = await this.hasElopage({ pubKey: loginUserPubKeyString })
|
user.hasElopage = await this.hasElopage({ pubKey: dbUser.pubKey.toString('hex') })
|
||||||
if (!user.hasElopage && publisherId) {
|
if (!user.hasElopage && publisherId) {
|
||||||
user.publisherId = publisherId
|
user.publisherId = publisherId
|
||||||
// TODO: Check if we can use updateUserInfos
|
// TODO: Check if we can use updateUserInfos
|
||||||
// await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey })
|
// await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey })
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
dbUser.publisherId = publisherId
|
||||||
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
|
DbUser.save(dbUser)
|
||||||
loginUser.publisherId = publisherId
|
|
||||||
loginUserRepository.save(loginUser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// coinAnimation
|
// coinAnimation
|
||||||
const userSettingRepository = getCustomRepository(UserSettingRepository)
|
const userSettingRepository = getCustomRepository(UserSettingRepository)
|
||||||
const coinanimation = await userSettingRepository
|
const coinanimation = await userSettingRepository
|
||||||
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
|
.readBoolean(dbUser.id, Setting.COIN_ANIMATION)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
})
|
})
|
||||||
@ -341,7 +309,7 @@ export class UserResolver {
|
|||||||
|
|
||||||
context.setHeaders.push({
|
context.setHeaders.push({
|
||||||
key: 'token',
|
key: 'token',
|
||||||
value: encode(loginUser.pubKey),
|
value: encode(dbUser.pubKey),
|
||||||
})
|
})
|
||||||
|
|
||||||
return user
|
return user
|
||||||
@ -393,18 +361,21 @@ export class UserResolver {
|
|||||||
// const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
|
// const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
|
||||||
const emailHash = getEmailHash(email)
|
const emailHash = getEmailHash(email)
|
||||||
|
|
||||||
// Table: login_users
|
// Table: state_users
|
||||||
const loginUser = new LoginUser()
|
const dbUser = new DbUser()
|
||||||
loginUser.email = email
|
dbUser.email = email
|
||||||
loginUser.firstName = firstName
|
dbUser.firstName = firstName
|
||||||
loginUser.lastName = lastName
|
dbUser.lastName = lastName
|
||||||
loginUser.username = username
|
dbUser.username = username
|
||||||
loginUser.description = ''
|
dbUser.description = ''
|
||||||
|
dbUser.emailHash = emailHash
|
||||||
|
dbUser.language = language
|
||||||
|
dbUser.publisherId = publisherId
|
||||||
|
dbUser.passphrase = passphrase.join(' ')
|
||||||
|
// TODO this field has no null allowed unlike the loginServer table
|
||||||
|
// dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000...
|
||||||
|
// dbUser.pubkey = keyPair[0]
|
||||||
// loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
|
// loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
|
||||||
loginUser.emailHash = emailHash
|
|
||||||
loginUser.language = language
|
|
||||||
loginUser.groupId = 1
|
|
||||||
loginUser.publisherId = publisherId
|
|
||||||
// loginUser.pubKey = keyPair[0]
|
// loginUser.pubKey = keyPair[0]
|
||||||
// loginUser.privKey = encryptedPrivkey
|
// loginUser.privKey = encryptedPrivkey
|
||||||
|
|
||||||
@ -412,43 +383,15 @@ export class UserResolver {
|
|||||||
await queryRunner.connect()
|
await queryRunner.connect()
|
||||||
await queryRunner.startTransaction('READ UNCOMMITTED')
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
try {
|
try {
|
||||||
const { id: loginUserId } = await queryRunner.manager.save(loginUser).catch((error) => {
|
await queryRunner.manager.save(dbUser).catch((error) => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('insert LoginUser failed', error)
|
console.log('Error while saving dbUser', error)
|
||||||
throw new Error('insert user failed')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Table: login_user_backups
|
|
||||||
const loginUserBackup = new LoginUserBackup()
|
|
||||||
loginUserBackup.userId = loginUserId
|
|
||||||
loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space
|
|
||||||
loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER;
|
|
||||||
|
|
||||||
await queryRunner.manager.save(loginUserBackup).catch((error) => {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('insert LoginUserBackup failed', error)
|
|
||||||
throw new Error('insert user backup failed')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Table: state_users
|
|
||||||
const dbUser = new DbUser()
|
|
||||||
dbUser.email = email
|
|
||||||
dbUser.firstName = firstName
|
|
||||||
dbUser.lastName = lastName
|
|
||||||
dbUser.username = username
|
|
||||||
// TODO this field has no null allowed unlike the loginServer table
|
|
||||||
dbUser.pubkey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000...
|
|
||||||
// dbUser.pubkey = keyPair[0]
|
|
||||||
|
|
||||||
await queryRunner.manager.save(dbUser).catch((er) => {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('Error while saving dbUser', er)
|
|
||||||
throw new Error('error saving user')
|
throw new Error('error saving user')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Store EmailOptIn in DB
|
// Store EmailOptIn in DB
|
||||||
// TODO: this has duplicate code with sendResetPasswordEmail
|
// TODO: this has duplicate code with sendResetPasswordEmail
|
||||||
const emailOptIn = await createEmailOptIn(loginUserId, queryRunner)
|
const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner)
|
||||||
|
|
||||||
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
||||||
/{code}/g,
|
/{code}/g,
|
||||||
@ -480,15 +423,14 @@ export class UserResolver {
|
|||||||
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
||||||
@Mutation(() => Boolean)
|
@Mutation(() => Boolean)
|
||||||
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
|
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
const user = await DbUser.findOneOrFail({ email: email })
|
||||||
const loginUser = await loginUserRepository.findOneOrFail({ email: email })
|
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
await queryRunner.connect()
|
await queryRunner.connect()
|
||||||
await queryRunner.startTransaction('READ UNCOMMITTED')
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const emailOptIn = await createEmailOptIn(loginUser.id, queryRunner)
|
const emailOptIn = await createEmailOptIn(user.id, queryRunner)
|
||||||
|
|
||||||
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
||||||
/{code}/g,
|
/{code}/g,
|
||||||
@ -497,8 +439,8 @@ export class UserResolver {
|
|||||||
|
|
||||||
const emailSent = await sendAccountActivationEmail({
|
const emailSent = await sendAccountActivationEmail({
|
||||||
link: activationLink,
|
link: activationLink,
|
||||||
firstName: loginUser.firstName,
|
firstName: user.firstName,
|
||||||
lastName: loginUser.lastName,
|
lastName: user.lastName,
|
||||||
email,
|
email,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -522,10 +464,9 @@ export class UserResolver {
|
|||||||
async sendResetPasswordEmail(@Arg('email') email: string): Promise<boolean> {
|
async sendResetPasswordEmail(@Arg('email') email: string): Promise<boolean> {
|
||||||
// TODO: this has duplicate code with createUser
|
// TODO: this has duplicate code with createUser
|
||||||
|
|
||||||
const loginUserRepository = await getCustomRepository(LoginUserRepository)
|
const user = await DbUser.findOneOrFail({ email })
|
||||||
const loginUser = await loginUserRepository.findOneOrFail({ email })
|
|
||||||
|
|
||||||
const optInCode = await getOptInCode(loginUser)
|
const optInCode = await getOptInCode(user.id)
|
||||||
|
|
||||||
const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace(
|
const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace(
|
||||||
/{code}/g,
|
/{code}/g,
|
||||||
@ -534,8 +475,8 @@ export class UserResolver {
|
|||||||
|
|
||||||
const emailSent = await sendResetPasswordEmail({
|
const emailSent = await sendResetPasswordEmail({
|
||||||
link,
|
link,
|
||||||
firstName: loginUser.firstName,
|
firstName: user.firstName,
|
||||||
lastName: loginUser.lastName,
|
lastName: user.lastName,
|
||||||
email,
|
email,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -575,34 +516,18 @@ export class UserResolver {
|
|||||||
throw new Error('Code is older than 10 minutes')
|
throw new Error('Code is older than 10 minutes')
|
||||||
}
|
}
|
||||||
|
|
||||||
// load loginUser
|
|
||||||
const loginUserRepository = await getCustomRepository(LoginUserRepository)
|
|
||||||
const loginUser = await loginUserRepository
|
|
||||||
.findOneOrFail({ id: optInCode.userId })
|
|
||||||
.catch(() => {
|
|
||||||
throw new Error('Could not find corresponding Login User')
|
|
||||||
})
|
|
||||||
|
|
||||||
// load user
|
// load user
|
||||||
const dbUserRepository = await getCustomRepository(UserRepository)
|
const user = await DbUser.findOneOrFail({ id: optInCode.userId }).catch(() => {
|
||||||
const dbUser = await dbUserRepository.findOneOrFail({ email: loginUser.email }).catch(() => {
|
throw new Error('Could not find corresponding Login User')
|
||||||
throw new Error('Could not find corresponding User')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const loginUserBackupRepository = await getRepository(LoginUserBackup)
|
|
||||||
let loginUserBackup = await loginUserBackupRepository.findOne({ userId: loginUser.id })
|
|
||||||
|
|
||||||
// Generate Passphrase if needed
|
// Generate Passphrase if needed
|
||||||
if (!loginUserBackup) {
|
if (!user.passphrase) {
|
||||||
const passphrase = PassphraseGenerate()
|
const passphrase = PassphraseGenerate()
|
||||||
loginUserBackup = new LoginUserBackup()
|
user.passphrase = passphrase.join(' ')
|
||||||
loginUserBackup.userId = loginUser.id
|
|
||||||
loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space
|
|
||||||
loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER;
|
|
||||||
loginUserBackupRepository.save(loginUserBackup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const passphrase = loginUserBackup.passphrase.split(' ')
|
const passphrase = user.passphrase.split(' ')
|
||||||
if (passphrase.length < PHRASE_WORD_COUNT) {
|
if (passphrase.length < PHRASE_WORD_COUNT) {
|
||||||
// TODO if this can happen we cannot recover from that
|
// TODO if this can happen we cannot recover from that
|
||||||
// this seem to be good on production data, if we dont
|
// this seem to be good on production data, if we dont
|
||||||
@ -611,29 +536,23 @@ export class UserResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Activate EMail
|
// Activate EMail
|
||||||
loginUser.emailChecked = true
|
user.emailChecked = true
|
||||||
|
|
||||||
// Update Password
|
// Update Password
|
||||||
const passwordHash = SecretKeyCryptographyCreateKey(loginUser.email, password) // return short and long hash
|
const passwordHash = SecretKeyCryptographyCreateKey(user.email, password) // return short and long hash
|
||||||
const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key
|
const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key
|
||||||
const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
|
const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
|
||||||
loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
|
user.password = passwordHash[0].readBigUInt64LE() // using the shorthash
|
||||||
loginUser.pubKey = keyPair[0]
|
user.pubKey = keyPair[0]
|
||||||
loginUser.privKey = encryptedPrivkey
|
user.privKey = encryptedPrivkey
|
||||||
dbUser.pubkey = keyPair[0]
|
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
await queryRunner.connect()
|
await queryRunner.connect()
|
||||||
await queryRunner.startTransaction('READ UNCOMMITTED')
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Save loginUser
|
|
||||||
await queryRunner.manager.save(loginUser).catch((error) => {
|
|
||||||
throw new Error('error saving loginUser: ' + error)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Save user
|
// Save user
|
||||||
await queryRunner.manager.save(dbUser).catch((error) => {
|
await queryRunner.manager.save(user).catch((error) => {
|
||||||
throw new Error('error saving user: ' + error)
|
throw new Error('error saving user: ' + error)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -654,12 +573,7 @@ export class UserResolver {
|
|||||||
// TODO do we always signUp the user? How to handle things with old users?
|
// TODO do we always signUp the user? How to handle things with old users?
|
||||||
if (optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER) {
|
if (optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER) {
|
||||||
try {
|
try {
|
||||||
await klicktippSignIn(
|
await klicktippSignIn(user.email, user.language, user.firstName, user.lastName)
|
||||||
loginUser.email,
|
|
||||||
loginUser.language,
|
|
||||||
loginUser.firstName,
|
|
||||||
loginUser.lastName,
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
// TODO is this a problem?
|
// TODO is this a problem?
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@ -689,8 +603,6 @@ export class UserResolver {
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
|
||||||
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
|
|
||||||
|
|
||||||
if (username) {
|
if (username) {
|
||||||
throw new Error('change username currently not supported!')
|
throw new Error('change username currently not supported!')
|
||||||
@ -704,46 +616,44 @@ export class UserResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (firstName) {
|
if (firstName) {
|
||||||
loginUser.firstName = firstName
|
|
||||||
userEntity.firstName = firstName
|
userEntity.firstName = firstName
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastName) {
|
if (lastName) {
|
||||||
loginUser.lastName = lastName
|
|
||||||
userEntity.lastName = lastName
|
userEntity.lastName = lastName
|
||||||
}
|
}
|
||||||
|
|
||||||
if (description) {
|
if (description) {
|
||||||
loginUser.description = description
|
userEntity.description = description
|
||||||
}
|
}
|
||||||
|
|
||||||
if (language) {
|
if (language) {
|
||||||
if (!isLanguage(language)) {
|
if (!isLanguage(language)) {
|
||||||
throw new Error(`"${language}" isn't a valid language`)
|
throw new Error(`"${language}" isn't a valid language`)
|
||||||
}
|
}
|
||||||
loginUser.language = language
|
userEntity.language = language
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password && passwordNew) {
|
if (password && passwordNew) {
|
||||||
// TODO: This had some error cases defined - like missing private key. This is no longer checked.
|
// TODO: This had some error cases defined - like missing private key. This is no longer checked.
|
||||||
const oldPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, password)
|
const oldPasswordHash = SecretKeyCryptographyCreateKey(userEntity.email, password)
|
||||||
if (BigInt(loginUser.password.toString()) !== oldPasswordHash[0].readBigUInt64LE()) {
|
if (BigInt(userEntity.password.toString()) !== oldPasswordHash[0].readBigUInt64LE()) {
|
||||||
throw new Error(`Old password is invalid`)
|
throw new Error(`Old password is invalid`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const privKey = SecretKeyCryptographyDecrypt(loginUser.privKey, oldPasswordHash[1])
|
const privKey = SecretKeyCryptographyDecrypt(userEntity.privKey, oldPasswordHash[1])
|
||||||
|
|
||||||
const newPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, passwordNew) // return short and long hash
|
const newPasswordHash = SecretKeyCryptographyCreateKey(userEntity.email, passwordNew) // return short and long hash
|
||||||
const encryptedPrivkey = SecretKeyCryptographyEncrypt(privKey, newPasswordHash[1])
|
const encryptedPrivkey = SecretKeyCryptographyEncrypt(privKey, newPasswordHash[1])
|
||||||
|
|
||||||
// Save new password hash and newly encrypted private key
|
// Save new password hash and newly encrypted private key
|
||||||
loginUser.password = newPasswordHash[0].readBigUInt64LE()
|
userEntity.password = newPasswordHash[0].readBigUInt64LE()
|
||||||
loginUser.privKey = encryptedPrivkey
|
userEntity.privKey = encryptedPrivkey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save publisherId only if Elopage is not yet registered
|
// Save publisherId only if Elopage is not yet registered
|
||||||
if (publisherId && !(await this.hasElopage(context))) {
|
if (publisherId && !(await this.hasElopage(context))) {
|
||||||
loginUser.publisherId = publisherId
|
userEntity.publisherId = publisherId
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
@ -760,10 +670,6 @@ export class UserResolver {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await queryRunner.manager.save(loginUser).catch((error) => {
|
|
||||||
throw new Error('error saving loginUser: ' + error)
|
|
||||||
})
|
|
||||||
|
|
||||||
await queryRunner.manager.save(userEntity).catch((error) => {
|
await queryRunner.manager.save(userEntity).catch((error) => {
|
||||||
throw new Error('error saving user: ' + error)
|
throw new Error('error saving user: ' + error)
|
||||||
})
|
})
|
||||||
@ -793,7 +699,7 @@ export class UserResolver {
|
|||||||
throw new Error(`Username must be at minimum ${MIN_CHARACTERS_USERNAME} characters long.`)
|
throw new Error(`Username must be at minimum ${MIN_CHARACTERS_USERNAME} characters long.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const usersFound = await LoginUser.count({ username })
|
const usersFound = await DbUser.count({ username })
|
||||||
|
|
||||||
// Username already present?
|
// Username already present?
|
||||||
if (usersFound !== 0) {
|
if (usersFound !== 0) {
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
import { EntityRepository, Repository } from '@dbTools/typeorm'
|
|
||||||
import { LoginUser } from '@entity/LoginUser'
|
|
||||||
|
|
||||||
@EntityRepository(LoginUser)
|
|
||||||
export class LoginUserRepository extends Repository<LoginUser> {
|
|
||||||
async findByEmail(email: string): Promise<LoginUser> {
|
|
||||||
return this.createQueryBuilder('loginUser')
|
|
||||||
.where('loginUser.email = :email', { email })
|
|
||||||
.getOneOrFail()
|
|
||||||
}
|
|
||||||
|
|
||||||
async findBySearchCriteria(searchCriteria: string): Promise<LoginUser[]> {
|
|
||||||
return await this.createQueryBuilder('user')
|
|
||||||
.where(
|
|
||||||
'user.firstName like :name or user.lastName like :lastName or user.email like :email',
|
|
||||||
{
|
|
||||||
name: `%${searchCriteria}%`,
|
|
||||||
lastName: `%${searchCriteria}%`,
|
|
||||||
email: `%${searchCriteria}%`,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.getMany()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
import { EntityRepository, Repository } from '@dbTools/typeorm'
|
|
||||||
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
|
||||||
|
|
||||||
@EntityRepository(LoginUserBackup)
|
|
||||||
export class LoginUserBackupRepository extends Repository<LoginUserBackup> {}
|
|
||||||
@ -5,7 +5,7 @@ import { User } from '@entity/User'
|
|||||||
export class UserRepository extends Repository<User> {
|
export class UserRepository extends Repository<User> {
|
||||||
async findByPubkeyHex(pubkeyHex: string): Promise<User> {
|
async findByPubkeyHex(pubkeyHex: string): Promise<User> {
|
||||||
return this.createQueryBuilder('user')
|
return this.createQueryBuilder('user')
|
||||||
.where('hex(user.pubkey) = :pubkeyHex', { pubkeyHex })
|
.where('hex(user.pubKey) = :pubkeyHex', { pubkeyHex })
|
||||||
.getOneOrFail()
|
.getOneOrFail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import { LoginElopageBuys } from '@entity/LoginElopageBuys'
|
|||||||
import { getCustomRepository } from '@dbTools/typeorm'
|
import { getCustomRepository } from '@dbTools/typeorm'
|
||||||
import { UserResolver } from '../graphql/resolver/UserResolver'
|
import { UserResolver } from '../graphql/resolver/UserResolver'
|
||||||
import { LoginElopageBuysRepository } from '../typeorm/repository/LoginElopageBuys'
|
import { LoginElopageBuysRepository } from '../typeorm/repository/LoginElopageBuys'
|
||||||
import { LoginUserRepository } from '../typeorm/repository/LoginUser'
|
import { User as dbUser } from '@entity/User'
|
||||||
|
|
||||||
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
|
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@ -114,8 +114,7 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do we already have such a user?
|
// Do we already have such a user?
|
||||||
const loginUserRepository = await getCustomRepository(LoginUserRepository)
|
if ((await dbUser.count({ email })) !== 0) {
|
||||||
if ((await loginUserRepository.count({ email })) !== 0) {
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Did not create User - already exists with email: ${email}`)
|
console.log(`Did not create User - already exists with email: ${email}`)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -7,13 +7,13 @@ export class User extends BaseEntity {
|
|||||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
@Column({ name: 'index_id', default: 0 })
|
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
|
||||||
indexId: number
|
indexId: number
|
||||||
|
|
||||||
@Column({ name: 'group_id', default: 0, unsigned: true })
|
@Column({ name: 'group_id', default: 0, unsigned: true })
|
||||||
groupId: number
|
groupId: number
|
||||||
|
|
||||||
@Column({ type: 'binary', length: 32, name: 'public_key' })
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
pubkey: Buffer
|
pubkey: Buffer
|
||||||
|
|
||||||
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
@ -40,7 +40,7 @@ export class User extends BaseEntity {
|
|||||||
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
username: string
|
username: string
|
||||||
|
|
||||||
@Column()
|
@Column({ type: 'bool', default: false })
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
|
|
||||||
@OneToOne(() => Balance, (balance) => balance.user)
|
@OneToOne(() => Balance, (balance) => balance.user)
|
||||||
|
|||||||
@ -7,13 +7,13 @@ export class User extends BaseEntity {
|
|||||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
@Column({ name: 'index_id', default: 0 })
|
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
|
||||||
indexId: number
|
indexId: number
|
||||||
|
|
||||||
@Column({ name: 'group_id', default: 0, unsigned: true })
|
@Column({ name: 'group_id', default: 0, unsigned: true })
|
||||||
groupId: number
|
groupId: number
|
||||||
|
|
||||||
@Column({ type: 'binary', length: 32, name: 'public_key' })
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
pubkey: Buffer
|
pubkey: Buffer
|
||||||
|
|
||||||
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
@ -40,7 +40,7 @@ export class User extends BaseEntity {
|
|||||||
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
username: string
|
username: string
|
||||||
|
|
||||||
@Column()
|
@Column({ type: 'bool', default: false })
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
|
|
||||||
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
|
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
||||||
import { LoginUserBackup } from '../LoginUserBackup'
|
import { LoginUserBackup } from './LoginUserBackup'
|
||||||
|
|
||||||
// Moriz: I do not like the idea of having two user tables
|
// Moriz: I do not like the idea of having two user tables
|
||||||
@Entity('login_users')
|
@Entity('login_users')
|
||||||
@ -19,7 +19,7 @@ export class LoginUser extends BaseEntity {
|
|||||||
@Column({ length: 255, default: '' })
|
@Column({ length: 255, default: '' })
|
||||||
username: string
|
username: string
|
||||||
|
|
||||||
@Column({ default: '', nullable: true })
|
@Column({ type: 'mediumtext', default: '', nullable: true })
|
||||||
description: string
|
description: string
|
||||||
|
|
||||||
@Column({ type: 'bigint', default: 0, unsigned: true })
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
@ -34,19 +34,19 @@ export class LoginUser extends BaseEntity {
|
|||||||
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
emailHash: Buffer
|
emailHash: Buffer
|
||||||
|
|
||||||
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
@Column({ name: 'email_checked', default: 0 })
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
emailChecked: boolean
|
emailChecked: boolean
|
||||||
|
|
||||||
@Column({ name: 'passphrase_shown', default: 0 })
|
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
|
||||||
passphraseShown: boolean
|
passphraseShown: boolean
|
||||||
|
|
||||||
@Column({ length: 4, default: 'de' })
|
@Column({ length: 4, default: 'de', nullable: false })
|
||||||
language: string
|
language: string
|
||||||
|
|
||||||
@Column({ default: 0 })
|
@Column({ type: 'bool', default: false })
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
|
|
||||||
@Column({ name: 'group_id', default: 0, unsigned: true })
|
@Column({ name: 'group_id', default: 0, unsigned: true })
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm'
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm'
|
||||||
import { LoginUser } from '../LoginUser'
|
import { LoginUser } from './LoginUser'
|
||||||
|
|
||||||
@Entity('login_user_backups')
|
@Entity('login_user_backups')
|
||||||
export class LoginUserBackup extends BaseEntity {
|
export class LoginUserBackup extends BaseEntity {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
||||||
import { LoginUserBackup } from '../LoginUserBackup'
|
import { LoginUserBackup } from '../0003-login_server_tables/LoginUserBackup'
|
||||||
|
|
||||||
// Moriz: I do not like the idea of having two user tables
|
// Moriz: I do not like the idea of having two user tables
|
||||||
@Entity('login_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
@Entity('login_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
@ -19,7 +19,7 @@ export class LoginUser extends BaseEntity {
|
|||||||
@Column({ length: 255, default: '', collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 255, default: '', collation: 'utf8mb4_unicode_ci' })
|
||||||
username: string
|
username: string
|
||||||
|
|
||||||
@Column({ default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
|
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
|
||||||
description: string
|
description: string
|
||||||
|
|
||||||
@Column({ type: 'bigint', default: 0, unsigned: true })
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
@ -34,19 +34,19 @@ export class LoginUser extends BaseEntity {
|
|||||||
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
emailHash: Buffer
|
emailHash: Buffer
|
||||||
|
|
||||||
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
@Column({ name: 'email_checked', default: 0 })
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
emailChecked: boolean
|
emailChecked: boolean
|
||||||
|
|
||||||
@Column({ name: 'passphrase_shown', default: 0 })
|
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
|
||||||
passphraseShown: boolean
|
passphraseShown: boolean
|
||||||
|
|
||||||
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||||
language: string
|
language: string
|
||||||
|
|
||||||
@Column({ default: 0 })
|
@Column({ type: 'bool', default: false })
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
|
|
||||||
@Column({ name: 'group_id', default: 0, unsigned: true })
|
@Column({ name: 'group_id', default: 0, unsigned: true })
|
||||||
|
|||||||
16
database/entity/0017-combine_user_tables/LoginUserBackup.ts
Normal file
16
database/entity/0017-combine_user_tables/LoginUserBackup.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||||
|
|
||||||
|
@Entity('login_user_backups')
|
||||||
|
export class LoginUserBackup extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ type: 'text', name: 'passphrase', nullable: false })
|
||||||
|
passphrase: string
|
||||||
|
|
||||||
|
@Column({ name: 'user_id', nullable: false })
|
||||||
|
userId: number
|
||||||
|
|
||||||
|
@Column({ name: 'mnemonic_type', default: -1 })
|
||||||
|
mnemonicType: number
|
||||||
|
}
|
||||||
74
database/entity/0017-combine_user_tables/User.ts
Normal file
74
database/entity/0017-combine_user_tables/User.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
|
||||||
|
import { UserSetting } from '../UserSetting'
|
||||||
|
|
||||||
|
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
|
export class User extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'login_user_id', default: null, unsigned: true })
|
||||||
|
loginUserId: number
|
||||||
|
|
||||||
|
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
|
||||||
|
indexId: number
|
||||||
|
|
||||||
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
pubKey: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
|
||||||
|
privKey: Buffer
|
||||||
|
|
||||||
|
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
email: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'first_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
firstName: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'last_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
lastName: string
|
||||||
|
|
||||||
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
username: string
|
||||||
|
|
||||||
|
@Column({ type: 'bool', default: false })
|
||||||
|
disabled: boolean
|
||||||
|
|
||||||
|
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
|
||||||
|
description: string
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
|
password: BigInt
|
||||||
|
|
||||||
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
emailHash: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
|
emailChecked: boolean
|
||||||
|
|
||||||
|
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
|
||||||
|
passphraseShown: boolean
|
||||||
|
|
||||||
|
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||||
|
language: string
|
||||||
|
|
||||||
|
@Column({ name: 'publisher_id', default: 0 })
|
||||||
|
publisherId: number
|
||||||
|
|
||||||
|
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
|
||||||
|
settings: UserSetting[]
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
|
||||||
|
import { UserSetting } from '../UserSetting'
|
||||||
|
|
||||||
|
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
|
export class User extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'login_user_id', default: null, unsigned: true })
|
||||||
|
loginUserId: number
|
||||||
|
|
||||||
|
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
|
||||||
|
indexId: number
|
||||||
|
|
||||||
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
pubKey: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
|
||||||
|
privKey: Buffer
|
||||||
|
|
||||||
|
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
email: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'first_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
firstName: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'last_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
lastName: string
|
||||||
|
|
||||||
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
username: string
|
||||||
|
|
||||||
|
@Column({ type: 'bool', default: false })
|
||||||
|
disabled: boolean
|
||||||
|
|
||||||
|
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
|
||||||
|
description: string
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
|
password: BigInt
|
||||||
|
|
||||||
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
emailHash: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
|
emailChecked: boolean
|
||||||
|
|
||||||
|
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
|
||||||
|
passphraseShown: boolean
|
||||||
|
|
||||||
|
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||||
|
language: string
|
||||||
|
|
||||||
|
@Column({ name: 'publisher_id', default: 0 })
|
||||||
|
publisherId: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
name: 'passphrase',
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
passphrase: string
|
||||||
|
|
||||||
|
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
|
||||||
|
settings: UserSetting[]
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
|
||||||
|
import { UserSetting } from '../UserSetting'
|
||||||
|
|
||||||
|
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
|
export class User extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
|
||||||
|
indexId: number
|
||||||
|
|
||||||
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
pubKey: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
|
||||||
|
privKey: Buffer
|
||||||
|
|
||||||
|
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
email: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'first_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
firstName: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'last_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
lastName: string
|
||||||
|
|
||||||
|
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
username: string
|
||||||
|
|
||||||
|
@Column({ type: 'bool', default: false })
|
||||||
|
disabled: boolean
|
||||||
|
|
||||||
|
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
|
||||||
|
description: string
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
|
password: BigInt
|
||||||
|
|
||||||
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
emailHash: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
|
emailChecked: boolean
|
||||||
|
|
||||||
|
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
|
||||||
|
passphraseShown: boolean
|
||||||
|
|
||||||
|
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||||
|
language: string
|
||||||
|
|
||||||
|
@Column({ name: 'publisher_id', default: 0 })
|
||||||
|
publisherId: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
name: 'passphrase',
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
passphrase: string
|
||||||
|
|
||||||
|
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
|
||||||
|
settings: UserSetting[]
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
export { LoginUser } from './0006-login_users_collation/LoginUser'
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export { LoginUserBackup } from './0003-login_server_tables/LoginUserBackup'
|
|
||||||
@ -1 +1 @@
|
|||||||
export { User } from './0002-add_settings/User'
|
export { User } from './0019-replace_login_user_id_with_state_user_id/User'
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { Balance } from './Balance'
|
import { Balance } from './Balance'
|
||||||
import { LoginElopageBuys } from './LoginElopageBuys'
|
import { LoginElopageBuys } from './LoginElopageBuys'
|
||||||
import { LoginEmailOptIn } from './LoginEmailOptIn'
|
import { LoginEmailOptIn } from './LoginEmailOptIn'
|
||||||
import { LoginUser } from './LoginUser'
|
|
||||||
import { LoginUserBackup } from './LoginUserBackup'
|
|
||||||
import { Migration } from './Migration'
|
import { Migration } from './Migration'
|
||||||
import { ServerUser } from './ServerUser'
|
import { ServerUser } from './ServerUser'
|
||||||
import { Transaction } from './Transaction'
|
import { Transaction } from './Transaction'
|
||||||
@ -18,8 +16,6 @@ export const entities = [
|
|||||||
Balance,
|
Balance,
|
||||||
LoginElopageBuys,
|
LoginElopageBuys,
|
||||||
LoginEmailOptIn,
|
LoginEmailOptIn,
|
||||||
LoginUser,
|
|
||||||
LoginUserBackup,
|
|
||||||
Migration,
|
Migration,
|
||||||
ServerUser,
|
ServerUser,
|
||||||
Transaction,
|
Transaction,
|
||||||
|
|||||||
150
database/migrations/0017-combine_user_tables.ts
Normal file
150
database/migrations/0017-combine_user_tables.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* MIGRATION TO COMBINE LOGIN_USERS WITH STATE_USERS TABLE
|
||||||
|
*
|
||||||
|
* This migration combines the table `login_users` with
|
||||||
|
* the `state_users` table, where the later is the target.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
// Drop column `group_id` since it contains uniform data which is not the same as the uniform data
|
||||||
|
// on login_users. Since we do not need this data anyway, we sjust throw it away.
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `group_id`;')
|
||||||
|
|
||||||
|
// Remove the unique constraint from the pubkey
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP INDEX `public_key`;')
|
||||||
|
|
||||||
|
// Allow NULL on the `state_users` pubkey like it is allowed on `login_users`
|
||||||
|
await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) DEFAULT NULL;')
|
||||||
|
|
||||||
|
// instead use a unique constraint for the email like on `login_users`
|
||||||
|
// therefore do not allow null on `email` anymore
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL;',
|
||||||
|
)
|
||||||
|
await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `email` UNIQUE KEY (`email`);')
|
||||||
|
|
||||||
|
// Create `login_user_id` column - to store the login_users.id field to not break references.
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;',
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create missing data columns for the data stored in `login_users`
|
||||||
|
await queryFn(
|
||||||
|
"ALTER TABLE `state_users` ADD COLUMN `description` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '' AFTER `disabled`;",
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `password` bigint(20) unsigned DEFAULT 0 AFTER `description`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `privkey` binary(80) DEFAULT NULL AFTER `public_key`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `email_hash` binary(32) DEFAULT NULL AFTER `password`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `created` datetime NOT NULL DEFAULT current_timestamp() AFTER `email_hash`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `email_checked` tinyint(4) NOT NULL DEFAULT 0 AFTER `created`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `passphrase_shown` tinyint(4) NOT NULL DEFAULT 0 AFTER `email_checked`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
"ALTER TABLE `state_users` ADD COLUMN `language` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'de' AFTER `passphrase_shown`;",
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `publisher_id` int(11) DEFAULT 0 AFTER `language`;',
|
||||||
|
)
|
||||||
|
|
||||||
|
// Move data from `login_users` to the newly modified `state_users` table.
|
||||||
|
// The following rules for overwriting data applies:
|
||||||
|
// email is the matching criteria
|
||||||
|
// public_key is overwritten by `login_users`.`pubkey` (we have validated the passphrases here) (2 keys differ)
|
||||||
|
// first_name is more accurate on `state_users` and stays unchanged (1 users with different first_* & last_name)
|
||||||
|
// last_name is more accurate on `state_users` and stays unchanged (1 users with different first_* & last_name)
|
||||||
|
// username does not contain any relevant data, either NULL or '' and therefore we do not change anything here
|
||||||
|
// disabled does not differ, we can omit it
|
||||||
|
await queryFn(`
|
||||||
|
UPDATE state_users
|
||||||
|
LEFT JOIN login_users ON state_users.email = login_users.email
|
||||||
|
SET state_users.public_key = login_users.pubkey,
|
||||||
|
state_users.login_user_id = login_users.id,
|
||||||
|
state_users.description = login_users.description,
|
||||||
|
state_users.password = login_users.password,
|
||||||
|
state_users.privkey = login_users.privkey,
|
||||||
|
state_users.email_hash = login_users.email_hash,
|
||||||
|
state_users.created = login_users.created,
|
||||||
|
state_users.email_checked = login_users.email_checked,
|
||||||
|
state_users.passphrase_shown = login_users.passphrase_shown,
|
||||||
|
state_users.language = login_users.language,
|
||||||
|
state_users.publisher_id = login_users.publisher_id
|
||||||
|
;
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Drop `login_users` table
|
||||||
|
await queryFn('DROP TABLE `login_users`;')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(`
|
||||||
|
CREATE TABLE \`login_users\` (
|
||||||
|
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
\`email\` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
\`first_name\` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
\`last_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '',
|
||||||
|
\`username\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '',
|
||||||
|
\`description\` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '',
|
||||||
|
\`password\` bigint(20) unsigned DEFAULT 0,
|
||||||
|
\`pubkey\` binary(32) DEFAULT NULL,
|
||||||
|
\`privkey\` binary(80) DEFAULT NULL,
|
||||||
|
\`email_hash\` binary(32) DEFAULT NULL,
|
||||||
|
\`created\` datetime NOT NULL DEFAULT current_timestamp(),
|
||||||
|
\`email_checked\` tinyint(4) NOT NULL DEFAULT 0,
|
||||||
|
\`passphrase_shown\` tinyint(4) NOT NULL DEFAULT 0,
|
||||||
|
\`language\` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'de',
|
||||||
|
\`disabled\` tinyint(4) DEFAULT 0,
|
||||||
|
\`group_id\` int(10) unsigned DEFAULT 0,
|
||||||
|
\`publisher_id\` int(11) DEFAULT 0,
|
||||||
|
PRIMARY KEY (\`id\`),
|
||||||
|
UNIQUE KEY \`email\` (\`email\`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2363 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
`)
|
||||||
|
await queryFn(`
|
||||||
|
INSERT INTO login_users
|
||||||
|
( id, email, first_name, last_name, username,
|
||||||
|
description, password, pubkey, privkey, email_hash,
|
||||||
|
created, email_checked, passphrase_shown, language,
|
||||||
|
disabled, group_id, publisher_id )
|
||||||
|
( SELECT login_user_id AS id, email, first_name,
|
||||||
|
last_name, username, description, password,
|
||||||
|
public_key AS pubkey, privkey, email_hash,
|
||||||
|
created, email_checked, passphrase_shown,
|
||||||
|
language, disabled, '1' AS group_id,
|
||||||
|
publisher_id
|
||||||
|
FROM state_users )
|
||||||
|
;
|
||||||
|
`)
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `publisher_id`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `language`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `passphrase_shown`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `email_checked`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `created`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `email_hash`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `privkey`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `password`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `description`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `login_user_id`;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP INDEX `email`;')
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL;',
|
||||||
|
)
|
||||||
|
// Note: if the public_key is NULL, we need to set a random key in order to meet the constraint
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `state_users` SET public_key = UNHEX(SHA1(RAND())) WHERE public_key IS NULL;',
|
||||||
|
)
|
||||||
|
await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) NOT NULL;')
|
||||||
|
await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `public_key` UNIQUE KEY (`public_key`);')
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `group_id` int(10) unsigned NOT NULL DEFAULT 0 AFTER index_id;',
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/* MIGRATION TO COMBINE LOGIN_BACKUP_USERS TABLE WITH STATE_USERS
|
||||||
|
*
|
||||||
|
* This migration combines the table `login_user_backups` into
|
||||||
|
* the `state_users` table, where the later is the target.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
// We only keep the passphrase, the mnemonic type is a constant,
|
||||||
|
// since every passphrase was converted to mnemonic type 2
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `passphrase` text DEFAULT NULL AFTER `publisher_id`;',
|
||||||
|
)
|
||||||
|
|
||||||
|
// Move data from `login_user_backups` to the newly modified `state_users` table.
|
||||||
|
await queryFn(`
|
||||||
|
UPDATE state_users
|
||||||
|
LEFT JOIN login_user_backups ON state_users.login_user_id = login_user_backups.user_id
|
||||||
|
SET state_users.passphrase = login_user_backups.passphrase
|
||||||
|
WHERE login_user_backups.passphrase IS NOT NULL
|
||||||
|
;
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Drop `login_user_backups` table
|
||||||
|
await queryFn('DROP TABLE `login_user_backups`;')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(`
|
||||||
|
CREATE TABLE \`login_user_backups\` (
|
||||||
|
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
\`user_id\` int(11) NOT NULL,
|
||||||
|
\`passphrase\` text NOT NULL,
|
||||||
|
\`mnemonic_type\` int(11) DEFAULT -1,
|
||||||
|
PRIMARY KEY (\`id\`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1862 DEFAULT CHARSET=utf8mb4;
|
||||||
|
`)
|
||||||
|
await queryFn(`
|
||||||
|
INSERT INTO login_user_backups
|
||||||
|
( user_id, passphrase, mnemonic_type )
|
||||||
|
( SELECT login_user_id AS user_id,
|
||||||
|
passphrase,
|
||||||
|
'2' as mnemonic_type
|
||||||
|
FROM state_users
|
||||||
|
WHERE passphrase IS NOT NULL )
|
||||||
|
;
|
||||||
|
`)
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `passphrase`;')
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
/* MIGRATION TO REPLACE LOGIN_USER_ID WITH STATE_USER_ID
|
||||||
|
*
|
||||||
|
* This migration replaces the `login_user_id with` the
|
||||||
|
* `state_user.id` and removes corresponding columns.
|
||||||
|
* The table affected is `login_email_opt_in`
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
// Delete email opt in codes which can not be linked to an user
|
||||||
|
await queryFn(`
|
||||||
|
DELETE FROM \`login_email_opt_in\`
|
||||||
|
WHERE user_id NOT IN
|
||||||
|
( SELECT login_user_id FROM state_users )
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Replace user_id in `login_email_opt_in`
|
||||||
|
await queryFn(`
|
||||||
|
UPDATE login_email_opt_in
|
||||||
|
LEFT JOIN state_users ON state_users.login_user_id = login_email_opt_in.user_id
|
||||||
|
SET login_email_opt_in.user_id = state_users.id;
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Remove the column `login_user_id` from `state_users`
|
||||||
|
await queryFn('ALTER TABLE `state_users` DROP COLUMN `login_user_id`;')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;',
|
||||||
|
)
|
||||||
|
// Instead of generating new `login_user_id`'s we just use the id of state user.
|
||||||
|
// This way we do not need to alter the `user_id`'s of `login_email_opt_in` table
|
||||||
|
// at all when migrating down.
|
||||||
|
// This is possible since there are no old `login_user.id` referenced anymore and
|
||||||
|
// we can freely choose them
|
||||||
|
await queryFn('UPDATE `state_users` SET login_user_id = id')
|
||||||
|
|
||||||
|
// Insert back broken data, since we generate new `user_id`'s the old data might be now
|
||||||
|
// linked to existing accounts. To prevent that all invalid `user_id`'s are now negative.
|
||||||
|
// This renders them invalid while still keeping the original value
|
||||||
|
await queryFn(`
|
||||||
|
INSERT INTO login_email_opt_in
|
||||||
|
(id, user_id, verification_code, email_opt_in_type_id, created, resend_count, updated)
|
||||||
|
VALUES
|
||||||
|
('38','-41','7544440030630126261','0','2019-11-09 13:58:21','0','2020-07-17 13:58:29'),
|
||||||
|
('1262','-1185','2702555860489093775','3','2020-10-17 00:57:29','0','2020-10-17 00:57:29'),
|
||||||
|
('1431','-1319','9846213635571107141','3','2020-12-29 00:07:32','0','2020-12-29 00:07:32'),
|
||||||
|
('1548','-1185','1009203004512986277','1','2021-01-26 01:07:29','0','2021-01-26 01:07:29'),
|
||||||
|
('1549','-1185','2144334450300724903','1','2021-01-26 01:07:32','0','2021-01-26 01:07:32'),
|
||||||
|
('1683','-1525','14803676216828342915','3','2021-03-10 08:39:39','0','2021-03-10 08:39:39'),
|
||||||
|
('1899','-1663','16616172057370363741','3','2021-04-12 14:49:18','0','2021-04-12 14:49:18'),
|
||||||
|
('2168','-1865','13129474130315401087','3','2021-07-08 11:58:54','0','2021-07-08 11:58:54'),
|
||||||
|
('2274','-1935','5775135935896874129','3','2021-08-24 11:40:04','0','2021-08-24 11:40:04'),
|
||||||
|
('2318','-1967','5713731625139303791','3','2021-09-06 21:38:30','0','2021-09-06 21:38:30'),
|
||||||
|
('2762','-2263','6997866521554931275','1','2021-12-25 11:44:30','0','2021-12-25 11:44:30');
|
||||||
|
`)
|
||||||
|
}
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import Faker from 'faker'
|
|
||||||
import { define } from 'typeorm-seeding'
|
|
||||||
import { LoginUserBackup } from '../../entity/LoginUserBackup'
|
|
||||||
import { LoginUserBackupContext } from '../interface/UserContext'
|
|
||||||
|
|
||||||
define(LoginUserBackup, (faker: typeof Faker, context?: LoginUserBackupContext) => {
|
|
||||||
if (!context || !context.userId) {
|
|
||||||
throw new Error('LoginUserBackup: No userId present!')
|
|
||||||
}
|
|
||||||
|
|
||||||
const userBackup = new LoginUserBackup()
|
|
||||||
// TODO: Get the real passphrase
|
|
||||||
userBackup.passphrase = context.passphrase ? context.passphrase : faker.random.words(24)
|
|
||||||
userBackup.mnemonicType = context.mnemonicType ? context.mnemonicType : 2
|
|
||||||
userBackup.userId = context.userId
|
|
||||||
|
|
||||||
return userBackup
|
|
||||||
})
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import Faker from 'faker'
|
|
||||||
import { define } from 'typeorm-seeding'
|
|
||||||
import { LoginUser } from '../../entity/LoginUser'
|
|
||||||
import { randomBytes } from 'crypto'
|
|
||||||
import { LoginUserContext } from '../interface/UserContext'
|
|
||||||
|
|
||||||
define(LoginUser, (faker: typeof Faker, context?: LoginUserContext) => {
|
|
||||||
if (!context) context = {}
|
|
||||||
|
|
||||||
const user = new LoginUser()
|
|
||||||
user.email = context.email ? context.email : faker.internet.email()
|
|
||||||
user.firstName = context.firstName ? context.firstName : faker.name.firstName()
|
|
||||||
user.lastName = context.lastName ? context.lastName : faker.name.lastName()
|
|
||||||
user.username = context.username ? context.username : faker.internet.userName()
|
|
||||||
user.description = context.description ? context.description : faker.random.words(4)
|
|
||||||
// TODO Create real password and keys/hash
|
|
||||||
user.password = context.password ? context.password : BigInt(0)
|
|
||||||
user.pubKey = context.pubKey ? context.pubKey : randomBytes(32)
|
|
||||||
user.privKey = context.privKey ? context.privKey : randomBytes(80)
|
|
||||||
user.emailHash = context.emailHash ? context.emailHash : randomBytes(32)
|
|
||||||
user.createdAt = context.createdAt ? context.createdAt : faker.date.recent()
|
|
||||||
user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked
|
|
||||||
user.passphraseShown = context.passphraseShown ? context.passphraseShown : false
|
|
||||||
user.language = context.language ? context.language : 'en'
|
|
||||||
user.disabled = context.disabled ? context.disabled : false
|
|
||||||
user.groupId = context.groupId ? context.groupId : 1
|
|
||||||
user.publisherId = context.publisherId ? context.publisherId : 0
|
|
||||||
|
|
||||||
return user
|
|
||||||
})
|
|
||||||
@ -1,21 +1,31 @@
|
|||||||
import Faker from 'faker'
|
import Faker from 'faker'
|
||||||
import { define } from 'typeorm-seeding'
|
import { define } from 'typeorm-seeding'
|
||||||
import { User } from '../../entity/User'
|
import { User } from '../../entity/User'
|
||||||
import { randomBytes } from 'crypto'
|
import { randomBytes, randomInt } from 'crypto'
|
||||||
import { UserContext } from '../interface/UserContext'
|
import { UserContext } from '../interface/UserContext'
|
||||||
|
|
||||||
define(User, (faker: typeof Faker, context?: UserContext) => {
|
define(User, (faker: typeof Faker, context?: UserContext) => {
|
||||||
if (!context) context = {}
|
if (!context) context = {}
|
||||||
|
|
||||||
const user = new User()
|
const user = new User()
|
||||||
user.pubkey = context.pubkey ? context.pubkey : randomBytes(32)
|
user.pubKey = context.pubKey ? context.pubKey : randomBytes(32)
|
||||||
user.email = context.email ? context.email : faker.internet.email()
|
user.email = context.email ? context.email : faker.internet.email()
|
||||||
user.firstName = context.firstName ? context.firstName : faker.name.firstName()
|
user.firstName = context.firstName ? context.firstName : faker.name.firstName()
|
||||||
user.lastName = context.lastName ? context.lastName : faker.name.lastName()
|
user.lastName = context.lastName ? context.lastName : faker.name.lastName()
|
||||||
user.username = context.username ? context.username : faker.internet.userName()
|
user.username = context.username ? context.username : faker.internet.userName()
|
||||||
user.disabled = context.disabled ? context.disabled : false
|
user.disabled = context.disabled ? context.disabled : false
|
||||||
user.groupId = 0
|
|
||||||
user.indexId = 0
|
user.indexId = 0
|
||||||
|
user.description = context.description ? context.description : faker.random.words(4)
|
||||||
|
// TODO Create real password and keys/hash
|
||||||
|
user.password = context.password ? context.password : BigInt(0)
|
||||||
|
user.privKey = context.privKey ? context.privKey : randomBytes(80)
|
||||||
|
user.emailHash = context.emailHash ? context.emailHash : randomBytes(32)
|
||||||
|
user.createdAt = context.createdAt ? context.createdAt : faker.date.recent()
|
||||||
|
user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked
|
||||||
|
user.passphraseShown = context.passphraseShown ? context.passphraseShown : false
|
||||||
|
user.language = context.language ? context.language : 'en'
|
||||||
|
user.publisherId = context.publisherId ? context.publisherId : 0
|
||||||
|
user.passphrase = context.passphrase ? context.passphrase : faker.random.words(24)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,35 +1,20 @@
|
|||||||
export interface UserContext {
|
export interface UserContext {
|
||||||
pubkey?: Buffer
|
pubKey?: Buffer
|
||||||
email?: string
|
email?: string
|
||||||
firstName?: string
|
firstName?: string
|
||||||
lastName?: string
|
lastName?: string
|
||||||
username?: string
|
username?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoginUserContext {
|
|
||||||
email?: string
|
|
||||||
firstName?: string
|
|
||||||
lastName?: string
|
|
||||||
username?: string
|
|
||||||
description?: string
|
description?: string
|
||||||
password?: BigInt
|
password?: BigInt
|
||||||
pubKey?: Buffer
|
|
||||||
privKey?: Buffer
|
privKey?: Buffer
|
||||||
emailHash?: Buffer
|
emailHash?: Buffer
|
||||||
createdAt?: Date
|
createdAt?: Date
|
||||||
emailChecked?: boolean
|
emailChecked?: boolean
|
||||||
passphraseShown?: boolean
|
passphraseShown?: boolean
|
||||||
language?: string
|
language?: string
|
||||||
disabled?: boolean
|
|
||||||
groupId?: number
|
|
||||||
publisherId?: number
|
publisherId?: number
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoginUserBackupContext {
|
|
||||||
userId?: number
|
|
||||||
passphrase?: string
|
passphrase?: string
|
||||||
mnemonicType?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServerUserContext {
|
export interface ServerUserContext {
|
||||||
@ -42,8 +27,3 @@ export interface ServerUserContext {
|
|||||||
created?: Date
|
created?: Date
|
||||||
modified?: Date
|
modified?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginUserRolesContext {
|
|
||||||
userId?: number
|
|
||||||
roleId?: number
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export interface UserInterface {
|
export interface UserInterface {
|
||||||
// from login user (contains state user)
|
// from user
|
||||||
email?: string
|
email?: string
|
||||||
firstName?: string
|
firstName?: string
|
||||||
lastName?: string
|
lastName?: string
|
||||||
@ -16,9 +16,7 @@ export interface UserInterface {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
groupId?: number
|
groupId?: number
|
||||||
publisherId?: number
|
publisherId?: number
|
||||||
// from login user backup
|
|
||||||
passphrase?: string
|
passphrase?: string
|
||||||
mnemonicType?: number
|
|
||||||
// from server user
|
// from server user
|
||||||
serverUserPassword?: string
|
serverUserPassword?: string
|
||||||
role?: string
|
role?: string
|
||||||
|
|||||||
@ -1,10 +1,4 @@
|
|||||||
import {
|
import { UserContext, ServerUserContext } from '../../interface/UserContext'
|
||||||
UserContext,
|
|
||||||
LoginUserContext,
|
|
||||||
LoginUserBackupContext,
|
|
||||||
ServerUserContext,
|
|
||||||
LoginUserRolesContext,
|
|
||||||
} from '../../interface/UserContext'
|
|
||||||
import {
|
import {
|
||||||
BalanceContext,
|
BalanceContext,
|
||||||
TransactionContext,
|
TransactionContext,
|
||||||
@ -13,8 +7,6 @@ import {
|
|||||||
} from '../../interface/TransactionContext'
|
} from '../../interface/TransactionContext'
|
||||||
import { UserInterface } from '../../interface/UserInterface'
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
import { User } from '../../../entity/User'
|
import { User } from '../../../entity/User'
|
||||||
import { LoginUser } from '../../../entity/LoginUser'
|
|
||||||
import { LoginUserBackup } from '../../../entity/LoginUserBackup'
|
|
||||||
import { ServerUser } from '../../../entity/ServerUser'
|
import { ServerUser } from '../../../entity/ServerUser'
|
||||||
import { Balance } from '../../../entity/Balance'
|
import { Balance } from '../../../entity/Balance'
|
||||||
import { Transaction } from '../../../entity/Transaction'
|
import { Transaction } from '../../../entity/Transaction'
|
||||||
@ -24,9 +16,6 @@ import { Factory } from 'typeorm-seeding'
|
|||||||
|
|
||||||
export const userSeeder = async (factory: Factory, userData: UserInterface): Promise<void> => {
|
export const userSeeder = async (factory: Factory, userData: UserInterface): Promise<void> => {
|
||||||
const user = await factory(User)(createUserContext(userData)).create()
|
const user = await factory(User)(createUserContext(userData)).create()
|
||||||
if (!userData.email) userData.email = user.email
|
|
||||||
const loginUser = await factory(LoginUser)(createLoginUserContext(userData)).create()
|
|
||||||
await factory(LoginUserBackup)(createLoginUserBackupContext(userData, loginUser)).create()
|
|
||||||
|
|
||||||
if (userData.isAdmin) {
|
if (userData.isAdmin) {
|
||||||
await factory(ServerUser)(createServerUserContext(userData)).create()
|
await factory(ServerUser)(createServerUserContext(userData)).create()
|
||||||
@ -49,47 +38,24 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
|
|||||||
|
|
||||||
const createUserContext = (context: UserInterface): UserContext => {
|
const createUserContext = (context: UserInterface): UserContext => {
|
||||||
return {
|
return {
|
||||||
pubkey: context.pubKey,
|
pubKey: context.pubKey,
|
||||||
email: context.email,
|
email: context.email,
|
||||||
firstName: context.firstName,
|
firstName: context.firstName,
|
||||||
lastName: context.lastName,
|
lastName: context.lastName,
|
||||||
username: context.username,
|
username: context.username,
|
||||||
disabled: context.disabled,
|
disabled: context.disabled,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createLoginUserContext = (context: UserInterface): LoginUserContext => {
|
|
||||||
return {
|
|
||||||
email: context.email,
|
|
||||||
firstName: context.firstName,
|
|
||||||
lastName: context.lastName,
|
|
||||||
username: context.username,
|
|
||||||
description: context.description,
|
description: context.description,
|
||||||
password: context.password,
|
password: context.password,
|
||||||
pubKey: context.pubKey,
|
|
||||||
privKey: context.privKey,
|
privKey: context.privKey,
|
||||||
emailHash: context.emailHash,
|
emailHash: context.emailHash,
|
||||||
createdAt: context.createdAt,
|
createdAt: context.createdAt,
|
||||||
emailChecked: context.emailChecked,
|
emailChecked: context.emailChecked,
|
||||||
passphraseShown: context.passphraseShown,
|
passphraseShown: context.passphraseShown,
|
||||||
language: context.language,
|
language: context.language,
|
||||||
disabled: context.disabled,
|
|
||||||
groupId: context.groupId,
|
|
||||||
publisherId: context.publisherId,
|
publisherId: context.publisherId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createLoginUserBackupContext = (
|
|
||||||
context: UserInterface,
|
|
||||||
loginUser: LoginUser,
|
|
||||||
): LoginUserBackupContext => {
|
|
||||||
return {
|
|
||||||
passphrase: context.passphrase,
|
|
||||||
mnemonicType: context.mnemonicType,
|
|
||||||
userId: loginUser.id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createServerUserContext = (context: UserInterface): ServerUserContext => {
|
const createServerUserContext = (context: UserInterface): ServerUserContext => {
|
||||||
return {
|
return {
|
||||||
role: context.role,
|
role: context.role,
|
||||||
@ -103,13 +69,6 @@ const createServerUserContext = (context: UserInterface): ServerUserContext => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createLoginUserRolesContext = (loginUser: LoginUser): LoginUserRolesContext => {
|
|
||||||
return {
|
|
||||||
userId: loginUser.id,
|
|
||||||
roleId: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createBalanceContext = (context: UserInterface, user: User): BalanceContext => {
|
const createBalanceContext = (context: UserInterface, user: User): BalanceContext => {
|
||||||
return {
|
return {
|
||||||
modified: context.balanceModified,
|
modified: context.balanceModified,
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
"memo": "Nachricht",
|
"memo": "Nachricht",
|
||||||
"message": "Nachricht",
|
"message": "Nachricht",
|
||||||
"new_balance": "Neuer Kontostand nach Bestätigung",
|
"new_balance": "Neuer Kontostand nach Bestätigung",
|
||||||
|
"no_gdd_available": "Du hast keine GDD zum versenden.",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"passwordRepeat": "Passwort wiederholen",
|
"passwordRepeat": "Passwort wiederholen",
|
||||||
"password_new": "Neues Passwort",
|
"password_new": "Neues Passwort",
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
"memo": "Message",
|
"memo": "Message",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
"new_balance": "Account balance after confirmation",
|
"new_balance": "Account balance after confirmation",
|
||||||
|
"no_gdd_available": "You do not have GDD to send.",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Repeat password",
|
"passwordRepeat": "Repeat password",
|
||||||
"password_new": "New password",
|
"password_new": "New password",
|
||||||
|
|||||||
@ -21,7 +21,7 @@ describe('GddSend', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const propsData = {
|
const propsData = {
|
||||||
balance: 100.0,
|
balance: 0.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = () => {
|
||||||
@ -37,7 +37,44 @@ describe('GddSend', () => {
|
|||||||
expect(wrapper.find('div.transaction-form').exists()).toBeTruthy()
|
expect(wrapper.find('div.transaction-form').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('transaction form disable because balance 0,0 GDD', () => {
|
||||||
|
it('has a disabled input field of type email', () => {
|
||||||
|
expect(wrapper.find('#input-group-1').find('input').attributes('disabled')).toBe('disabled')
|
||||||
|
})
|
||||||
|
it('has a disabled input field for amount', () => {
|
||||||
|
expect(wrapper.find('#input-2').find('input').attributes('disabled')).toBe('disabled')
|
||||||
|
})
|
||||||
|
it('has a disabled textarea field ', () => {
|
||||||
|
expect(wrapper.find('#input-3').find('textarea').attributes('disabled')).toBe('disabled')
|
||||||
|
})
|
||||||
|
it('has a message indicating that there are no GDDs to send ', () => {
|
||||||
|
expect(wrapper.find('.text-danger').text()).toBe('form.no_gdd_available')
|
||||||
|
})
|
||||||
|
it('has no reset button and no submit button ', () => {
|
||||||
|
expect(wrapper.find('.test-buttons').exists()).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('transaction form', () => {
|
describe('transaction form', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({ balance: 100.0 })
|
||||||
|
})
|
||||||
|
describe('transaction form show because balance 100,0 GDD', () => {
|
||||||
|
it('has no warning message ', () => {
|
||||||
|
expect(wrapper.find('.text-danger').exists()).toBeFalsy()
|
||||||
|
})
|
||||||
|
it('has a reset button', () => {
|
||||||
|
expect(wrapper.find('.test-buttons').findAll('button').at(0).attributes('type')).toBe(
|
||||||
|
'reset',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('has a submit button', () => {
|
||||||
|
expect(wrapper.find('.test-buttons').findAll('button').at(1).attributes('type')).toBe(
|
||||||
|
'submit',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('email field', () => {
|
describe('email field', () => {
|
||||||
it('has an input field of type email', () => {
|
it('has an input field of type email', () => {
|
||||||
expect(wrapper.find('#input-group-1').find('input').attributes('type')).toBe('email')
|
expect(wrapper.find('#input-group-1').find('input').attributes('type')).toBe('email')
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
placeholder="E-Mail"
|
placeholder="E-Mail"
|
||||||
style="font-size: large"
|
style="font-size: large"
|
||||||
class="pl-3"
|
class="pl-3"
|
||||||
|
:disabled="isBalanceDisabled"
|
||||||
></b-form-input>
|
></b-form-input>
|
||||||
</b-input-group>
|
</b-input-group>
|
||||||
<b-col v-if="errors">
|
<b-col v-if="errors">
|
||||||
@ -76,6 +77,7 @@
|
|||||||
:placeholder="$n(0.01)"
|
:placeholder="$n(0.01)"
|
||||||
style="font-size: large"
|
style="font-size: large"
|
||||||
class="pl-3"
|
class="pl-3"
|
||||||
|
:disabled="isBalanceDisabled"
|
||||||
></b-form-input>
|
></b-form-input>
|
||||||
</b-input-group>
|
</b-input-group>
|
||||||
<b-col v-if="errors">
|
<b-col v-if="errors">
|
||||||
@ -105,6 +107,7 @@
|
|||||||
v-model="form.memo"
|
v-model="form.memo"
|
||||||
class="pl-3"
|
class="pl-3"
|
||||||
style="font-size: large"
|
style="font-size: large"
|
||||||
|
:disabled="isBalanceDisabled"
|
||||||
></b-form-textarea>
|
></b-form-textarea>
|
||||||
</b-input-group>
|
</b-input-group>
|
||||||
<b-col v-if="errors">
|
<b-col v-if="errors">
|
||||||
@ -114,7 +117,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<b-row>
|
<div v-if="!!isBalanceDisabled" class="text-danger">
|
||||||
|
{{ $t('form.no_gdd_available') }}
|
||||||
|
</div>
|
||||||
|
<b-row v-else class="test-buttons">
|
||||||
<b-col>
|
<b-col>
|
||||||
<b-button type="reset" variant="secondary" @click="onReset">
|
<b-button type="reset" variant="secondary" @click="onReset">
|
||||||
{{ $t('form.reset') }}
|
{{ $t('form.reset') }}
|
||||||
@ -192,6 +198,11 @@ export default {
|
|||||||
this.form.email = this.form.email.trim()
|
this.form.email = this.form.email.trim()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isBalanceDisabled() {
|
||||||
|
return this.balance <= 0 ? 'disabled' : false
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user