diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2579fa28d..afb0c5314 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -441,7 +441,7 @@ jobs:
report_name: Coverage Admin Interface
type: lcov
result_path: ./coverage/lcov.info
- min_coverage: 51
+ min_coverage: 53
token: ${{ github.token }}
##############################################################################
diff --git a/README.md b/README.md
index ead54701c..ce8e84df0 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,22 @@ We are currently restructuring the service to reduce dependencies and unify busi
Once you have `docker-compose` up and running, you can open [http://localhost/vue](http://localhost/vue) and create yourself a new wallet account.
+## How to release
+
+A release is tagged on Github by its version number and published as github release. This is done automatically when a new version is defined in the [package.json](./package.json) and merged into master - furthermore we set all our sub-package-versions to the same version as the main package.json version to make version management as simple as possible.
+Each release is accompanied with release notes automatically generated from the git log which is available as [CHANGELOG.md](./CHANGELOG.md).
+
+To generate the Changelog and set a new Version you should use the following commands in the main folder
+```bash
+git fetch --all
+yarn release
+```
+
+The first command `git fetch --all` will make sure you have all tags previously defined which is required to generate a correct changelog. The second command `yarn release` will execute the changelog tool and set version numbers in the main package and sub-packages. It is required to do `yarn install` before you can use this command.
+After generating a new version you should commit the changes. This will be the CHANGELOG.md and several package.json files. This commit will be omitted in the changelog.
+
+Note: The Changelog will be regenerated with all tags on release on the external builder tool, but will not be checked in there. The Changelog on the github release will therefore always be correct, on the repo it might be incorrect due to missing tags when executing the `yarn release` command.
+
## Troubleshooting
| Problem | Issue | Solution | Description |
diff --git a/admin/package.json b/admin/package.json
index e3c94f5d8..9d70c0b06 100644
--- a/admin/package.json
+++ b/admin/package.json
@@ -43,6 +43,7 @@
"vue-jest": "^3.0.7",
"vue-moment": "^4.1.0",
"vue-router": "^3.5.3",
+ "vue-toasted": "^1.1.28",
"vuex": "^3.6.2",
"vuex-persistedstate": "^4.1.0"
},
diff --git a/admin/public/img/brand/green.png b/admin/public/img/brand/green.png
new file mode 100644
index 000000000..bb7f12c39
Binary files /dev/null and b/admin/public/img/brand/green.png differ
diff --git a/admin/src/components/CreationFormular.spec.js b/admin/src/components/CreationFormular.spec.js
index fcdf97cfa..e1bbff1cc 100644
--- a/admin/src/components/CreationFormular.spec.js
+++ b/admin/src/components/CreationFormular.spec.js
@@ -3,6 +3,16 @@ import CreationFormular from './CreationFormular.vue'
const localVue = global.localVue
+const apolloMock = jest.fn().mockResolvedValue({
+ data: {
+ verifyLogin: {
+ name: 'success',
+ id: 0,
+ },
+ },
+})
+const stateCommitMock = jest.fn()
+
const mocks = {
$moment: jest.fn(() => {
return {
@@ -14,6 +24,12 @@ const mocks = {
}),
}
}),
+ $apollo: {
+ query: apolloMock,
+ },
+ $store: {
+ commit: stateCommitMock,
+ },
}
const propsData = {
@@ -39,6 +55,23 @@ describe('CreationFormular', () => {
expect(wrapper.find('.component-creation-formular').exists()).toBeTruthy()
})
+ describe('server sends back moderator data', () => {
+ it('called store commit with mocked data', () => {
+ expect(stateCommitMock).toBeCalledWith('moderator', { name: 'success', id: 0 })
+ })
+ })
+
+ describe('server throws error for moderator data call', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ apolloMock.mockRejectedValue({ message: 'Ouch!' })
+ wrapper = Wrapper()
+ })
+ it('has called store commit with fake data', () => {
+ expect(stateCommitMock).toBeCalledWith('moderator', { id: 0, name: 'Test Moderator' })
+ })
+ })
+
describe('radio buttons to selcet month', () => {
it('has three radio buttons', () => {
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
diff --git a/admin/src/components/CreationFormular.vue b/admin/src/components/CreationFormular.vue
index d6b637152..b6a12433e 100644
--- a/admin/src/components/CreationFormular.vue
+++ b/admin/src/components/CreationFormular.vue
@@ -7,7 +7,6 @@
? 'Einzelschöpfung für ' + item.firstName + ' ' + item.lastName + ''
: 'Mehrfachschöpfung für ' + Object.keys(this.itemsMassCreation).length + ' Mitglieder'
}}
- {{ item }}
Bitte wähle ein oder Mehrere Mitglieder aus für die du Schöpfen möchtest
@@ -24,6 +23,7 @@
@@ -34,6 +34,7 @@
@@ -44,6 +45,7 @@
@@ -52,30 +54,29 @@
-
+
-
-
- GDD
-
-
-
+
-
- Die anzahl der offene Schöpfungen stimmen nicht! Diese wird bei absenden im $store
- hochgezählt. Die Liste die hier angezeigt wird ist SIMULIERT!
-
diff --git a/admin/src/pages/Overview.vue b/admin/src/pages/Overview.vue
index 056bb14a6..1c58751bc 100644
--- a/admin/src/pages/Overview.vue
+++ b/admin/src/pages/Overview.vue
@@ -76,7 +76,24 @@
diff --git a/admin/src/pages/UserSearch.vue b/admin/src/pages/UserSearch.vue
index ae0ade7b2..7fae2cdcd 100644
--- a/admin/src/pages/UserSearch.vue
+++ b/admin/src/pages/UserSearch.vue
@@ -32,7 +32,13 @@ export default {
{ key: 'email', label: 'Email' },
{ key: 'firstName', label: 'Firstname' },
{ key: 'lastName', label: 'Lastname' },
- { key: 'creation', label: 'Creation' },
+ {
+ key: 'creation',
+ label: 'Creation',
+ formatter: (value, key, item) => {
+ return String(value)
+ },
+ },
{ key: 'show_details', label: 'Details' },
],
searchResult: [],
diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js
index d59234a25..4ed6c8516 100644
--- a/admin/src/router/guards.js
+++ b/admin/src/router/guards.js
@@ -1,12 +1,28 @@
+import { verifyLogin } from '../graphql/verifyLogin'
import CONFIG from '../config'
-const addNavigationGuards = (router, store) => {
+const addNavigationGuards = (router, store, apollo) => {
// store token on `authenticate`
- router.beforeEach((to, from, next) => {
+ router.beforeEach(async (to, from, next) => {
if (to.path === '/authenticate' && to.query && to.query.token) {
- // TODO verify user to get user data
store.commit('token', to.query.token)
- next({ path: '/' })
+ await apollo
+ .query({
+ query: verifyLogin,
+ fetchPolicy: 'network-only',
+ })
+ .then((result) => {
+ const moderator = result.data.verifyLogin
+ if (moderator.isAdmin) {
+ store.commit('moderator', moderator)
+ next({ path: '/' })
+ } else {
+ next({ path: '/not-found' })
+ }
+ })
+ .catch(() => {
+ next({ path: '/not-found' })
+ })
} else {
next()
}
@@ -16,7 +32,9 @@ const addNavigationGuards = (router, store) => {
router.beforeEach((to, from, next) => {
if (
!CONFIG.DEBUG_DISABLE_AUTH && // we did not disabled the auth module for debug purposes
- !store.state.token && // we do not have a token
+ (!store.state.token || // we do not have a token
+ !store.state.moderator || // no moderator set in store
+ !store.state.moderator.isAdmin) && // user is no admin
to.path !== '/not-found' && // we are not on `not-found`
to.path !== '/logout' // we are not on `logout`
) {
diff --git a/admin/src/router/guards.test.js b/admin/src/router/guards.test.js
index e69846aab..cd5b33e68 100644
--- a/admin/src/router/guards.test.js
+++ b/admin/src/router/guards.test.js
@@ -2,6 +2,13 @@ import addNavigationGuards from './guards'
import router from './router'
const storeCommitMock = jest.fn()
+const apolloQueryMock = jest.fn().mockResolvedValue({
+ data: {
+ verifyLogin: {
+ isAdmin: true,
+ },
+ },
+})
const store = {
commit: storeCommitMock,
@@ -10,7 +17,11 @@ const store = {
},
}
-addNavigationGuards(router, store)
+const apollo = {
+ query: apolloQueryMock,
+}
+
+addNavigationGuards(router, store, apollo)
describe('navigation guards', () => {
beforeEach(() => {
@@ -21,18 +32,70 @@ describe('navigation guards', () => {
const navGuard = router.beforeHooks[0]
const next = jest.fn()
- describe('with valid token', () => {
- it('commits the token to the store', async () => {
+ describe('with valid token and as admin', () => {
+ beforeEach(() => {
navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ })
+
+ it('commits the token to the store', async () => {
expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
})
+ it('commits the moderator to the store', () => {
+ expect(storeCommitMock).toBeCalledWith('moderator', { isAdmin: true })
+ })
+
it('redirects to /', async () => {
- navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
expect(next).toBeCalledWith({ path: '/' })
})
})
+ describe('with valid token and not as admin', () => {
+ beforeEach(() => {
+ apolloQueryMock.mockResolvedValue({
+ data: {
+ verifyLogin: {
+ isAdmin: false,
+ },
+ },
+ })
+ navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ })
+
+ it('commits the token to the store', async () => {
+ expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
+ })
+
+ it('does not commit the moderator to the store', () => {
+ expect(storeCommitMock).not.toBeCalledWith('moderator', { isAdmin: false })
+ })
+
+ it('redirects to /not-found', async () => {
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+ })
+
+ describe('with valid token and server error on verification', () => {
+ beforeEach(() => {
+ apolloQueryMock.mockRejectedValue({
+ message: 'Ouch!',
+ })
+ navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ })
+
+ it('commits the token to the store', async () => {
+ expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
+ })
+
+ it('does not commit the moderator to the store', () => {
+ expect(storeCommitMock).not.toBeCalledWith('moderator', { isAdmin: false })
+ })
+
+ it('redirects to /not-found', async () => {
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+ })
+
describe('without valid token', () => {
it('does not commit the token to the store', async () => {
navGuard({ path: '/authenticate' }, {}, next)
@@ -55,9 +118,16 @@ describe('navigation guards', () => {
expect(next).toBeCalledWith({ path: '/not-found' })
})
- it('does not redirect when token in store', () => {
+ it('redirects to not found with token in store and not moderator', () => {
store.state.token = 'valid token'
navGuard({ path: '/' }, {}, next)
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+
+ it('does not redirect with token in store and as moderator', () => {
+ store.state.token = 'valid token'
+ store.state.moderator = { isAdmin: true }
+ navGuard({ path: '/' }, {}, next)
expect(next).toBeCalledWith()
})
})
diff --git a/admin/src/store/store.js b/admin/src/store/store.js
index 754c559c8..fe5629e19 100644
--- a/admin/src/store/store.js
+++ b/admin/src/store/store.js
@@ -18,11 +18,18 @@ export const mutations = {
token: (state, token) => {
state.token = token
},
+ setOpenCreations: (state, openCreations) => {
+ state.openCreations = openCreations
+ },
+ moderator: (state, moderator) => {
+ state.moderator = moderator
+ },
}
export const actions = {
logout: ({ commit, state }) => {
commit('token', null)
+ commit('moderator', null)
window.localStorage.clear()
},
}
@@ -35,7 +42,7 @@ const store = new Vuex.Store({
],
state: {
token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
- moderator: 'Dertest Moderator',
+ moderator: null,
openCreations: 0,
},
// Syncronous mutation of the state
diff --git a/admin/src/store/store.test.js b/admin/src/store/store.test.js
index 4482a46bf..e027ebf1a 100644
--- a/admin/src/store/store.test.js
+++ b/admin/src/store/store.test.js
@@ -1,11 +1,19 @@
import store, { mutations, actions } from './store'
+import CONFIG from '../config'
-const { token, openCreationsPlus, openCreationsMinus, resetOpenCreations } = mutations
+jest.mock('../config')
+
+const {
+ token,
+ openCreationsPlus,
+ openCreationsMinus,
+ resetOpenCreations,
+ setOpenCreations,
+ moderator,
+} = mutations
const { logout } = actions
-const CONFIG = {
- DEBUG_DISABLE_AUTH: true,
-}
+CONFIG.DEBUG_DISABLE_AUTH = true
describe('Vuex store', () => {
describe('mutations', () => {
@@ -40,6 +48,22 @@ describe('Vuex store', () => {
expect(state.openCreations).toEqual(0)
})
})
+
+ describe('moderator', () => {
+ it('sets the moderator object in state', () => {
+ const state = { moderator: null }
+ moderator(state, { id: 1 })
+ expect(state.moderator).toEqual({ id: 1 })
+ })
+ })
+
+ describe('setOpenCreations', () => {
+ it('sets the open creations to given value', () => {
+ const state = { openCreations: 24 }
+ setOpenCreations(state, 12)
+ expect(state.openCreations).toEqual(12)
+ })
+ })
})
describe('actions', () => {
@@ -57,6 +81,11 @@ describe('Vuex store', () => {
expect(commit).toBeCalledWith('token', null)
})
+ it('deletes the moderator in store', () => {
+ logout({ commit, state })
+ expect(commit).toBeCalledWith('moderator', null)
+ })
+
it.skip('clears the window local storage', () => {
expect(windowStorageMock).toBeCalled()
})
diff --git a/admin/yarn.lock b/admin/yarn.lock
index d7960320b..46b5aaa93 100644
--- a/admin/yarn.lock
+++ b/admin/yarn.lock
@@ -12524,6 +12524,11 @@ vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
+vue-toasted@^1.1.28:
+ version "1.1.28"
+ resolved "https://registry.yarnpkg.com/vue-toasted/-/vue-toasted-1.1.28.tgz#dbabb83acc89f7a9e8765815e491d79f0dc65c26"
+ integrity sha512-UUzr5LX51UbbiROSGZ49GOgSzFxaMHK6L00JV8fir/CYNJCpIIvNZ5YmS4Qc8Y2+Z/4VVYRpeQL2UO0G800Raw==
+
vue@^2.6.11:
version "2.6.14"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
diff --git a/backend/package.json b/backend/package.json
index 375046363..e573a2704 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -29,6 +29,7 @@
"jest": "^27.2.4",
"jsonwebtoken": "^8.5.1",
"module-alias": "^2.2.2",
+ "moment": "^2.29.1",
"mysql2": "^2.3.0",
"nodemailer": "^6.6.5",
"random-bigint": "^0.0.1",
diff --git a/backend/src/graphql/arg/CreatePendingCreationArgs.ts b/backend/src/graphql/arg/CreatePendingCreationArgs.ts
new file mode 100644
index 000000000..d2c17abf1
--- /dev/null
+++ b/backend/src/graphql/arg/CreatePendingCreationArgs.ts
@@ -0,0 +1,19 @@
+import { ArgsType, Field, Int } from 'type-graphql'
+
+@ArgsType()
+export default class CreatePendingCreationArgs {
+ @Field(() => String)
+ email: string
+
+ @Field(() => Int)
+ amount: number
+
+ @Field(() => String)
+ memo: string
+
+ @Field(() => String)
+ creationDate: string
+
+ @Field(() => Int)
+ moderator: number
+}
diff --git a/backend/src/graphql/model/CreatePendingCreation.ts b/backend/src/graphql/model/CreatePendingCreation.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/src/graphql/model/PendingCreation.ts b/backend/src/graphql/model/PendingCreation.ts
new file mode 100644
index 000000000..f1087ea0b
--- /dev/null
+++ b/backend/src/graphql/model/PendingCreation.ts
@@ -0,0 +1,34 @@
+import { ObjectType, Field, Int } from 'type-graphql'
+
+@ObjectType()
+export class PendingCreation {
+ @Field(() => String)
+ firstName: string
+
+ @Field(() => Int)
+ id?: number
+
+ @Field(() => String)
+ lastName: string
+
+ @Field(() => Number)
+ userId: number
+
+ @Field(() => String)
+ email: string
+
+ @Field(() => Date)
+ date: Date
+
+ @Field(() => String)
+ memo: string
+
+ @Field(() => Number)
+ amount: BigInt
+
+ @Field(() => Number)
+ moderator: number
+
+ @Field(() => [Number])
+ creation: number[]
+}
diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts
index cdb46c954..c7b5806ca 100644
--- a/backend/src/graphql/model/User.ts
+++ b/backend/src/graphql/model/User.ts
@@ -12,6 +12,7 @@ export class User {
*/
constructor(json?: any) {
if (json) {
+ this.id = json.id
this.email = json.email
this.firstName = json.first_name
this.lastName = json.last_name
@@ -24,6 +25,9 @@ export class User {
}
}
+ @Field(() => Number)
+ id: number
+
@Field(() => String)
email: string
diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts
index 4ae259087..169b79250 100644
--- a/backend/src/graphql/resolver/AdminResolver.ts
+++ b/backend/src/graphql/resolver/AdminResolver.ts
@@ -1,28 +1,200 @@
-import { Resolver, Query, Arg, Authorized } from 'type-graphql'
-import { getCustomRepository } from 'typeorm'
+import { Resolver, Query, Arg, Args, Authorized, Mutation } from 'type-graphql'
+import { getCustomRepository, Raw } from 'typeorm'
import { UserAdmin } from '../model/UserAdmin'
-import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
+import { PendingCreation } from '../model/PendingCreation'
import { RIGHTS } from '../../auth/RIGHTS'
+import { TransactionCreationRepository } from '../../typeorm/repository/TransactionCreation'
+import { PendingCreationRepository } from '../../typeorm/repository/PendingCreation'
+import { UserRepository } from '../../typeorm/repository/User'
+import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs'
+import moment from 'moment'
@Resolver()
export class AdminResolver {
@Authorized([RIGHTS.SEARCH_USERS])
@Query(() => [UserAdmin])
async searchUsers(@Arg('searchText') searchText: string): Promise {
- const loginUserRepository = getCustomRepository(LoginUserRepository)
- const loginUsers = await loginUserRepository.findBySearchCriteria(searchText)
- const users = loginUsers.map((loginUser) => {
- const user = new UserAdmin()
- user.firstName = loginUser.firstName
- user.lastName = loginUser.lastName
- user.email = loginUser.email
- user.creation = [
- (Math.floor(Math.random() * 50) + 1) * 20,
- (Math.floor(Math.random() * 50) + 1) * 20,
- (Math.floor(Math.random() * 50) + 1) * 20,
- ]
- return user
- })
- return users
+ const userRepository = getCustomRepository(UserRepository)
+ const users = await userRepository.findBySearchCriteria(searchText)
+ const adminUsers = await Promise.all(
+ users.map(async (user) => {
+ const adminUser = new UserAdmin()
+ adminUser.firstName = user.firstName
+ adminUser.lastName = user.lastName
+ adminUser.email = user.email
+ adminUser.creation = await getUserCreations(user.id)
+ return adminUser
+ }),
+ )
+ return adminUsers
+ }
+
+ @Mutation(() => [Number])
+ async createPendingCreation(
+ @Args() { email, amount, memo, creationDate, moderator }: CreatePendingCreationArgs,
+ ): Promise {
+ const userRepository = getCustomRepository(UserRepository)
+ const user = await userRepository.findByEmail(email)
+
+ const creations = await getUserCreations(user.id)
+ const creationDateObj = new Date(creationDate)
+ if (isCreationValid(creations, amount, creationDateObj)) {
+ const pendingCreationRepository = getCustomRepository(PendingCreationRepository)
+ const loginPendingTaskAdmin = pendingCreationRepository.create()
+ loginPendingTaskAdmin.userId = user.id
+ loginPendingTaskAdmin.amount = BigInt(amount * 10000)
+ loginPendingTaskAdmin.created = new Date()
+ loginPendingTaskAdmin.date = creationDateObj
+ loginPendingTaskAdmin.memo = memo
+ loginPendingTaskAdmin.moderator = moderator
+
+ pendingCreationRepository.save(loginPendingTaskAdmin)
+ }
+ return await getUserCreations(user.id)
+ }
+
+ @Query(() => [PendingCreation])
+ async getPendingCreations(): Promise {
+ const pendingCreationRepository = getCustomRepository(PendingCreationRepository)
+ const pendingCreations = await pendingCreationRepository.find()
+
+ const pendingCreationsPromise = await Promise.all(
+ pendingCreations.map(async (pendingCreation) => {
+ const userRepository = getCustomRepository(UserRepository)
+ const user = await userRepository.findOneOrFail({ id: pendingCreation.userId })
+
+ const newPendingCreation = {
+ ...pendingCreation,
+ firstName: user.firstName,
+ lastName: user.lastName,
+ email: user.email,
+ creation: await getUserCreations(user.id),
+ }
+
+ return newPendingCreation
+ }),
+ )
+ return pendingCreationsPromise
}
}
+
+async function getUserCreations(id: number): Promise {
+ const dateNextMonth = moment().add(1, 'month').format('YYYY-MM') + '-01'
+ const dateMonth = moment().format('YYYY-MM') + '-01'
+ const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM') + '-01'
+ const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM') + '-01'
+
+ const transactionCreationRepository = getCustomRepository(TransactionCreationRepository)
+ const createdAmountBeforeLastMonth = await transactionCreationRepository
+ .createQueryBuilder('transaction_creations')
+ .select('SUM(transaction_creations.amount)', 'sum')
+ .where('transaction_creations.state_user_id = :id', { id })
+ .andWhere({
+ targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
+ date: dateBeforeLastMonth,
+ enddate: dateLastMonth,
+ }),
+ })
+ .getRawOne()
+
+ const createdAmountLastMonth = await transactionCreationRepository
+ .createQueryBuilder('transaction_creations')
+ .select('SUM(transaction_creations.amount)', 'sum')
+ .where('transaction_creations.state_user_id = :id', { id })
+ .andWhere({
+ targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
+ date: dateLastMonth,
+ enddate: dateMonth,
+ }),
+ })
+ .getRawOne()
+
+ const createdAmountMonth = await transactionCreationRepository
+ .createQueryBuilder('transaction_creations')
+ .select('SUM(transaction_creations.amount)', 'sum')
+ .where('transaction_creations.state_user_id = :id', { id })
+ .andWhere({
+ targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
+ date: dateMonth,
+ enddate: dateNextMonth,
+ }),
+ })
+ .getRawOne()
+
+ const pendingCreationRepository = getCustomRepository(PendingCreationRepository)
+ const pendingAmountMounth = await pendingCreationRepository
+ .createQueryBuilder('login_pending_tasks_admin')
+ .select('SUM(login_pending_tasks_admin.amount)', 'sum')
+ .where('login_pending_tasks_admin.userId = :id', { id })
+ .andWhere({
+ date: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
+ date: dateMonth,
+ enddate: dateNextMonth,
+ }),
+ })
+ .getRawOne()
+
+ const pendingAmountLastMounth = await pendingCreationRepository
+ .createQueryBuilder('login_pending_tasks_admin')
+ .select('SUM(login_pending_tasks_admin.amount)', 'sum')
+ .where('login_pending_tasks_admin.userId = :id', { id })
+ .andWhere({
+ date: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
+ date: dateLastMonth,
+ enddate: dateMonth,
+ }),
+ })
+ .getRawOne()
+
+ const pendingAmountBeforeLastMounth = await pendingCreationRepository
+ .createQueryBuilder('login_pending_tasks_admin')
+ .select('SUM(login_pending_tasks_admin.amount)', 'sum')
+ .where('login_pending_tasks_admin.userId = :id', { id })
+ .andWhere({
+ date: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
+ date: dateBeforeLastMonth,
+ enddate: dateLastMonth,
+ }),
+ })
+ .getRawOne()
+
+ // COUNT amount from 2 tables
+ const usedCreationBeforeLastMonth =
+ (Number(createdAmountBeforeLastMonth.sum) + Number(pendingAmountBeforeLastMounth.sum)) / 10000
+ const usedCreationLastMonth =
+ (Number(createdAmountLastMonth.sum) + Number(pendingAmountLastMounth.sum)) / 10000
+ const usedCreationMonth =
+ (Number(createdAmountMonth.sum) + Number(pendingAmountMounth.sum)) / 10000
+ return [
+ 1000 - usedCreationBeforeLastMonth,
+ 1000 - usedCreationLastMonth,
+ 1000 - usedCreationMonth,
+ ]
+}
+
+function isCreationValid(creations: number[], amount: number, creationDate: Date) {
+ const dateMonth = moment().format('YYYY-MM')
+ const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM')
+ const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM')
+ const creationDateMonth = moment(creationDate).format('YYYY-MM')
+
+ let openCreation
+ switch (creationDateMonth) {
+ case dateMonth:
+ openCreation = creations[2]
+ break
+ case dateLastMonth:
+ openCreation = creations[1]
+ break
+ case dateBeforeLastMonth:
+ openCreation = creations[0]
+ break
+ default:
+ throw new Error('CreationDate is not in last three months')
+ }
+
+ if (openCreation < amount) {
+ throw new Error(`Open creation (${openCreation}) is less than amount (${amount})`)
+ }
+ return true
+}
diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts
index 6824303a6..9856e1968 100644
--- a/backend/src/graphql/resolver/UserResolver.ts
+++ b/backend/src/graphql/resolver/UserResolver.ts
@@ -161,6 +161,7 @@ export class UserResolver {
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findByEmail(userEntity.email)
const user = new User()
+ user.id = userEntity.id
user.email = userEntity.email
user.firstName = userEntity.firstName
user.lastName = userEntity.lastName
@@ -239,6 +240,7 @@ export class UserResolver {
}
const user = new User()
+ user.id = userEntity.id
user.email = email
user.firstName = loginUser.firstName
user.lastName = loginUser.lastName
diff --git a/backend/src/server/createServer.ts b/backend/src/server/createServer.ts
index 28e0e1ce4..e5dfd113b 100644
--- a/backend/src/server/createServer.ts
+++ b/backend/src/server/createServer.ts
@@ -29,7 +29,7 @@ import { elopageWebhook } from '../webhook/elopage'
// TODO implement
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
-const DB_VERSION = '0004-login_server_data'
+const DB_VERSION = '0005-admin_tables'
const createServer = async (context: any = serverContext): Promise => {
// open mysql connection
diff --git a/backend/src/typeorm/repository/PendingCreation.ts b/backend/src/typeorm/repository/PendingCreation.ts
new file mode 100644
index 000000000..8b49e7ecc
--- /dev/null
+++ b/backend/src/typeorm/repository/PendingCreation.ts
@@ -0,0 +1,5 @@
+import { EntityRepository, Repository } from 'typeorm'
+import { LoginPendingTasksAdmin } from '@entity/LoginPendingTasksAdmin'
+
+@EntityRepository(LoginPendingTasksAdmin)
+export class PendingCreationRepository extends Repository {}
diff --git a/backend/src/typeorm/repository/TransactionCreation.ts b/backend/src/typeorm/repository/TransactionCreation.ts
new file mode 100644
index 000000000..89266838a
--- /dev/null
+++ b/backend/src/typeorm/repository/TransactionCreation.ts
@@ -0,0 +1,5 @@
+import { EntityRepository, Repository } from 'typeorm'
+import { TransactionCreation } from '@entity/TransactionCreation'
+
+@EntityRepository(TransactionCreation)
+export class TransactionCreationRepository extends Repository {}
diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts
index e127c179c..cf67c837b 100644
--- a/backend/src/typeorm/repository/User.ts
+++ b/backend/src/typeorm/repository/User.ts
@@ -30,4 +30,17 @@ export class UserRepository extends Repository {
})
return usersIndiced
}
+
+ async findBySearchCriteria(searchCriteria: string): Promise {
+ 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()
+ }
}
diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts
index 9e2ddeb81..eb46b10e8 100644
--- a/backend/src/webhook/elopage.ts
+++ b/backend/src/webhook/elopage.ts
@@ -1,3 +1,5 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/*
Elopage Webhook
diff --git a/backend/yarn.lock b/backend/yarn.lock
index 5b74ba7c3..b46bc183d 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -4139,6 +4139,11 @@ module-alias@^2.2.2:
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
+moment@^2.29.1:
+ version "2.29.1"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
+ integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
diff --git a/community_server/src/Controller/ServerUsersController.php b/community_server/src/Controller/ServerUsersController.php
index c07808641..7255eee0f 100644
--- a/community_server/src/Controller/ServerUsersController.php
+++ b/community_server/src/Controller/ServerUsersController.php
@@ -16,7 +16,7 @@ class ServerUsersController extends AppController
{
parent::initialize();
// uncomment in devmode to add new community server admin user, but don't!!! commit it
- //$this->Auth->allow(['add', 'edit']);
+ // $this->Auth->allow(['add', 'edit']);
$this->Auth->deny('index');
}
diff --git a/database/.env.dist b/database/.env.dist
index 8488fb1bf..644dcaaf4 100644
--- a/database/.env.dist
+++ b/database/.env.dist
@@ -4,4 +4,6 @@ DB_USER=root
DB_PASSWORD=
DB_DATABASE=gradido_community
MIGRATIONS_TABLE=migrations
-MIGRATIONS_DIRECTORY=./migrations/
\ No newline at end of file
+MIGRATIONS_DIRECTORY=./migrations/
+
+TYPEORM_SEEDING_FACTORIES=src/factories/**/*{.ts,.js}
diff --git a/database/Dockerfile b/database/Dockerfile
index fe3d5ab9f..f2048284b 100644
--- a/database/Dockerfile
+++ b/database/Dockerfile
@@ -118,7 +118,7 @@ CMD /bin/sh -c "yarn run up"
##################################################################################
# PRODUCTION RESET ###############################################################
##################################################################################
-FROM production as production_reset
+# FROM production as production_reset
# Run command
CMD /bin/sh -c "yarn run reset"
diff --git a/database/README.md b/database/README.md
new file mode 100644
index 000000000..f78eca113
--- /dev/null
+++ b/database/README.md
@@ -0,0 +1,46 @@
+# database
+
+## Project setup
+```
+yarn install
+```
+
+## Upgrade migrations production
+```
+yarn up
+```
+
+## Upgrade migrations development
+```
+yarn dev_up
+```
+
+## Downgrade migrations production
+```
+yarn down
+```
+
+## Downgrade migrations development
+```
+yarn dev_down
+```
+
+
+## Reset DB
+```
+yarn dev_reset
+```
+
+## Seed DB
+```
+yarn seed
+```
+
+## Seeded Users
+
+| email | password | admin |
+| peter@lustig.de | `Aa12345_` | `true` |
+| bibi@bloxberg.de | `Aa12345_` | `false` |
+| raeuber@hotzenplotz.de | `Aa12345_` | `false` |
+| bob@baumeister.de | `Aa12345_` | `false` |
+
diff --git a/database/entity/0001-init_db/ServerUser.ts b/database/entity/0001-init_db/ServerUser.ts
index e776093ac..42b5816ae 100644
--- a/database/entity/0001-init_db/ServerUser.ts
+++ b/database/entity/0001-init_db/ServerUser.ts
@@ -8,8 +8,8 @@ export class ServerUser extends BaseEntity {
@Column({ length: 50 })
username: string
- @Column({ type: 'bigint', unsigned: true })
- password: BigInt
+ @Column({ length: 255 })
+ password: string
@Column({ length: 50, unique: true })
email: string
@@ -23,9 +23,9 @@ export class ServerUser extends BaseEntity {
@Column({ name: 'last_login', default: null, nullable: true })
lastLogin: Date
- @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
+ @Column({ default: () => 'CURRENT_TIMESTAMP' })
created: Date
- @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
+ @Column({ default: () => 'CURRENT_TIMESTAMP' })
modified: Date
}
diff --git a/database/entity/0001-init_db/User.ts b/database/entity/0001-init_db/User.ts
index d76711eb1..7280dca40 100644
--- a/database/entity/0001-init_db/User.ts
+++ b/database/entity/0001-init_db/User.ts
@@ -3,22 +3,28 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
// Moriz: I do not like the idea of having two user tables
@Entity('state_users')
export class User extends BaseEntity {
- @PrimaryGeneratedColumn()
+ @PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
+ @Column({ name: 'index_id', default: 0 })
+ indexId: number
+
+ @Column({ name: 'group_id', default: 0, unsigned: true })
+ groupId: number
+
@Column({ type: 'binary', length: 32, name: 'public_key' })
pubkey: Buffer
- @Column()
+ @Column({ length: 255, nullable: true, default: null })
email: string
- @Column({ name: 'first_name' })
+ @Column({ name: 'first_name', length: 255, nullable: true, default: null })
firstName: string
- @Column({ name: 'last_name' })
+ @Column({ name: 'last_name', length: 255, nullable: true, default: null })
lastName: string
- @Column()
+ @Column({ length: 255, nullable: true, default: null })
username: string
@Column()
diff --git a/database/entity/0002-add_settings/User.ts b/database/entity/0002-add_settings/User.ts
index 51bbef164..6f3067c79 100644
--- a/database/entity/0002-add_settings/User.ts
+++ b/database/entity/0002-add_settings/User.ts
@@ -4,22 +4,28 @@ import { UserSetting } from './UserSetting'
// Moriz: I do not like the idea of having two user tables
@Entity('state_users')
export class User extends BaseEntity {
- @PrimaryGeneratedColumn()
+ @PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
+ @Column({ name: 'index_id', default: 0 })
+ indexId: number
+
+ @Column({ name: 'group_id', default: 0, unsigned: true })
+ groupId: number
+
@Column({ type: 'binary', length: 32, name: 'public_key' })
pubkey: Buffer
- @Column()
+ @Column({ length: 255, nullable: true, default: null })
email: string
- @Column({ name: 'first_name' })
+ @Column({ name: 'first_name', length: 255, nullable: true, default: null })
firstName: string
- @Column({ name: 'last_name' })
+ @Column({ name: 'last_name', length: 255, nullable: true, default: null })
lastName: string
- @Column()
+ @Column({ length: 255, nullable: true, default: null })
username: string
@Column()
diff --git a/database/entity/0003-login_server_tables/LoginUser.ts b/database/entity/0003-login_server_tables/LoginUser.ts
index 1b444b0e4..ebaf8ad53 100644
--- a/database/entity/0003-login_server_tables/LoginUser.ts
+++ b/database/entity/0003-login_server_tables/LoginUser.ts
@@ -1,4 +1,5 @@
-import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
+import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
+import { LoginUserBackup } from '../LoginUserBackup'
// Moriz: I do not like the idea of having two user tables
@Entity('login_users')
@@ -53,4 +54,7 @@ export class LoginUser extends BaseEntity {
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
+
+ @OneToOne(() => LoginUserBackup, (loginUserBackup) => loginUserBackup.loginUser)
+ loginUserBackup: LoginUserBackup
}
diff --git a/database/entity/0003-login_server_tables/LoginUserBackup.ts b/database/entity/0003-login_server_tables/LoginUserBackup.ts
index af152e534..7152e12e5 100644
--- a/database/entity/0003-login_server_tables/LoginUserBackup.ts
+++ b/database/entity/0003-login_server_tables/LoginUserBackup.ts
@@ -1,16 +1,21 @@
-import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
+import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm'
+import { LoginUser } from '../LoginUser'
@Entity('login_user_backups')
export class LoginUserBackup extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
- @Column({ name: 'user_id', nullable: false })
- userId: 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
+
+ @OneToOne(() => LoginUser, (loginUser) => loginUser.loginUserBackup, { nullable: false })
+ @JoinColumn({ name: 'user_id' })
+ loginUser: LoginUser
}
diff --git a/database/entity/0003-login_server_tables/LoginUserRoles.ts b/database/entity/0003-login_server_tables/LoginUserRoles.ts
new file mode 100644
index 000000000..33553fea4
--- /dev/null
+++ b/database/entity/0003-login_server_tables/LoginUserRoles.ts
@@ -0,0 +1,13 @@
+import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
+
+@Entity('login_user_roles')
+export class LoginUserRoles extends BaseEntity {
+ @PrimaryGeneratedColumn('increment', { unsigned: true })
+ id: number
+
+ @Column({ name: 'user_id' })
+ userId: number
+
+ @Column({ name: 'role_id' })
+ roleId: number
+}
diff --git a/database/entity/0005-admin_tables/LoginPendingTasksAdmin.ts b/database/entity/0005-admin_tables/LoginPendingTasksAdmin.ts
new file mode 100644
index 000000000..26b92f43b
--- /dev/null
+++ b/database/entity/0005-admin_tables/LoginPendingTasksAdmin.ts
@@ -0,0 +1,25 @@
+import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
+
+@Entity('login_pending_tasks_admin')
+export class LoginPendingTasksAdmin extends BaseEntity {
+ @PrimaryGeneratedColumn('increment', { unsigned: true })
+ id: number
+
+ @Column({ unsigned: true, nullable: false })
+ userId: number
+
+ @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
+ created: Date
+
+ @Column({ type: 'datetime', nullable: false })
+ date: Date
+
+ @Column({ length: 256, nullable: true, default: null })
+ memo: string
+
+ @Column({ type: 'bigint', nullable: false })
+ amount: BigInt
+
+ @Column()
+ moderator: number
+}
diff --git a/database/entity/LoginPendingTasksAdmin.ts b/database/entity/LoginPendingTasksAdmin.ts
new file mode 100644
index 000000000..f766b74dd
--- /dev/null
+++ b/database/entity/LoginPendingTasksAdmin.ts
@@ -0,0 +1 @@
+export { LoginPendingTasksAdmin } from './0005-admin_tables/LoginPendingTasksAdmin'
diff --git a/database/entity/LoginUserRoles.ts b/database/entity/LoginUserRoles.ts
new file mode 100644
index 000000000..1efa81caf
--- /dev/null
+++ b/database/entity/LoginUserRoles.ts
@@ -0,0 +1 @@
+export { LoginUserRoles } from './0003-login_server_tables/LoginUserRoles'
diff --git a/database/entity/index.ts b/database/entity/index.ts
index 53a6a14bf..92b3875f8 100644
--- a/database/entity/index.ts
+++ b/database/entity/index.ts
@@ -2,6 +2,7 @@ import { Balance } from './Balance'
import { LoginElopageBuys } from './LoginElopageBuys'
import { LoginEmailOptIn } from './LoginEmailOptIn'
import { LoginUser } from './LoginUser'
+import { LoginUserRoles } from './LoginUserRoles'
import { LoginUserBackup } from './LoginUserBackup'
import { Migration } from './Migration'
import { ServerUser } from './ServerUser'
@@ -11,12 +12,14 @@ import { TransactionSendCoin } from './TransactionSendCoin'
import { User } from './User'
import { UserSetting } from './UserSetting'
import { UserTransaction } from './UserTransaction'
+import { LoginPendingTasksAdmin } from './LoginPendingTasksAdmin'
export const entities = [
Balance,
LoginElopageBuys,
LoginEmailOptIn,
LoginUser,
+ LoginUserRoles,
LoginUserBackup,
Migration,
ServerUser,
@@ -26,4 +29,5 @@ export const entities = [
User,
UserSetting,
UserTransaction,
+ LoginPendingTasksAdmin,
]
diff --git a/database/migrations/0001-init_db.ts b/database/migrations/0001-init_db.ts
index b402c0f72..242037363 100644
--- a/database/migrations/0001-init_db.ts
+++ b/database/migrations/0001-init_db.ts
@@ -1,3 +1,6 @@
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
diff --git a/database/migrations/0002-add_settings.ts b/database/migrations/0002-add_settings.ts
index d26a2b4cc..1b3b8a1a4 100644
--- a/database/migrations/0002-add_settings.ts
+++ b/database/migrations/0002-add_settings.ts
@@ -1,3 +1,6 @@
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
diff --git a/database/migrations/0003-login_server_tables.ts b/database/migrations/0003-login_server_tables.ts
index dacc211ac..54e7bca54 100644
--- a/database/migrations/0003-login_server_tables.ts
+++ b/database/migrations/0003-login_server_tables.ts
@@ -1,3 +1,6 @@
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
diff --git a/database/migrations/0004-login_server_data.ts b/database/migrations/0004-login_server_data.ts
index dad7d1e34..f319b16ab 100644
--- a/database/migrations/0004-login_server_data.ts
+++ b/database/migrations/0004-login_server_data.ts
@@ -1,3 +1,6 @@
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
diff --git a/database/migrations/0005-admin_tables.ts b/database/migrations/0005-admin_tables.ts
new file mode 100644
index 000000000..6398b22fc
--- /dev/null
+++ b/database/migrations/0005-admin_tables.ts
@@ -0,0 +1,29 @@
+/* MIGRATION FOR ADMIN INTERFACE
+ *
+ * This migration is special since it takes into account that
+ * the database can be setup already but also may not be.
+ * Therefore you will find all `CREATE TABLE` statements with
+ * a `IF NOT EXISTS`, all `INSERT` with an `IGNORE` and in the
+ * downgrade function all `DROP TABLE` with a `IF EXISTS`.
+ * This ensures compatibility for existing or non-existing
+ * databases.
+ */
+
+export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) {
+ await queryFn(`
+ CREATE TABLE \`login_pending_tasks_admin\` (
+ \`id\` int UNSIGNED NOT NULL AUTO_INCREMENT,
+ \`userId\` int UNSIGNED DEFAULT 0,
+ \`created\` datetime NOT NULL,
+ \`date\` datetime NOT NULL,
+ \`memo\` text DEFAULT NULL,
+ \`amount\` bigint(20) NOT NULL,
+ \`moderator\` int UNSIGNED DEFAULT 0,
+ PRIMARY KEY (\`id\`)
+ ) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4;
+ `)
+}
+
+export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) {
+ await queryFn(`DROP TABLE \`login_pending_tasks_admin\`;`)
+}
diff --git a/database/ormconfig.js b/database/ormconfig.js
new file mode 100644
index 000000000..71e444061
--- /dev/null
+++ b/database/ormconfig.js
@@ -0,0 +1,15 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+
+const CONFIG = require('./src/config')
+
+module.export = {
+ name: 'default',
+ type: 'mysql',
+ host: CONFIG.DB_HOST,
+ port: CONFIG.DB_PORT,
+ username: CONFIG.DB_USER,
+ password: CONFIG.DB_PASSWORD,
+ database: CONFIG.DB_DATABASE,
+ seeds: ['src/seeds/**/*{.ts,.js}'],
+ factories: ['src/factories/**/*{.ts,.js}'],
+}
diff --git a/database/package.json b/database/package.json
index c01c75f2b..a84026e6a 100644
--- a/database/package.json
+++ b/database/package.json
@@ -16,9 +16,12 @@
"dev_up": "nodemon -w ./ --ext ts --exec ts-node src/index.ts up",
"dev_down": "nodemon -w ./ --ext ts --exec ts-node src/index.ts down",
"dev_reset": "nodemon -w ./ --ext ts --exec ts-node src/index.ts reset",
- "lint": "eslint . --ext .js,.ts"
+ "lint": "eslint . --ext .js,.ts",
+ "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config",
+ "seed": "nodemon -w ./ --ext ts --exec ts-node src/index.ts seed"
},
"devDependencies": {
+ "@types/faker": "^5.5.9",
"@types/node": "^16.10.3",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
@@ -35,10 +38,13 @@
"typescript": "^4.3.5"
},
"dependencies": {
+ "crypto": "^1.0.1",
"dotenv": "^10.0.0",
+ "faker": "^5.5.3",
"mysql2": "^2.3.0",
"reflect-metadata": "^0.1.13",
"ts-mysql-migrate": "^1.0.2",
- "typeorm": "^0.2.38"
+ "typeorm": "^0.2.38",
+ "typeorm-seeding": "^1.6.1"
}
}
diff --git a/database/src/factories/login-user-backup.factory.ts b/database/src/factories/login-user-backup.factory.ts
new file mode 100644
index 000000000..c4ae18a77
--- /dev/null
+++ b/database/src/factories/login-user-backup.factory.ts
@@ -0,0 +1,18 @@
+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
+})
diff --git a/database/src/factories/login-user-roles.factory.ts b/database/src/factories/login-user-roles.factory.ts
new file mode 100644
index 000000000..5b7ba9c9d
--- /dev/null
+++ b/database/src/factories/login-user-roles.factory.ts
@@ -0,0 +1,16 @@
+import Faker from 'faker'
+import { define } from 'typeorm-seeding'
+import { LoginUserRoles } from '../../entity/LoginUserRoles'
+import { LoginUserRolesContext } from '../interface/UserContext'
+
+define(LoginUserRoles, (faker: typeof Faker, context?: LoginUserRolesContext) => {
+ if (!context) context = {}
+ if (!context.userId) throw new Error('LoginUserRoles: No userId present!')
+ if (!context.roleId) throw new Error('LoginUserRoles: No roleId present!')
+
+ const userRoles = new LoginUserRoles()
+ userRoles.userId = context.userId
+ userRoles.roleId = context.roleId
+
+ return userRoles
+})
diff --git a/database/src/factories/login-user.factory.ts b/database/src/factories/login-user.factory.ts
new file mode 100644
index 000000000..a5a28943e
--- /dev/null
+++ b/database/src/factories/login-user.factory.ts
@@ -0,0 +1,30 @@
+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 ? context.emailChecked : true
+ 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
+})
diff --git a/database/src/factories/server-user.factory.ts b/database/src/factories/server-user.factory.ts
new file mode 100644
index 000000000..06ff17c35
--- /dev/null
+++ b/database/src/factories/server-user.factory.ts
@@ -0,0 +1,20 @@
+import Faker from 'faker'
+import { define } from 'typeorm-seeding'
+import { ServerUser } from '../../entity/ServerUser'
+import { ServerUserContext } from '../interface/UserContext'
+
+define(ServerUser, (faker: typeof Faker, context?: ServerUserContext) => {
+ if (!context) context = {}
+
+ const user = new ServerUser()
+ user.username = context.username ? context.username : faker.internet.userName()
+ user.password = context.password ? context.password : faker.internet.password()
+ user.email = context.email ? context.email : faker.internet.email()
+ user.role = context.role ? context.role : 'admin'
+ user.activated = context.activated ? context.activated : 0
+ user.lastLogin = context.lastLogin ? context.lastLogin : faker.date.recent()
+ user.created = context.created ? context.created : faker.date.recent()
+ user.modified = context.modified ? context.modified : faker.date.recent()
+
+ return user
+})
diff --git a/database/src/factories/user.factory.ts b/database/src/factories/user.factory.ts
new file mode 100644
index 000000000..1f684f23f
--- /dev/null
+++ b/database/src/factories/user.factory.ts
@@ -0,0 +1,21 @@
+import Faker from 'faker'
+import { define } from 'typeorm-seeding'
+import { User } from '../../entity/User'
+import { randomBytes } from 'crypto'
+import { UserContext } from '../interface/UserContext'
+
+define(User, (faker: typeof Faker, context?: UserContext) => {
+ if (!context) context = {}
+
+ const user = new User()
+ user.pubkey = context.pubkey ? context.pubkey : randomBytes(32)
+ 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.disabled = context.disabled ? context.disabled : false
+ user.groupId = 0
+ user.indexId = 0
+
+ return user
+})
diff --git a/database/src/index.ts b/database/src/index.ts
index 2492a1306..ec5328a9a 100644
--- a/database/src/index.ts
+++ b/database/src/index.ts
@@ -4,6 +4,11 @@ import { Migration } from 'ts-mysql-migrate'
import CONFIG from './config'
import prepare from './prepare'
import connection from './typeorm/connection'
+import { useSeeding, runSeeder } from 'typeorm-seeding'
+import { CreatePeterLustigSeed } from './seeds/users/peter-lustig.admin.seed'
+import { CreateBibiBloxbergSeed } from './seeds/users/bibi-bloxberg.seed'
+import { CreateRaeuberHotzenplotzSeed } from './seeds/users/raeuber-hotzenplotz.seed'
+import { CreateBobBaumeisterSeed } from './seeds/users/bob-baumeister.seed'
const run = async (command: string) => {
// Database actions not supported by our migration library
@@ -45,8 +50,20 @@ const run = async (command: string) => {
await migration.down() // use for downgrade script
break
case 'reset':
+ // TODO protect from production
await migration.reset() // use for resetting database
break
+ case 'seed':
+ // TODO protect from production
+ await useSeeding({
+ root: process.cwd(),
+ configName: 'ormconfig.js',
+ })
+ await runSeeder(CreatePeterLustigSeed)
+ await runSeeder(CreateBibiBloxbergSeed)
+ await runSeeder(CreateRaeuberHotzenplotzSeed)
+ await runSeeder(CreateBobBaumeisterSeed)
+ break
default:
throw new Error(`Unsupported command ${command}`)
}
diff --git a/database/src/interface/UserContext.ts b/database/src/interface/UserContext.ts
new file mode 100644
index 000000000..4e188904c
--- /dev/null
+++ b/database/src/interface/UserContext.ts
@@ -0,0 +1,49 @@
+export interface UserContext {
+ pubkey?: Buffer
+ email?: string
+ firstName?: string
+ lastName?: string
+ username?: string
+ disabled?: boolean
+}
+
+export interface LoginUserContext {
+ email?: string
+ firstName?: string
+ lastName?: string
+ username?: string
+ description?: string
+ password?: BigInt
+ pubKey?: Buffer
+ privKey?: Buffer
+ emailHash?: Buffer
+ createdAt?: Date
+ emailChecked?: boolean
+ passphraseShown?: boolean
+ language?: string
+ disabled?: boolean
+ groupId?: number
+ publisherId?: number | null
+}
+
+export interface LoginUserBackupContext {
+ userId?: number
+ passphrase?: string
+ mnemonicType?: number
+}
+
+export interface ServerUserContext {
+ username?: string
+ password?: string
+ email?: string
+ role?: string
+ activated?: number
+ lastLogin?: Date
+ created?: Date
+ modified?: Date
+}
+
+export interface LoginUserRolesContext {
+ userId?: number
+ roleId?: number
+}
diff --git a/database/src/interface/UserInterface.ts b/database/src/interface/UserInterface.ts
new file mode 100644
index 000000000..942158593
--- /dev/null
+++ b/database/src/interface/UserInterface.ts
@@ -0,0 +1,30 @@
+export interface UserInterface {
+ // from login user (contains state user)
+ email?: string
+ firstName?: string
+ lastName?: string
+ username?: string
+ description?: string
+ password?: BigInt
+ pubKey?: Buffer
+ privKey?: Buffer
+ emailHash?: Buffer
+ createdAt?: Date
+ emailChecked?: boolean
+ passphraseShown?: boolean
+ language?: string
+ disabled?: boolean
+ groupId?: number
+ publisherId?: number | null
+ // from login user backup
+ passphrase?: string
+ mnemonicType?: number
+ // from server user
+ serverUserPassword?: string
+ role?: string
+ activated?: number
+ lastLogin?: Date
+ modified?: Date
+ // flag for admin
+ isAdmin?: boolean
+}
diff --git a/database/src/seeds/create-user.seed.ts b/database/src/seeds/create-user.seed.ts
new file mode 100644
index 000000000..ca3a182c4
--- /dev/null
+++ b/database/src/seeds/create-user.seed.ts
@@ -0,0 +1,11 @@
+import { Factory, Seeder } from 'typeorm-seeding'
+import { User } from '../../entity/User'
+// import { LoginUser } from '../../entity/LoginUser'
+
+export class CreateUserSeed implements Seeder {
+ public async run(factory: Factory): Promise {
+ // const loginUser = await factory(LoginUser)().make()
+ // console.log(loginUser.email)
+ await factory(User)().create()
+ }
+}
diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts
new file mode 100644
index 000000000..805104519
--- /dev/null
+++ b/database/src/seeds/helpers/user-helpers.ts
@@ -0,0 +1,91 @@
+import {
+ UserContext,
+ LoginUserContext,
+ LoginUserBackupContext,
+ ServerUserContext,
+ LoginUserRolesContext,
+} from '../../interface/UserContext'
+import { UserInterface } from '../../interface/UserInterface'
+import { User } from '../../../entity/User'
+import { LoginUser } from '../../../entity/LoginUser'
+import { LoginUserBackup } from '../../../entity/LoginUserBackup'
+import { ServerUser } from '../../../entity/ServerUser'
+import { LoginUserRoles } from '../../../entity/LoginUserRoles'
+import { Factory } from 'typeorm-seeding'
+
+export const userSeeder = async (factory: Factory, userData: UserInterface): Promise => {
+ await factory(User)(createUserContext(userData)).create()
+ const loginUser = await factory(LoginUser)(createLoginUserContext(userData)).create()
+ await factory(LoginUserBackup)(createLoginUserBackupContext(userData, loginUser)).create()
+
+ if (userData.isAdmin) {
+ await factory(ServerUser)(createServerUserContext(userData)).create()
+
+ // This is crazy: we just need the relation to roleId but no role at all
+ // It works with LoginRoles empty!!
+ await factory(LoginUserRoles)(createLoginUserRolesContext(loginUser)).create()
+ }
+}
+
+export const createUserContext = (context: UserInterface): UserContext => {
+ return {
+ pubkey: context.pubKey,
+ email: context.email,
+ firstName: context.firstName,
+ lastName: context.lastName,
+ username: context.username,
+ disabled: context.disabled,
+ }
+}
+
+export const createLoginUserContext = (context: UserInterface): LoginUserContext => {
+ return {
+ email: context.email,
+ firstName: context.firstName,
+ lastName: context.lastName,
+ username: context.username,
+ description: context.description,
+ password: context.password,
+ pubKey: context.pubKey,
+ privKey: context.privKey,
+ emailHash: context.emailHash,
+ createdAt: context.createdAt,
+ emailChecked: context.emailChecked,
+ passphraseShown: context.passphraseShown,
+ language: context.language,
+ disabled: context.disabled,
+ groupId: context.groupId,
+ publisherId: context.publisherId,
+ }
+}
+
+export const createLoginUserBackupContext = (
+ context: UserInterface,
+ loginUser: LoginUser,
+): LoginUserBackupContext => {
+ return {
+ passphrase: context.passphrase,
+ mnemonicType: context.mnemonicType,
+ userId: loginUser.id,
+ }
+}
+
+export const createServerUserContext = (context: UserInterface): ServerUserContext => {
+ return {
+ role: context.role,
+ username: context.username,
+ password: context.serverUserPassword,
+ email: context.email,
+ activated: context.activated,
+ created: context.createdAt,
+ lastLogin: context.lastLogin,
+ modified: context.modified,
+ }
+}
+
+export const createLoginUserRolesContext = (loginUser: LoginUser): LoginUserRolesContext => {
+ return {
+ userId: loginUser.id,
+ roleId: 1,
+ }
+}
diff --git a/database/src/seeds/users/bibi-bloxberg.seed.ts b/database/src/seeds/users/bibi-bloxberg.seed.ts
new file mode 100644
index 000000000..4899f3809
--- /dev/null
+++ b/database/src/seeds/users/bibi-bloxberg.seed.ts
@@ -0,0 +1,9 @@
+import { Factory, Seeder } from 'typeorm-seeding'
+import { bibiBloxberg } from './bibi-bloxberg'
+import { userSeeder } from '../helpers/user-helpers'
+
+export class CreateBibiBloxbergSeed implements Seeder {
+ public async run(factory: Factory): Promise {
+ await userSeeder(factory, bibiBloxberg)
+ }
+}
diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts
new file mode 100644
index 000000000..d87e3eb4a
--- /dev/null
+++ b/database/src/seeds/users/bibi-bloxberg.ts
@@ -0,0 +1,25 @@
+export const bibiBloxberg = {
+ email: 'bibi@bloxberg.de',
+ firstName: 'Bibi',
+ lastName: 'Bloxberg',
+ username: 'bibi',
+ description: 'Hex Hex',
+ password: BigInt('12825419584724616625'),
+ pubKey: Buffer.from('42de7e4754625b730018c3b4ea745a4d043d9d867af352d0f08871793dfa6743', 'hex'),
+ privKey: Buffer.from(
+ '60681365b6ad6fd500eae09ac8df0de6beb7554226e0ca1049e957cc6f202205b86e258bbbe98561a86bd9b986ea8b2a6c60abdff8a745f73c8932d4b6545a8da09bbcd6e18ec61a2ef30bac85f83c5d',
+ 'hex',
+ ),
+ emailHash: Buffer.from('38a0d8c8658a5681cc1180c5d9e2b2a18e4f611db8ab3ca61de4aa91ae94219b', 'hex'),
+ createdAt: new Date('2021-11-26T11:32:16'),
+ emailChecked: true,
+ passphraseShown: false,
+ language: 'de',
+ disabled: false,
+ groupId: 1,
+ publisherId: null,
+ passphrase:
+ 'knife normal level all hurdle crucial color avoid warrior stadium road bachelor affair topple hawk pottery right afford immune two ceiling budget glance hour ',
+ mnemonicType: 2,
+ isAdmin: false,
+}
diff --git a/database/src/seeds/users/bob-baumeister.seed.ts b/database/src/seeds/users/bob-baumeister.seed.ts
new file mode 100644
index 000000000..22ff30824
--- /dev/null
+++ b/database/src/seeds/users/bob-baumeister.seed.ts
@@ -0,0 +1,9 @@
+import { Factory, Seeder } from 'typeorm-seeding'
+import { bobBaumeister } from './bob-baumeister'
+import { userSeeder } from '../helpers/user-helpers'
+
+export class CreateBobBaumeisterSeed implements Seeder {
+ public async run(factory: Factory): Promise {
+ await userSeeder(factory, bobBaumeister)
+ }
+}
diff --git a/database/src/seeds/users/bob-baumeister.ts b/database/src/seeds/users/bob-baumeister.ts
new file mode 100644
index 000000000..33ce35656
--- /dev/null
+++ b/database/src/seeds/users/bob-baumeister.ts
@@ -0,0 +1,25 @@
+export const bobBaumeister = {
+ email: 'bob@baumeister.de',
+ firstName: 'Bob',
+ lastName: 'der Baumeister',
+ username: 'bob',
+ description: 'Können wir das schaffen? Ja, wir schaffen das!',
+ password: BigInt('3296644341468822636'),
+ pubKey: Buffer.from('a509d9a146374fc975e3677db801ae8a4a83bff9dea96da64053ff6de6b2dd7e', 'hex'),
+ privKey: Buffer.from(
+ 'd30606ac59c29058896180bebd6dcd1714dbdd697cc14b65eb4de9ef5241a5d5fc789eaab48957a887c45b7e71ab75c47fd132c14b99007891b5bdfb1026575009f0802b0126930803c113ab3f44e1be',
+ 'hex',
+ ),
+ emailHash: Buffer.from('4b8ce4e175587aaf33da19e272719da1a547daff557820191fab0c65c5a3b7f1', 'hex'),
+ createdAt: new Date('2021-11-26T11:36:31'),
+ emailChecked: true,
+ passphraseShown: false,
+ language: 'de',
+ disabled: false,
+ groupId: 1,
+ publisherId: null,
+ passphrase:
+ 'detail master source effort unable waste tilt flush domain orchard art truck hint barrel response gate impose peanut secret merry three uncle wink resource ',
+ mnemonicType: 2,
+ isAdmin: false,
+}
diff --git a/database/src/seeds/users/peter-lustig.admin.seed.ts b/database/src/seeds/users/peter-lustig.admin.seed.ts
new file mode 100644
index 000000000..9dd3fd4c7
--- /dev/null
+++ b/database/src/seeds/users/peter-lustig.admin.seed.ts
@@ -0,0 +1,9 @@
+import { Factory, Seeder } from 'typeorm-seeding'
+import { peterLustig } from './peter-lustig'
+import { userSeeder } from '../helpers/user-helpers'
+
+export class CreatePeterLustigSeed implements Seeder {
+ public async run(factory: Factory): Promise {
+ await userSeeder(factory, peterLustig)
+ }
+}
diff --git a/database/src/seeds/users/peter-lustig.ts b/database/src/seeds/users/peter-lustig.ts
new file mode 100644
index 000000000..63caf55f6
--- /dev/null
+++ b/database/src/seeds/users/peter-lustig.ts
@@ -0,0 +1,30 @@
+export const peterLustig = {
+ email: 'peter@lustig.de',
+ firstName: 'Peter',
+ lastName: 'Lustig',
+ username: 'peter',
+ description: 'Latzhose und Nickelbrille',
+ password: BigInt('3917921995996627700'),
+ pubKey: Buffer.from('7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', 'hex'),
+ privKey: Buffer.from(
+ '3c7c0253033212ed983f6bb10ce73362a99f0bd01d4d1b21ca702d532ca32710ba36abf72a22a963b9026e764e954f441f4905b87a66861bd6b9d9689b7f8aefea66cc493e21da4244e85be81660b9c4',
+ 'hex',
+ ),
+ emailHash: Buffer.from('9f700e6f6ec351a140b674c0edd4479509697b023bd8bee8826915ef6c2af036', 'hex'),
+ createdAt: new Date('2020-11-25T10:48:43'),
+ emailChecked: true,
+ passphraseShown: false,
+ language: 'de',
+ disabled: false,
+ groupId: 1,
+ publisherId: null,
+ passphrase:
+ 'okay property choice naive calm present weird increase stuff royal vibrant frame attend wood one else tribe pull hedgehog woman kitchen hawk snack smart ',
+ mnemonicType: 2,
+ role: 'admin',
+ serverUserPassword: '$2y$10$TzIWLeZoKs251gwrhSQmHeKhKI/EQ4EV5ClfAT8Ufnb4lcUXPa5X.',
+ activated: 1,
+ lastLogin: new Date('2021-10-27T12:25:57'),
+ modified: new Date('2021-09-27T12:25:57'),
+ isAdmin: true,
+}
diff --git a/database/src/seeds/users/raeuber-hotzenplotz.seed.ts b/database/src/seeds/users/raeuber-hotzenplotz.seed.ts
new file mode 100644
index 000000000..2399136d2
--- /dev/null
+++ b/database/src/seeds/users/raeuber-hotzenplotz.seed.ts
@@ -0,0 +1,9 @@
+import { Factory, Seeder } from 'typeorm-seeding'
+import { raeuberHotzenplotz } from './raeuber-hotzenplotz'
+import { userSeeder } from '../helpers/user-helpers'
+
+export class CreateRaeuberHotzenplotzSeed implements Seeder {
+ public async run(factory: Factory): Promise {
+ await userSeeder(factory, raeuberHotzenplotz)
+ }
+}
diff --git a/database/src/seeds/users/raeuber-hotzenplotz.ts b/database/src/seeds/users/raeuber-hotzenplotz.ts
new file mode 100644
index 000000000..10fcf8af1
--- /dev/null
+++ b/database/src/seeds/users/raeuber-hotzenplotz.ts
@@ -0,0 +1,25 @@
+export const raeuberHotzenplotz = {
+ email: 'raeuber@hotzenplotz.de',
+ firstName: 'Räuber',
+ lastName: 'Hotzenplotz',
+ username: 'räuber',
+ description: 'Pfefferpistole',
+ password: BigInt('12123692783243004812'),
+ pubKey: Buffer.from('d7c70f94234dff071d982aa8f41583876c356599773b5911b39080da2b8c2d2b', 'hex'),
+ privKey: Buffer.from(
+ 'c4ede7e7e65acd4cc0a2d91136ee8f753c6903b3594798afde341092b21a4c1589f296d43c6e7adcd7602fcc2a2bcbf74c9f42453ad49cc5186eadf654bbd2c5fa9aa027f152592819246da896ebfcd2',
+ 'hex',
+ ),
+ emailHash: Buffer.from('ec8d34112adb40ff2f6538b05660b03440372690f034cd7d6322d17020233c77', 'hex'),
+ createdAt: new Date('2021-11-26T11:32:16'),
+ emailChecked: true,
+ passphraseShown: false,
+ language: 'de',
+ disabled: false,
+ groupId: 1,
+ publisherId: null,
+ passphrase:
+ 'gospel trip tenant mouse spider skill auto curious man video chief response same little over expire drum display fancy clinic keen throw urge basket ',
+ mnemonicType: 2,
+ isAdmin: false,
+}
diff --git a/database/yarn.lock b/database/yarn.lock
index 1afbc7f09..25b236371 100644
--- a/database/yarn.lock
+++ b/database/yarn.lock
@@ -122,6 +122,11 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
+"@types/faker@^5.5.9":
+ version "5.5.9"
+ resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.9.tgz#588ede92186dc557bff8341d294335d50d255f0c"
+ integrity sha512-uCx6mP3UY5SIO14XlspxsGjgaemrxpssJI0Ol+GfhxtcKpv9pgRZYsS4eeKeHVLje6Qtc8lGszuBI461+gVZBA==
+
"@types/json-schema@^7.0.7":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@@ -457,7 +462,7 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
-camelcase@^5.3.1:
+camelcase@^5.0.0, camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
@@ -473,7 +478,7 @@ chalk@^1.1.1:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
-chalk@^2.0.0:
+chalk@^2.0.0, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -523,6 +528,13 @@ cli-boxes@^2.2.0:
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
+cli-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ dependencies:
+ restore-cursor "^3.1.0"
+
cli-highlight@^2.1.11:
version "2.1.11"
resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf"
@@ -535,6 +547,20 @@ cli-highlight@^2.1.11:
parse5-htmlparser2-tree-adapter "^6.0.0"
yargs "^16.0.0"
+cli-spinners@^2.2.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
+ integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
+
+cliui@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+ integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^6.2.0"
+
cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
@@ -551,6 +577,11 @@ clone-response@^1.0.2:
dependencies:
mimic-response "^1.0.0"
+clone@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+ integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -616,6 +647,11 @@ crypto-random-string@^2.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
+crypto@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
+ integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
+
debug@^2.2.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -637,6 +673,11 @@ debug@^4.0.1, debug@^4.1.1, debug@^4.3.1:
dependencies:
ms "2.1.2"
+decamelize@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
@@ -654,6 +695,13 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+defaults@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+ integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
+ dependencies:
+ clone "^1.0.2"
+
defer-to-connect@^1.0.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
@@ -1002,6 +1050,16 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+faker@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f"
+ integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=
+
+faker@^5.5.3:
+ version "5.5.3"
+ resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e"
+ integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -1066,6 +1124,14 @@ find-up@^2.0.0, find-up@^2.1.0:
dependencies:
locate-path "^2.0.0"
+find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
@@ -1106,7 +1172,7 @@ generate-function@^2.3.1:
dependencies:
is-property "^1.0.2"
-get-caller-file@^2.0.5:
+get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
@@ -1141,6 +1207,18 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
dependencies:
is-glob "^4.0.1"
+glob@7.1.6:
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
glob@^7.1.3:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
@@ -1434,6 +1512,11 @@ is-installed-globally@^0.3.1:
global-dirs "^2.0.1"
is-path-inside "^3.0.1"
+is-interactive@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
+ integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+
is-negative-zero@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
@@ -1605,6 +1688,13 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
@@ -1620,6 +1710,13 @@ lodash.truncate@^4.4.2:
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
+log-symbols@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
+ integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==
+ dependencies:
+ chalk "^2.4.2"
+
long@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
@@ -1675,6 +1772,11 @@ micromatch@^4.0.4:
braces "^3.0.1"
picomatch "^2.2.3"
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
@@ -1712,6 +1814,11 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+mute-stream@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+ integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+
mysql2@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.3.0.tgz#600f5cc27e397dfb77b59eac93666434f88e8079"
@@ -1841,6 +1948,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
+onetime@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@@ -1853,6 +1967,20 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
+ora@4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.3.tgz#752a1b7b4be4825546a7a3d59256fa523b6b6d05"
+ integrity sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg==
+ dependencies:
+ chalk "^3.0.0"
+ cli-cursor "^3.1.0"
+ cli-spinners "^2.2.0"
+ is-interactive "^1.0.0"
+ log-symbols "^3.0.0"
+ mute-stream "0.0.8"
+ strip-ansi "^6.0.0"
+ wcwidth "^1.0.1"
+
p-cancelable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
@@ -1865,6 +1993,13 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
+p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -1872,11 +2007,23 @@ p-locate@^2.0.0:
dependencies:
p-limit "^1.1.0"
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
package-json@^6.3.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
@@ -1929,6 +2076,11 @@ path-exists@^3.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -2094,7 +2246,7 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
-reflect-metadata@^0.1.13:
+reflect-metadata@0.1.13, reflect-metadata@^0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
@@ -2128,6 +2280,11 @@ require-from-string@^2.0.2:
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+require-main-filename@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -2148,6 +2305,14 @@ responselike@^1.0.2:
dependencies:
lowercase-keys "^1.0.0"
+restore-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+ dependencies:
+ onetime "^5.1.0"
+ signal-exit "^3.0.2"
+
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
@@ -2216,6 +2381,11 @@ seq-queue@^0.0.5:
resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=
+set-blocking@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
sha.js@^2.4.11:
version "2.4.11"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
@@ -2532,6 +2702,18 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
+typeorm-seeding@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/typeorm-seeding/-/typeorm-seeding-1.6.1.tgz#4fe3a1aec9a611007d1135419cde286cced8defd"
+ integrity sha512-xJIW1pp72hv6npPqbQ7xDvawcDmS60EDUjK++UCfiqT0WE4xTzCn+QK1ZijLkD3GYCqFPuFt4nmeyRJn6VO2Vw==
+ dependencies:
+ chalk "^4.0.0"
+ faker "4.1.0"
+ glob "7.1.6"
+ ora "4.0.3"
+ reflect-metadata "0.1.13"
+ yargs "15.3.1"
+
typeorm@^0.2.38:
version "0.2.38"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.38.tgz#2af08079919f6ab04cd17017f9faa2c8d5cd566f"
@@ -2635,6 +2817,13 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
+wcwidth@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+ integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+ dependencies:
+ defaults "^1.0.3"
+
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
@@ -2646,6 +2835,11 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
+which-module@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+ integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
@@ -2665,6 +2859,15 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -2707,6 +2910,11 @@ xmlbuilder@~11.0.0:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
+y18n@^4.0.0:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+ integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
+
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
@@ -2731,11 +2939,36 @@ yargonaut@^1.1.4:
figlet "^1.1.1"
parent-require "^1.0.0"
+yargs-parser@^18.1.1:
+ version "18.1.3"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+ integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
yargs-parser@^20.2.2:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+yargs@15.3.1:
+ version "15.3.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b"
+ integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==
+ dependencies:
+ cliui "^6.0.0"
+ decamelize "^1.2.0"
+ find-up "^4.1.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^4.2.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^18.1.1"
+
yargs@^16.0.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json
index 0d8980fc4..478c9cba5 100644
--- a/frontend/src/locales/de.json
+++ b/frontend/src/locales/de.json
@@ -114,6 +114,11 @@
"message": "hallo gradido !!",
"overview": "Übersicht",
"privacy_policy": "Datenschutzerklärung",
+ "publisher": {
+ "infoNoRegister": "Dies ist für die Registrieung nicht nötig!",
+ "infoText": "Trage hier die ID des Herausgebers ein. Wenn du keine ID hast dann bitte leer lassen.",
+ "publisherId": "PublisherID"
+ },
"send": "Senden",
"settings": {
"coinanimation": {
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 3ceef637e..179d5b4b4 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -114,6 +114,11 @@
"message": "hello gradido !!",
"overview": "Overview",
"privacy_policy": "Privacy policy",
+ "publisher": {
+ "infoNoRegister": "This is not necessary for registration!",
+ "infoText": "Enter the ID of the publisher here. If you do not have an ID, please leave it blank.",
+ "publisherId": "PublisherID"
+ },
"send": "Send",
"settings": {
"coinanimation": {
diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js
index 829678b44..4f25f5352 100644
--- a/frontend/src/store/store.test.js
+++ b/frontend/src/store/store.test.js
@@ -11,6 +11,7 @@ const {
coinanimation,
newsletterState,
publisherId,
+ isAdmin,
community,
hasElopage,
} = mutations
@@ -104,6 +105,14 @@ describe('Vuex store', () => {
})
})
+ describe('isAdmin', () => {
+ it('sets the state of isAdmin', () => {
+ const state = { isAdmin: null }
+ isAdmin(state, true)
+ expect(state.isAdmin).toEqual(true)
+ })
+ })
+
describe('community', () => {
it('sets the state of community', () => {
const state = {}
diff --git a/frontend/src/views/Pages/Register.spec.js b/frontend/src/views/Pages/Register.spec.js
index e33e35190..b75c881c6 100644
--- a/frontend/src/views/Pages/Register.spec.js
+++ b/frontend/src/views/Pages/Register.spec.js
@@ -164,6 +164,11 @@ describe('Register', () => {
expect(wrapper.find('#registerCheckbox').exists()).toBeTruthy()
})
+ it('has PublisherId input fields', () => {
+ wrapper.find('.publisherCollaps').trigger('click')
+ expect(wrapper.find('#publisherid').exists()).toBe(true)
+ })
+
it('has disabled submit button when not completely filled', () => {
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled')
})
@@ -213,6 +218,11 @@ describe('Register', () => {
wrapper.find('#registerLastname').setValue('Mustermann')
wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net')
wrapper.find('.language-switch-select').findAll('option').at(1).setSelected()
+ wrapper.find('#publisherid').setValue('12345')
+ })
+
+ it('commits publisherId to store', () => {
+ expect(mockStoreCommit).toBeCalledWith('publisherId', 12345)
})
it('has enabled submit button when completely filled', () => {
diff --git a/frontend/src/views/Pages/Register.vue b/frontend/src/views/Pages/Register.vue
index d6e849345..070d80bb9 100755
--- a/frontend/src/views/Pages/Register.vue
+++ b/frontend/src/views/Pages/Register.vue
@@ -117,8 +117,44 @@
{{ messageError }}
+
+
+ {{ $t('publisher.publisherId') }} : {{ $store.state.publisherId }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+