Merge branch 'master' into bugfix_registration

This commit is contained in:
Ulf Gebhardt 2021-11-22 23:27:58 +01:00 committed by GitHub
commit 73c9f7c2ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
232 changed files with 28876 additions and 10466 deletions

View File

@ -1,13 +1,13 @@
---
name: 🏅 Release
name: 🎂 Release
about: Define a Release
labels: release
title: 🏅 [RELEASE]
title: 🎂 [RELEASE]
---
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
<!-- Proceed only if you know what you are doing - have a chat with Project's Team first -->
## 🏅 RELEASE
## 🎂 RELEASE
<!-- Describe your Release in detail. Include screenshots and drawings -->

View File

@ -72,7 +72,7 @@ jobs:
##########################################################################
- name: Backend | Build `production` image
run: |
docker build --target production -t "gradido/backend:latest" -t "gradido/backend:production" -t "gradido/backend:${VERSION}" -t "gradido/backend:${BUILD_VERSION}" backend/
docker build -f ./backend/Dockerfile --target production -t "gradido/backend:latest" -t "gradido/backend:production" -t "gradido/backend:${VERSION}" -t "gradido/backend:${BUILD_VERSION}" .
docker save "gradido/backend" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2

View File

@ -29,6 +29,32 @@ jobs:
name: docker-frontend-test
path: /tmp/frontend.tar
##############################################################################
# JOB: DOCKER BUILD TEST ADMIN INTERFACE #####################################
##############################################################################
build_test_admin:
name: Docker Build Test - Admin Interface
runs-on: ubuntu-latest
#needs: [nothing]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# ADMIN INTERFACE ########################################################
##########################################################################
- name: Admin | Build `test` image
run: |
docker build --target test -t "gradido/admin:test" admin/
docker save "gradido/admin:test" > /tmp/admin.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-admin-test
path: /tmp/admin.tar
##############################################################################
# JOB: DOCKER BUILD TEST BACKEND #############################################
##############################################################################
@ -47,7 +73,7 @@ jobs:
##########################################################################
- name: Backend | Build `test` image
run: |
docker build --target test -t "gradido/backend:test" backend/
docker build -f ./backend/Dockerfile --target test -t "gradido/backend:test" .
docker save "gradido/backend:test" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
@ -147,7 +173,7 @@ jobs:
##########################################################################
- name: mariadb | Build `test` image
run: |
docker build --target mariadb_server_test -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./
docker build --target mariadb_server -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./
docker save "gradido/mariadb:test" > /tmp/mariadb.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
@ -240,7 +266,36 @@ jobs:
run: docker run --rm gradido/frontend:test yarn run lint
##############################################################################
# JOB: LINT BACKEND #########################################################
# JOB: LINT ADMIN INTERFACE ##################################################
##############################################################################
lint_admin:
name: Lint - Admin Interface
runs-on: ubuntu-latest
needs: [build_test_admin]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Admin Interface)
uses: actions/download-artifact@v2
with:
name: docker-admin-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/admin.tar
##########################################################################
# LINT ADMIN INTERFACE ###################################################
##########################################################################
- name: Admin Interface | Lint
run: docker run --rm gradido/admin:test yarn run lint
##############################################################################
# JOB: LINT BACKEND ##########################################################
##############################################################################
lint_backend:
name: Lint - Backend
@ -344,16 +399,16 @@ jobs:
report_name: Coverage Frontend
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 76
min_coverage: 86
token: ${{ github.token }}
##############################################################################
# JOB: UNIT TEST BACKEND ####################################################
# JOB: UNIT TEST ADMIN INTERFACE #############################################
##############################################################################
unit_test_backend:
name: Unit tests - Backend
unit_test_admin:
name: Unit tests - Admin Interface
runs-on: ubuntu-latest
needs: [build_test_backend]
needs: [build_test_admin]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
@ -363,6 +418,55 @@ jobs:
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Admin Interface)
uses: actions/download-artifact@v2
with:
name: docker-admin-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/admin.tar
##########################################################################
# UNIT TESTS ADMIN INTERFACE #############################################
##########################################################################
- name: Admin Interface | Unit tests
run: |
docker run -v ~/coverage:/app/coverage --rm gradido/admin:test yarn run test
cp -r ~/coverage ./coverage
##########################################################################
# COVERAGE CHECK ADMIN INTERFACE #########################################
##########################################################################
- name: Admin Interface | Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage Admin Interface
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 65
token: ${{ github.token }}
##############################################################################
# JOB: UNIT TEST BACKEND ####################################################
##############################################################################
unit_test_backend:
name: Unit tests - Backend
runs-on: ubuntu-latest
needs: [build_test_backend,build_test_mariadb]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Mariadb)
uses: actions/download-artifact@v2
with:
name: docker-mariadb-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/mariadb.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
with:
@ -373,10 +477,11 @@ jobs:
##########################################################################
# UNIT TESTS BACKEND #####################################################
##########################################################################
- name: backend | Unit tests
run: |
docker run -v ~/coverage:/app/coverage --rm gradido/backend:test yarn run test
cp -r ~/coverage ./coverage
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb database
- name: backend Unit tests | test
run: cd database && yarn && cd ../backend && yarn && yarn test
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn test
##########################################################################
# COVERAGE CHECK BACKEND #################################################
##########################################################################
@ -385,8 +490,8 @@ jobs:
with:
report_name: Coverage Backend
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 3
result_path: ./backend/coverage/lcov.info
min_coverage: 38
token: ${{ github.token }}
##############################################################################
@ -409,10 +514,16 @@ jobs:
--health-timeout=3s
--health-retries=4
steps:
- name: Debug service
run: echo "$(docker ps)"
- name: Debug container choosing script
run: echo "$(docker container ls | grep mariadb | awk '{ print $1 }')"
# - name: Debug service
# run: echo "$(docker ps)"
#- name: Debug container choosing script
# run: echo "$(docker container ls | grep mariadb | awk '{ print $1 }')"
- name: get mariadb container id
run: echo "::set-output name=id::$(docker container ls | grep mariadb | awk '{ print $1 }')"
id: mariadb_container
- name: get automatic created network
run: echo "::set-output name=id::$(docker network ls | grep github_network | awk '{ print $1 }')"
id: network
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
@ -420,6 +531,11 @@ jobs:
uses: actions/checkout@v2
with:
submodules: true
# Database migration
- name: Start database migration
run: |
docker build --target production_up -t "gradido/database:production_up" database/
docker run --network ${{ steps.network.outputs.id }} --name=database --env NODE_ENV=production --env DB_HOST=mariadb --env DB_DATABASE=gradido_community_test -d gradido/database:production_up
##########################################################################
# Build Login-Server Test Docker image ###################################
##########################################################################

1
.gitignore vendored
View File

@ -2,7 +2,6 @@
/node_modules/*
.vscode
messages.pot
.skeema
nbproject
.metadata
/.env

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"streetsidesoftware.code-spell-checker",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

View File

@ -4,8 +4,153 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [1.3.1](https://github.com/gradido/gradido/compare/1.2.1...1.3.1)
#### [1.5.1](https://github.com/gradido/gradido/compare/1.5.0...1.5.1)
> 15 October 2021
- fix isExitInDb [`#994`](https://github.com/gradido/gradido/pull/994)
- fix [`80228ef`](https://github.com/gradido/gradido/commit/80228ef842d4087ea4b80934b15b8112611e3e33)
#### [1.5.0](https://github.com/gradido/gradido/compare/1.4.0...1.5.0)
> 15 October 2021
- v1.5.0 [`#986`](https://github.com/gradido/gradido/pull/986)
- default value for empty field [`#993`](https://github.com/gradido/gradido/pull/993)
- fix language registriert [`#992`](https://github.com/gradido/gradido/pull/992)
- Change the publisherId to nullable. [`#991`](https://github.com/gradido/gradido/pull/991)
- Revert "deliver a decay block every time" [`#990`](https://github.com/gradido/gradido/pull/990)
- update first name, last name and username in community server db on c… [`#989`](https://github.com/gradido/gradido/pull/989)
- deliver a decay block every time [`#988`](https://github.com/gradido/gradido/pull/988)
- refactor: Activate Registration [`#978`](https://github.com/gradido/gradido/pull/978)
- fix: summe-gdd-schoepfun [`#980`](https://github.com/gradido/gradido/pull/980)
- fix: Use TransactionId as Collapse Identifier [`#985`](https://github.com/gradido/gradido/pull/985)
- icon in one row - fix [`#979`](https://github.com/gradido/gradido/pull/979)
- error rounding in transactions list fixed [`#974`](https://github.com/gradido/gradido/pull/974)
- fix publisherId config [`#973`](https://github.com/gradido/gradido/pull/973)
- refactor: In Production only Show one Community [`#968`](https://github.com/gradido/gradido/pull/968)
- 804 elopage registration [`#959`](https://github.com/gradido/gradido/pull/959)
- Add autocorrect keys [`#960`](https://github.com/gradido/gradido/pull/960)
- check for elopage account [`#949`](https://github.com/gradido/gradido/pull/949)
- check pid on gdt server [`#958`](https://github.com/gradido/gradido/pull/958)
- add check for empty user list [`#957`](https://github.com/gradido/gradido/pull/957)
- feat: Improve Test of Navigation Guard of Vue Router [`#950`](https://github.com/gradido/gradido/pull/950)
- refactor: Clean Up Frontend Links, Tests and Logic [`#952`](https://github.com/gradido/gradido/pull/952)
- feat: Global Catch of Publisher ID to Store [`#943`](https://github.com/gradido/gradido/pull/943)
- Add User on createUser to community server db [`#915`](https://github.com/gradido/gradido/pull/915)
- Frontend tests & coinanimation fixes [`#936`](https://github.com/gradido/gradido/pull/936)
- 803 community selection page of registration process 2 [`#918`](https://github.com/gradido/gradido/pull/918)
- feat: Test Vue Router [`#940`](https://github.com/gradido/gradido/pull/940)
- Frontend style documentation wallet-app [`#938`](https://github.com/gradido/gradido/pull/938)
- Clean graphql structure [`#935`](https://github.com/gradido/gradido/pull/935)
- silence webpack, it was very noisy [`#934`](https://github.com/gradido/gradido/pull/934)
- Community test database fix [`#932`](https://github.com/gradido/gradido/pull/932)
- 585 coin animation on and off switchable [`#813`](https://github.com/gradido/gradido/pull/813)
- Bug fixes recent changes [`#931`](https://github.com/gradido/gradido/pull/931)
- fix migrations to use only the entity model [`#928`](https://github.com/gradido/gradido/pull/928)
- Refactor graphql [`#929`](https://github.com/gradido/gradido/pull/929)
- Clean models2 [`#927`](https://github.com/gradido/gradido/pull/927)
- Docu graphics mocks [`#930`](https://github.com/gradido/gradido/pull/930)
- feat: Setup Unit Tests for Backend [`#920`](https://github.com/gradido/gradido/pull/920)
- refactor: Provide pubKey in Resolver Context to Avoid API Calls [`#906`](https://github.com/gradido/gradido/pull/906)
- fix: Double Redirect After Logout with Expired JWT [`#914`](https://github.com/gradido/gradido/pull/914)
- fix: Whitespace in Password Shows Validation Error [`#907`](https://github.com/gradido/gradido/pull/907)
- feat: Test Change Language in User Profile [`#904`](https://github.com/gradido/gradido/pull/904)
- Apollo list transactions [`#893`](https://github.com/gradido/gradido/pull/893)
- Remove api endpoints from frontend config [`#912`](https://github.com/gradido/gradido/pull/912)
- register process simplified [`#908`](https://github.com/gradido/gradido/pull/908)
- Cleaned up locales for settings and adjusted texts in files [`#905`](https://github.com/gradido/gradido/pull/905)
- fix: Warnings in Gdd Transaction List Spec [`#903`](https://github.com/gradido/gradido/pull/903)
- docu release plan [`#868`](https://github.com/gradido/gradido/pull/868)
- update password check, allow every special char [`#890`](https://github.com/gradido/gradido/pull/890)
- fix error with apollo, typeorm and build [`#897`](https://github.com/gradido/gradido/pull/897)
- Db check with typeorm [`#894`](https://github.com/gradido/gradido/pull/894)
- fix bug col to cols on &lt;b-col&gt; [`#895`](https://github.com/gradido/gradido/pull/895)
- bug fix givenType 2,3,5,6 change to givenType 1 [`#896`](https://github.com/gradido/gradido/pull/896)
- Apollo list gdt transactions [`#891`](https://github.com/gradido/gradido/pull/891)
- 826 different design of gdd and gdt lists [`#886`](https://github.com/gradido/gradido/pull/886)
- decay calculation is calculated [`#815`](https://github.com/gradido/gradido/pull/815)
- round corners removed from profile settings page [`#887`](https://github.com/gradido/gradido/pull/887)
- Implement getBalance in Apollo [`#831`](https://github.com/gradido/gradido/pull/831)
- feat: Redirect to Login when JWT has expired [`#889`](https://github.com/gradido/gradido/pull/889)
- refactor: Queries to Mutations [`#863`](https://github.com/gradido/gradido/pull/863)
- Community-Server Test with new db migration [`#881`](https://github.com/gradido/gradido/pull/881)
- fix database not reading config [`#871`](https://github.com/gradido/gradido/pull/871)
- 733 language switch toast success [`#880`](https://github.com/gradido/gradido/pull/880)
- UserProfil Language switch has an error & UserProfil Language switch don't show right language text [`#879`](https://github.com/gradido/gradido/pull/879)
- docker skeema: mount sql files folder dynamic [`#864`](https://github.com/gradido/gradido/pull/864)
- disable auto-resend of not finished transactions on login-server restart [`#865`](https://github.com/gradido/gradido/pull/865)
- add commit hash with link to github into frontend [`#794`](https://github.com/gradido/gradido/pull/794)
- mock_choose_community [`#844`](https://github.com/gradido/gradido/pull/844)
- Remove discord link for issue templates [`#845`](https://github.com/gradido/gradido/pull/845)
- update docker files [`#830`](https://github.com/gradido/gradido/pull/830)
- added publisher_id field to user [`#245`](https://github.com/gradido/gradido/pull/245)
- webpack update [`#811`](https://github.com/gradido/gradido/pull/811)
- resolvers [`562ad9a`](https://github.com/gradido/gradido/commit/562ad9ae31d97f90a371452bed1ffe10ebf2d3a5)
- deleted inputs (now args) [`8ab542a`](https://github.com/gradido/gradido/commit/8ab542a28acf6b78d7a9e7fe7757363d225f7b4f)
- fix UserCard_CoinAnimation to properly use the store, have 100% coverage and other minor fixes & simplifications [`ce826de`](https://github.com/gradido/gradido/commit/ce826deb1d6d92caba514713539dca2da3f74de7)
#### [1.4.0](https://github.com/gradido/gradido/compare/1.3.1...1.4.0)
> 16 September 2021
- feat: Release 1.4.0 [`#814`](https://github.com/gradido/gradido/pull/814)
- Integration of the KlicktippAPI to the User management [`#753`](https://github.com/gradido/gradido/pull/753)
- feat: Locale Management [`#809`](https://github.com/gradido/gradido/pull/809)
- feat: Increase Coverage Test Frontend [`#812`](https://github.com/gradido/gradido/pull/812)
- My thoughts to jwt [`#800`](https://github.com/gradido/gradido/pull/800)
- Remove discord link [`#808`](https://github.com/gradido/gradido/pull/808)
- refactor: Pagination Buttons [`#806`](https://github.com/gradido/gradido/pull/806)
- add new components selectLanguage and Usercard_Language [`#798`](https://github.com/gradido/gradido/pull/798)
- gdt transaction with arrays and without slots [`#793`](https://github.com/gradido/gradido/pull/793)
- feat: New JWT in Every Authenticated Response [`#797`](https://github.com/gradido/gradido/pull/797)
- fix euro after comma gdt view [`#799`](https://github.com/gradido/gradido/pull/799)
- different path for checkEmail and resetPassword [`#796`](https://github.com/gradido/gradido/pull/796)
- fix old frontend wrong display of event gdt [`#795`](https://github.com/gradido/gradido/pull/795)
- update scripts and doc for login-server setup without docker [`#783`](https://github.com/gradido/gradido/pull/783)
- fix: Flaky Test for Logout [`#792`](https://github.com/gradido/gradido/pull/792)
- Analyse Iota Colored Coins [`#779`](https://github.com/gradido/gradido/pull/779)
- Fix Bug in displaying GDT in Old frontend [`#788`](https://github.com/gradido/gradido/pull/788)
- feat: JSON Web Token for Authentification [`#777`](https://github.com/gradido/gradido/pull/777)
- select language during registration [`#778`](https://github.com/gradido/gradido/pull/778)
- Fix missing gdt id [`#782`](https://github.com/gradido/gradido/pull/782)
- Remove Migrations from community server [`#776`](https://github.com/gradido/gradido/pull/776)
- fix_database_migrations [`#775`](https://github.com/gradido/gradido/pull/775)
- decay with the value 0 is no longer displayed [`#773`](https://github.com/gradido/gradido/pull/773)
- database_migrations [`#770`](https://github.com/gradido/gradido/pull/770)
- backend_version [`#756`](https://github.com/gradido/gradido/pull/756)
- issue_type_release [`#769`](https://github.com/gradido/gradido/pull/769)
- some_docu [`#771`](https://github.com/gradido/gradido/pull/771)
- database_reachable_in_testmode [`#768`](https://github.com/gradido/gradido/pull/768)
- logo changed, old logos and icons deleted [`#734`](https://github.com/gradido/gradido/pull/734)
- change default value of communty url [`#755`](https://github.com/gradido/gradido/pull/755)
- feat: Testing Tabs of TransactionLists [`#737`](https://github.com/gradido/gradido/pull/737)
- Added a fetchPolicy: 'no-cache' to the login vall. [`#736`](https://github.com/gradido/gradido/pull/736)
- Docu fragen community [`#731`](https://github.com/gradido/gradido/pull/731)
- feat: Vue Apollo Client [`#701`](https://github.com/gradido/gradido/pull/701)
- change text from Geld to Gradidos [`#711`](https://github.com/gradido/gradido/pull/711)
- fix fix [`#728`](https://github.com/gradido/gradido/pull/728)
- sort locales [`ec12a28`](https://github.com/gradido/gradido/commit/ec12a28f81577d530f58b42b7f8c2c7d20dffd64)
- feat: Unify and Sort Locales [`aba4f4d`](https://github.com/gradido/gradido/commit/aba4f4d20e0a13016e3528a1c5c30c111eb3a9f1)
- feat: Increase Coverage [`3c061bc`](https://github.com/gradido/gradido/commit/3c061bcb8d1a3a47442ed6a351e1428e15b314aa)
#### [1.3.1](https://github.com/gradido/gradido/compare/1.3.0...1.3.1)
> 12 August 2021
- fix: Translations and Formula Display [`#727`](https://github.com/gradido/gradido/pull/727)
- 612 docu structure [`#688`](https://github.com/gradido/gradido/pull/688)
- Community update for gdt list GDT transaction format [`#726`](https://github.com/gradido/gradido/pull/726)
- [#612] new directory structure in /docu/Concepts [`10bf3b0`](https://github.com/gradido/gradido/commit/10bf3b0cdfa6c44f879be0155e93f636601a051b)
- #612 additional documents [`ac0ed4f`](https://github.com/gradido/gradido/commit/ac0ed4fee81caff26d09b5de47dd130f12abdb45)
- #612 docu restructuring [`e67e1c4`](https://github.com/gradido/gradido/commit/e67e1c41e78264698e6fae4cf1d29751de7e7b29)
#### [1.3.0](https://github.com/gradido/gradido/compare/1.2.1...1.3.0)
> 12 August 2021
- Update README.md [`#719`](https://github.com/gradido/gradido/pull/719)
- fixed bugs, step 1 [`#718`](https://github.com/gradido/gradido/pull/718)
- update code for correct transactions listening [`#725`](https://github.com/gradido/gradido/pull/725)
- bugfix: change to new frontend passwort reset [`#724`](https://github.com/gradido/gradido/pull/724)
- feat: Add Pagination Buttons to GDT Transaction List [`#716`](https://github.com/gradido/gradido/pull/716)

View File

@ -8,33 +8,73 @@ The Gradido model can create global prosperity and peace
The Corona crisis has fundamentally changed our world within a very short time.
The dominant financial system threatens to fail around the globe, followed by mass insolvencies, record unemployment and abject poverty. Only with a sustainable new monetary system can humanity master these challenges of the 21st century. The Gradido Academy for Bionic Economy has developed such a system.
Find out more about the Project on its [Website](https://gradido.net/). It is offering vast resources about the idea. The remaining document will discuss the gradido software only.
## Software requirements
Currently we only support `docker` as environment to run all services, since many different programming languages and frameworks are used.
Currently we only support `docker` install instructions to run all services, since many different programming languages and frameworks are used.
- [docker](https://www.docker.com/)
- [docker](https://www.docker.com/)
- [docker-compose]
### For Arch Linux
Install the required packages:
```bash
sudo pacman -S docker
sudo pacman -S docker-compose
```
Add group `docker` and then your user to it in order to allow you to run docker without sudo
```bash
sudo groupadd docker # may already exist `groupadd: group 'docker' already exists`
sudo usermod -aG docker $USER
groups # verify you have the group (requires relog)
```
Start the docker service:
```bash
sudo systemctrl start docker
```
## How to run?
1. Clone the repo and pull all submodules
### 1. Clone Sources
Clone the repo and pull all submodules
```bash
git clone git@github.com:gradido/gradido.git
git submodule update --recursive --init
```
2. Run docker compose
1. Run docker compose for the debug build
### 2. Run docker-compose
Run docker-compose to bring up the development environment
```bash
docker-compose up
```
### Additional Build options
If you want to build for production you can do this aswell:
```bash
docker-compose -f docker-compose.yml up
```
```bash
docker-compose up
```
## Services defined in this package
2. Or run docker compose in production build
- [frontend](./frontend) Wallet frontend
- [backend](./backend) GraphQL & Business logic backend
- [mariadb](./mariadb) Database backend
- [login_server](./login_server) User credential storage & business logic backend
- [community_server](./community_server/) Business logic backend
```bash
docker-compose -f docker-compose.yml up
```
We are currently restructuring the service to reduce dependencies and unify business logic into one place. Furthermore the databases defined for each service will be unified into one.
### Open the wallet
Once you have `docker-compose` up and running, you can open [http://localhost/vue](http://localhost/vue) and create yourself a new wallet account.
## Troubleshooting
| Problem | Issue | Solution | Description |
| ------- | ----- | -------- | ----------- |
| docker-compose raises database connection errors | [#1062](https://github.com/gradido/gradido/issues/1062) | End `ctrl+c` and restart the `docker-compose up` after a successful build | Several Database connection related errors occur in the docker-compose log. |
| Wallet page is empty | [#1063](https://github.com/gradido/gradido/issues/1063) | Accept Cookies and Local Storage in your Browser | The page stays empty when navigating to [http://localhost/vue](http://localhost/vue) |
## Useful Links

3
admin/.dockerignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
.git
.gitignore

4
admin/.eslintignore Normal file
View File

@ -0,0 +1,4 @@
node_modules
coverage
**/*.min.js
dist

26
admin/.eslintrc.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
jest: true,
},
parserOptions: {
parser: 'babel-eslint',
},
extends: ['standard', 'plugin:vue/essential', 'plugin:prettier/recommended'],
// required to lint *.vue files
plugins: ['vue', 'prettier', 'jest'],
// add your custom rules here
rules: {
'no-console': ['error'],
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/component-name-in-template-casing': ['error', 'kebab-case'],
'prettier/prettier': [
'error',
{
htmlWhitespaceSensitivity: 'ignore',
},
],
},
}

15
admin/.gitattributes vendored Normal file
View File

@ -0,0 +1,15 @@
# Auto detect text files and perform LF normalization
*.scss linguist-language=Vue
*.css linguist-language=Vue
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

11
admin/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
node_modules/
dist/
.cache/
.env
# coverage folder
coverage/
# emacs
*~

8
admin/.prettierrc.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
semi: false,
printWidth: 100,
singleQuote: true,
trailingComma: "all",
tabWidth: 2,
bracketSpacing: true
};

98
admin/Dockerfile Normal file
View File

@ -0,0 +1,98 @@
##################################################################################
# BASE ###########################################################################
##################################################################################
FROM node:14.17.0-alpine3.10 as base
# ENVs (available in production aswell, can be overwritten by commandline or env file)
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
ENV DOCKER_WORKDIR="/app"
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
ENV BUILD_VERSION="0.0.0.0"
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
ENV BUILD_COMMIT="0000000"
## SET NODE_ENV
ENV NODE_ENV="production"
## App relevant Envs
ENV PORT="8080"
# Labels
LABEL org.label-schema.build-date="${BUILD_DATE}"
LABEL org.label-schema.name="gradido:admin"
LABEL org.label-schema.description="Gradido Vue Admin Interface"
LABEL org.label-schema.usage="https://github.com/gradido/gradido/admin/README.md"
LABEL org.label-schema.url="https://gradido.net"
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/backend"
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
LABEL org.label-schema.vendor="gradido Community"
LABEL org.label-schema.version="${BUILD_VERSION}"
LABEL org.label-schema.schema-version="1.0"
LABEL maintainer="support@ogradido.net"
# Install Additional Software
## install: git
#RUN apk --no-cache add git
# Settings
## Expose Container Port
EXPOSE ${PORT}
## Workdir
RUN mkdir -p ${DOCKER_WORKDIR}
WORKDIR ${DOCKER_WORKDIR}
##################################################################################
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
##################################################################################
FROM base as development
# We don't need to copy or build anything since we gonna bind to the
# local filesystem which will need a rebuild anyway
# Run command
# (for development we need to execute yarn install since the
# node_modules are on another volume and need updating)
CMD /bin/sh -c "yarn install && yarn run dev"
##################################################################################
# BUILD (Does contain all files and is therefore bloated) ########################
##################################################################################
FROM base as build
# Copy everything
COPY . .
# yarn install
RUN yarn install --production=false --frozen-lockfile --non-interactive
# yarn build
RUN yarn run build
##################################################################################
# TEST ###########################################################################
##################################################################################
FROM build as test
# Install Additional Software
RUN apk add --no-cache bash jq
# Run command
CMD /bin/sh -c "yarn run dev"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
##################################################################################
FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/dist ./dist
# We also copy the node_modules express and serve-static for the run script
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
# Copy static files
COPY --from=build ${DOCKER_WORKDIR}/public ./public
# Copy package.json for script definitions (lock file should not be needed)
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
# Copy run scripts run/
COPY --from=build ${DOCKER_WORKDIR}/run ./run
# Run command
CMD /bin/sh -c "yarn run start"

26
admin/README.md Normal file
View File

@ -0,0 +1,26 @@
# admin
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Unit tests
```
yarn test
```

4
admin/babel.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
presets: ['@babel/preset-env'],
plugins: ['transform-require-context'],
}

25
admin/jest.config.js Normal file
View File

@ -0,0 +1,25 @@
module.exports = {
verbose: true,
collectCoverageFrom: ['src/**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'],
moduleFileExtensions: [
'js',
// 'jsx',
'json',
'vue',
],
// coverageReporters: ['lcov', 'text'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less)$': 'identity-obj-proxy',
},
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.(js|jsx)?$': 'babel-jest',
'<rootDir>/node_modules/vee-validate/dist/rules': 'babel-jest',
},
setupFiles: ['<rootDir>/test/testSetup.js'],
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
// snapshotSerializers: ['jest-serializer-vue'],
transformIgnorePatterns: ['<rootDir>/node_modules/(?!vee-validate/dist/rules)'],
testEnvironment: 'jest-environment-jsdom-sixteen',
}

70
admin/package.json Normal file
View File

@ -0,0 +1,70 @@
{
"name": "admin",
"description": "Administraion Interface for Gradido",
"main": "index.js",
"author": "Moriz Wahl",
"version": "0.1.0",
"license": "MIT",
"private": false,
"scripts": {
"start": "node run/server.js",
"serve": "vue-cli-service serve --open",
"dev": "yarn run serve",
"build": "vue-cli-service build",
"lint": "eslint --ext .js,.vue .",
"test": "jest --coverage"
},
"dependencies": {
"@babel/core": "^7.15.8",
"@babel/node": "^7.15.8",
"@babel/preset-env": "^7.15.8",
"@vue/cli-plugin-unit-jest": "^4.5.14",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/test-utils": "^1.2.2",
"apollo-boost": "^0.4.9",
"babel-core": "7.0.0-bridge.0",
"babel-jest": "^27.3.1",
"babel-plugin-component": "^1.1.1",
"babel-preset-env": "^1.7.0",
"babel-preset-vue": "^2.0.2",
"bootstrap": "^5.1.3",
"bootstrap-vue": "^2.21.2",
"core-js": "^3.6.5",
"dotenv-webpack": "^7.0.3",
"graphql": "^15.6.1",
"jest": "26.6.3",
"regenerator-runtime": "^0.13.9",
"stats-webpack-plugin": "^0.7.0",
"vue": "^2.6.11",
"vue-apollo": "^3.0.8",
"vue-i18n": "^8.26.5",
"vue-jest": "^3.0.7",
"vue-router": "^3.5.3",
"vuex": "^3.6.2"
},
"devDependencies": {
"@babel/eslint-parser": "^7.15.8",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"babel-plugin-transform-require-context": "^0.1.1",
"eslint": "7.25.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-standard": "^16.0.3",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^25.2.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-vue": "^7.20.0",
"jest-environment-jsdom-sixteen": "^2.0.0",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
]
}

BIN
admin/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

22
admin/public/index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="icon" type="image/png" sizes="96x96" href="<%= webpackConfig.output.publicPath %>favicon.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Gradido Admin Interface</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>
<body>
<div class="wrapper" id="app">
</div>
<!-- built files will be auto injected -->
</body>
</html>

15
admin/run/server.js Normal file
View File

@ -0,0 +1,15 @@
// Imports
const express = require('express')
const serveStatic = require('serve-static')
// Port
const port = process.env.PORT || 8080
// Express Server
const app = express()
// eslint-disable-next-line node/no-path-concat
app.use(serveStatic(__dirname + '/../dist'))
app.listen(port)
// eslint-disable-next-line no-console
console.log(`http://admin:${port} server started.`)

68
admin/src/App.spec.js Normal file
View File

@ -0,0 +1,68 @@
import { mount } from '@vue/test-utils'
import App from './App'
const localVue = global.localVue
const storeCommitMock = jest.fn()
const mocks = {
$store: {
commit: storeCommitMock,
},
}
const localStorageMock = (() => {
let store = {}
return {
getItem: (key) => {
return store[key] || null
},
setItem: (key, value) => {
store[key] = value.toString()
},
removeItem: (key) => {
delete store[key]
},
clear: () => {
store = {}
},
}
})()
describe('App', () => {
let wrapper
const Wrapper = () => {
return mount(App, { localVue, mocks })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('has a div with id "app"', () => {
expect(wrapper.find('div#app').exists()).toBeTruthy()
})
})
describe('window localStorage is undefined', () => {
it('does not commit a token to the store', () => {
expect(storeCommitMock).not.toBeCalled()
})
})
describe('with token in local storage', () => {
beforeEach(() => {
Object.defineProperty(window, 'localStorage', {
value: localStorageMock,
})
window.localStorage.setItem('vuex', JSON.stringify({ token: 1234 }))
})
it.skip('commits the token to the store', () => {
expect(storeCommitMock).toBeCalledWith('token', 1234)
})
})
})

9
admin/src/App.vue Normal file
View File

@ -0,0 +1,9 @@
<template>
<div id="app"></div>
</template>
<script>
export default {
name: 'App',
}
</script>

View File

@ -0,0 +1,22 @@
import { mount } from '@vue/test-utils'
import NotFoundPage from './NotFoundPage'
const localVue = global.localVue
describe('NotFoundPage', () => {
let wrapper
const Wrapper = () => {
return mount(NotFoundPage, { localVue })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('has a svg', () => {
expect(wrapper.find('svg').exists()).toBeTruthy()
})
})
})

File diff suppressed because it is too large Load Diff

33
admin/src/config/index.js Normal file
View File

@ -0,0 +1,33 @@
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env).
// The whole contents is exposed to the client
// Load Package Details for some default values
const pkg = require('../../package')
const version = {
APP_VERSION: pkg.version,
BUILD_COMMIT: process.env.BUILD_COMMIT || null,
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').substr(0, 7),
}
const environment = {
NODE_ENV: process.env.NODE_ENV,
DEBUG: process.env.NODE_ENV !== 'production' || false,
PRODUCTION: process.env.NODE_ENV === 'production' || false,
}
const server = {
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql',
}
const options = {}
const CONFIG = {
...version,
...environment,
...server,
...options,
}
export default CONFIG

89
admin/src/i18n.js Normal file
View File

@ -0,0 +1,89 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const loadLocaleMessages = () => {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key)
}
})
return messages
}
const numberFormats = {
en: {
decimal: {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
},
ungroupedDecimal: {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: false,
},
},
de: {
decimal: {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
},
ungroupedDecimal: {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: false,
},
},
}
const dateTimeFormats = {
en: {
short: {
year: 'numeric',
month: 'numeric',
day: 'numeric',
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
},
},
de: {
short: {
day: 'numeric',
month: 'numeric',
year: 'numeric',
},
long: {
day: 'numeric',
month: 'short',
year: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
},
},
}
const i18n = new VueI18n({
locale: 'en',
fallbackLocale: 'en',
messages: loadLocaleMessages(),
numberFormats,
dateTimeFormats,
})
export default i18n

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

63
admin/src/main.js Normal file
View File

@ -0,0 +1,63 @@
import Vue from 'vue'
import App from './App.vue'
// without this async calls are not working
import 'regenerator-runtime'
import store from './store/store'
import router from './router/router'
import addNavigationGuards from './router/guards'
import i18n from './i18n'
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import VueApollo from 'vue-apollo'
import CONFIG from './config'
import { BootstrapVue } from 'bootstrap-vue'
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
const authLink = new ApolloLink((operation, forward) => {
const token = store.state.token
operation.setContext({
headers: {
Authorization: token && token.length > 0 ? `Bearer ${token}` : '',
},
})
return forward(operation)
/* .map((response) => {
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
response.errors[0].message = i18n.t('error.session-expired')
store.dispatch('logout', null)
if (router.currentRoute.path !== '/login') router.push('/login')
return response
}
const newToken = operation.getContext().response.headers.get('token')
if (newToken) store.commit('token', newToken)
return response
}) */
})
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
Vue.use(BootstrapVue)
addNavigationGuards(router, store)
new Vue({
router,
store,
i18n,
apolloProvider,
render: (h) => h(App),
}).$mount('#app')

56
admin/src/main.test.js Normal file
View File

@ -0,0 +1,56 @@
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import './main'
import CONFIG from './config'
import Vue from 'vue'
import Vuex from 'vuex'
import VueI18n from 'vue-i18n'
jest.mock('vue')
jest.mock('vuex')
jest.mock('vue-i18n')
const storeMock = jest.fn()
Vuex.Store = storeMock
jest.mock('apollo-boost', () => {
return {
__esModule: true,
ApolloClient: jest.fn(),
ApolloLink: jest.fn(() => {
return { concat: jest.fn() }
}),
InMemoryCache: jest.fn(),
HttpLink: jest.fn(),
}
})
describe('main', () => {
it('calls the HttpLink', () => {
expect(HttpLink).toBeCalledWith({ uri: CONFIG.GRAPHQL_URI })
})
it('calls the ApolloLink', () => {
expect(ApolloLink).toBeCalled()
})
it('calls the ApolloClient', () => {
expect(ApolloClient).toBeCalled()
})
it('calls the InMemoryCache', () => {
expect(InMemoryCache).toBeCalled()
})
it('calls Vue', () => {
expect(Vue).toBeCalled()
})
it('calls VueI18n', () => {
expect(VueI18n).toBeCalled()
})
it.skip('creates a store', () => {
expect(storeMock).toBeCalled()
})
})

View File

@ -0,0 +1,12 @@
const addNavigationGuards = (router, store) => {
router.beforeEach((to, from, next) => {
// handle authentication
if (to.meta.requiresAuth && !store.state.token) {
next({ path: '/not-found' })
} else {
next()
}
})
}
export default addNavigationGuards

View File

@ -0,0 +1,23 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
Vue.use(VueRouter)
const router = new VueRouter({
base: '/admin',
routes,
linkActiveClass: 'active',
mode: 'history',
scrollBehavior: (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
return { x: 0, y: 0 }
},
})
export default router

View File

@ -0,0 +1,15 @@
import NotFound from '@/components/NotFoundPage.vue'
const routes = [
{
path: '/',
/*
meta: {
requiresAuth: true,
},
*/
},
{ path: '*', component: NotFound },
]
export default routes

19
admin/src/store/store.js Normal file
View File

@ -0,0 +1,19 @@
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export const mutations = {
token: (state, token) => {
state.token = token
},
}
const store = new Vuex.Store({
mutations,
state: {
token: 'some-token',
},
})
export default store

View File

@ -0,0 +1,15 @@
import { mutations } from './store'
const { token } = mutations
describe('Vuex store', () => {
describe('mutations', () => {
describe('token', () => {
it('sets the state of token', () => {
const state = { token: null }
token(state, '1234')
expect(state.token).toEqual('1234')
})
})
})
})

15
admin/test/testSetup.js Normal file
View File

@ -0,0 +1,15 @@
import { createLocalVue } from '@vue/test-utils'
import Vue from 'vue'
import { BootstrapVue } from 'bootstrap-vue'
// without this async calls are not working
import 'regenerator-runtime'
global.localVue = createLocalVue()
global.localVue.use(BootstrapVue)
// throw errors for vue warnings to force the programmers to take care about warnings
Vue.config.warnHandler = (w) => {
throw new Error(w)
}

51
admin/vue.config.js Normal file
View File

@ -0,0 +1,51 @@
const path = require('path')
const webpack = require('webpack')
const Dotenv = require('dotenv-webpack')
const StatsPlugin = require('stats-webpack-plugin')
// vue.config.js
module.exports = {
devServer: {
port: process.env.PORT || 8080,
},
pluginOptions: {
i18n: {
locale: 'de',
fallbackLocale: 'de',
localeDir: 'locales',
enableInSFC: false,
},
},
lintOnSave: true,
publicPath: '/admin',
configureWebpack: {
// Set up all the aliases we use in our app.
resolve: {
alias: {
assets: path.join(__dirname, 'src/assets'),
},
},
plugins: [
// .env and Environment Variables
new Dotenv(),
new webpack.DefinePlugin({
// Those are Environment Variables transmitted via Docker and are only available when defined here aswell
// 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR),
// 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE),
// 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION),
'process.env.BUILD_COMMIT': JSON.stringify(process.env.BUILD_COMMIT),
// 'process.env.PORT': JSON.stringify(process.env.PORT),
}),
// generate webpack stats to allow analysis of the bundlesize
new StatsPlugin('webpack.stats.json'),
],
infrastructureLogging: {
level: 'warn', // 'none' | 'error' | 'warn' | 'info' | 'log' | 'verbose'
},
},
css: {
// Enable CSS source maps.
sourceMap: process.env.NODE_ENV !== 'production',
},
outputDir: path.resolve(__dirname, './dist'),
}

13027
admin/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,16 @@ DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_DATABASE=gradido_community
#EMAIL=true
#EMAIL_USERNAME=
#EMAIL_SENDER=
#EMAIL_PASSWORD=
#EMAIL_SMTP_URL=
#EMAIL_SMTP_PORT=587
#EMAIL_LINK_VERIFICATION=http://localhost/vue/checkEmail/$1
#KLICKTIPP_USER=
#KLICKTIPP_PASSWORD=
#KLICKTIPP_APIKEY_DE=
@ -18,4 +28,6 @@ DB_DATABASE=gradido_community
COMMUNITY_NAME=
COMMUNITY_URL=
COMMUNITY_REGISTER_URL=
COMMUNITY_DESCRIPTION=
COMMUNITY_DESCRIPTION=
LOGIN_APP_SECRET=21ffbbc616fe
LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a

View File

@ -1,505 +0,0 @@
### Changelog
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [1.5.1](https://github.com/gradido/gradido/compare/1.5.0...1.5.1)
- fix isExitInDb [`4c732b2`](https://github.com/gradido/gradido/commit/4c732b20e0c5591bde6b8c41b7c0f7adcc9b82e8)
- fix [`80228ef`](https://github.com/gradido/gradido/commit/80228ef842d4087ea4b80934b15b8112611e3e33)
#### [1.5.0](https://github.com/gradido/gradido/compare/1.4.0...1.5.0)
> 15 October 2021
- v1.5.0 [`#986`](https://github.com/gradido/gradido/pull/986)
- default value for empty field [`#993`](https://github.com/gradido/gradido/pull/993)
- fix language registriert [`#992`](https://github.com/gradido/gradido/pull/992)
- Change the publisherId to nullable. [`#991`](https://github.com/gradido/gradido/pull/991)
- Revert "deliver a decay block every time" [`#990`](https://github.com/gradido/gradido/pull/990)
- update first name, last name and username in community server db on c… [`#989`](https://github.com/gradido/gradido/pull/989)
- deliver a decay block every time [`#988`](https://github.com/gradido/gradido/pull/988)
- refactor: Activate Registration [`#978`](https://github.com/gradido/gradido/pull/978)
- fix: summe-gdd-schoepfun [`#980`](https://github.com/gradido/gradido/pull/980)
- fix: Use TransactionId as Collapse Identifier [`#985`](https://github.com/gradido/gradido/pull/985)
- icon in one row - fix [`#979`](https://github.com/gradido/gradido/pull/979)
- error rounding in transactions list fixed [`#974`](https://github.com/gradido/gradido/pull/974)
- fix publisherId config [`#973`](https://github.com/gradido/gradido/pull/973)
- refactor: In Production only Show one Community [`#968`](https://github.com/gradido/gradido/pull/968)
- 804 elopage registration [`#959`](https://github.com/gradido/gradido/pull/959)
- Add autocorrect keys [`#960`](https://github.com/gradido/gradido/pull/960)
- check for elopage account [`#949`](https://github.com/gradido/gradido/pull/949)
- check pid on gdt server [`#958`](https://github.com/gradido/gradido/pull/958)
- add check for empty user list [`#957`](https://github.com/gradido/gradido/pull/957)
- feat: Improve Test of Navigation Guard of Vue Router [`#950`](https://github.com/gradido/gradido/pull/950)
- refactor: Clean Up Frontend Links, Tests and Logic [`#952`](https://github.com/gradido/gradido/pull/952)
- feat: Global Catch of Publisher ID to Store [`#943`](https://github.com/gradido/gradido/pull/943)
- Add User on createUser to community server db [`#915`](https://github.com/gradido/gradido/pull/915)
- Frontend tests & coinanimation fixes [`#936`](https://github.com/gradido/gradido/pull/936)
- 803 community selection page of registration process 2 [`#918`](https://github.com/gradido/gradido/pull/918)
- feat: Test Vue Router [`#940`](https://github.com/gradido/gradido/pull/940)
- Frontend style documentation wallet-app [`#938`](https://github.com/gradido/gradido/pull/938)
- Clean graphql structure [`#935`](https://github.com/gradido/gradido/pull/935)
- silence webpack, it was very noisy [`#934`](https://github.com/gradido/gradido/pull/934)
- Community test database fix [`#932`](https://github.com/gradido/gradido/pull/932)
- 585 coin animation on and off switchable [`#813`](https://github.com/gradido/gradido/pull/813)
- Bug fixes recent changes [`#931`](https://github.com/gradido/gradido/pull/931)
- fix migrations to use only the entity model [`#928`](https://github.com/gradido/gradido/pull/928)
- Refactor graphql [`#929`](https://github.com/gradido/gradido/pull/929)
- Clean models2 [`#927`](https://github.com/gradido/gradido/pull/927)
- Docu graphics mocks [`#930`](https://github.com/gradido/gradido/pull/930)
- feat: Setup Unit Tests for Backend [`#920`](https://github.com/gradido/gradido/pull/920)
- refactor: Provide pubKey in Resolver Context to Avoid API Calls [`#906`](https://github.com/gradido/gradido/pull/906)
- fix: Double Redirect After Logout with Expired JWT [`#914`](https://github.com/gradido/gradido/pull/914)
- fix: Whitespace in Password Shows Validation Error [`#907`](https://github.com/gradido/gradido/pull/907)
- feat: Test Change Language in User Profile [`#904`](https://github.com/gradido/gradido/pull/904)
- Apollo list transactions [`#893`](https://github.com/gradido/gradido/pull/893)
- Remove api endpoints from frontend config [`#912`](https://github.com/gradido/gradido/pull/912)
- register process simplified [`#908`](https://github.com/gradido/gradido/pull/908)
- Cleaned up locales for settings and adjusted texts in files [`#905`](https://github.com/gradido/gradido/pull/905)
- fix: Warnings in Gdd Transaction List Spec [`#903`](https://github.com/gradido/gradido/pull/903)
- docu release plan [`#868`](https://github.com/gradido/gradido/pull/868)
- update password check, allow every special char [`#890`](https://github.com/gradido/gradido/pull/890)
- fix error with apollo, typeorm and build [`#897`](https://github.com/gradido/gradido/pull/897)
- Db check with typeorm [`#894`](https://github.com/gradido/gradido/pull/894)
- fix bug col to cols on &lt;b-col&gt; [`#895`](https://github.com/gradido/gradido/pull/895)
- bug fix givenType 2,3,5,6 change to givenType 1 [`#896`](https://github.com/gradido/gradido/pull/896)
- Apollo list gdt transactions [`#891`](https://github.com/gradido/gradido/pull/891)
- 826 different design of gdd and gdt lists [`#886`](https://github.com/gradido/gradido/pull/886)
- decay calculation is calculated [`#815`](https://github.com/gradido/gradido/pull/815)
- round corners removed from profile settings page [`#887`](https://github.com/gradido/gradido/pull/887)
- Implement getBalance in Apollo [`#831`](https://github.com/gradido/gradido/pull/831)
- feat: Redirect to Login when JWT has expired [`#889`](https://github.com/gradido/gradido/pull/889)
- refactor: Queries to Mutations [`#863`](https://github.com/gradido/gradido/pull/863)
- Community-Server Test with new db migration [`#881`](https://github.com/gradido/gradido/pull/881)
- fix database not reading config [`#871`](https://github.com/gradido/gradido/pull/871)
- 733 language switch toast success [`#880`](https://github.com/gradido/gradido/pull/880)
- UserProfil Language switch has an error & UserProfil Language switch don't show right language text [`#879`](https://github.com/gradido/gradido/pull/879)
- docker skeema: mount sql files folder dynamic [`#864`](https://github.com/gradido/gradido/pull/864)
- disable auto-resend of not finished transactions on login-server restart [`#865`](https://github.com/gradido/gradido/pull/865)
- add commit hash with link to github into frontend [`#794`](https://github.com/gradido/gradido/pull/794)
- mock_choose_community [`#844`](https://github.com/gradido/gradido/pull/844)
- Remove discord link for issue templates [`#845`](https://github.com/gradido/gradido/pull/845)
- update docker files [`#830`](https://github.com/gradido/gradido/pull/830)
- added publisher_id field to user [`#245`](https://github.com/gradido/gradido/pull/245)
- webpack update [`#811`](https://github.com/gradido/gradido/pull/811)
- resolvers [`562ad9a`](https://github.com/gradido/gradido/commit/562ad9ae31d97f90a371452bed1ffe10ebf2d3a5)
- deleted inputs (now args) [`8ab542a`](https://github.com/gradido/gradido/commit/8ab542a28acf6b78d7a9e7fe7757363d225f7b4f)
- fix UserCard_CoinAnimation to properly use the store, have 100% coverage and other minor fixes & simplifications [`ce826de`](https://github.com/gradido/gradido/commit/ce826deb1d6d92caba514713539dca2da3f74de7)
#### [1.4.0](https://github.com/gradido/gradido/compare/1.3.1...1.4.0)
> 16 September 2021
- feat: Release 1.4.0 [`#814`](https://github.com/gradido/gradido/pull/814)
- Integration of the KlicktippAPI to the User management [`#753`](https://github.com/gradido/gradido/pull/753)
- feat: Locale Management [`#809`](https://github.com/gradido/gradido/pull/809)
- feat: Increase Coverage Test Frontend [`#812`](https://github.com/gradido/gradido/pull/812)
- My thoughts to jwt [`#800`](https://github.com/gradido/gradido/pull/800)
- Remove discord link [`#808`](https://github.com/gradido/gradido/pull/808)
- refactor: Pagination Buttons [`#806`](https://github.com/gradido/gradido/pull/806)
- add new components selectLanguage and Usercard_Language [`#798`](https://github.com/gradido/gradido/pull/798)
- gdt transaction with arrays and without slots [`#793`](https://github.com/gradido/gradido/pull/793)
- feat: New JWT in Every Authenticated Response [`#797`](https://github.com/gradido/gradido/pull/797)
- fix euro after comma gdt view [`#799`](https://github.com/gradido/gradido/pull/799)
- different path for checkEmail and resetPassword [`#796`](https://github.com/gradido/gradido/pull/796)
- fix old frontend wrong display of event gdt [`#795`](https://github.com/gradido/gradido/pull/795)
- update scripts and doc for login-server setup without docker [`#783`](https://github.com/gradido/gradido/pull/783)
- fix: Flaky Test for Logout [`#792`](https://github.com/gradido/gradido/pull/792)
- Analyse Iota Colored Coins [`#779`](https://github.com/gradido/gradido/pull/779)
- Fix Bug in displaying GDT in Old frontend [`#788`](https://github.com/gradido/gradido/pull/788)
- feat: JSON Web Token for Authentification [`#777`](https://github.com/gradido/gradido/pull/777)
- select language during registration [`#778`](https://github.com/gradido/gradido/pull/778)
- Fix missing gdt id [`#782`](https://github.com/gradido/gradido/pull/782)
- Remove Migrations from community server [`#776`](https://github.com/gradido/gradido/pull/776)
- fix_database_migrations [`#775`](https://github.com/gradido/gradido/pull/775)
- decay with the value 0 is no longer displayed [`#773`](https://github.com/gradido/gradido/pull/773)
- database_migrations [`#770`](https://github.com/gradido/gradido/pull/770)
- backend_version [`#756`](https://github.com/gradido/gradido/pull/756)
- issue_type_release [`#769`](https://github.com/gradido/gradido/pull/769)
- some_docu [`#771`](https://github.com/gradido/gradido/pull/771)
- database_reachable_in_testmode [`#768`](https://github.com/gradido/gradido/pull/768)
- logo changed, old logos and icons deleted [`#734`](https://github.com/gradido/gradido/pull/734)
- change default value of communty url [`#755`](https://github.com/gradido/gradido/pull/755)
- feat: Testing Tabs of TransactionLists [`#737`](https://github.com/gradido/gradido/pull/737)
- Added a fetchPolicy: 'no-cache' to the login vall. [`#736`](https://github.com/gradido/gradido/pull/736)
- Docu fragen community [`#731`](https://github.com/gradido/gradido/pull/731)
- feat: Vue Apollo Client [`#701`](https://github.com/gradido/gradido/pull/701)
- change text from Geld to Gradidos [`#711`](https://github.com/gradido/gradido/pull/711)
- fix fix [`#728`](https://github.com/gradido/gradido/pull/728)
- sort locales [`ec12a28`](https://github.com/gradido/gradido/commit/ec12a28f81577d530f58b42b7f8c2c7d20dffd64)
- feat: Unify and Sort Locales [`aba4f4d`](https://github.com/gradido/gradido/commit/aba4f4d20e0a13016e3528a1c5c30c111eb3a9f1)
- feat: Increase Coverage [`3c061bc`](https://github.com/gradido/gradido/commit/3c061bcb8d1a3a47442ed6a351e1428e15b314aa)
#### [1.3.1](https://github.com/gradido/gradido/compare/1.3.0...1.3.1)
> 12 August 2021
- fix: Translations and Formula Display [`#727`](https://github.com/gradido/gradido/pull/727)
- 612 docu structure [`#688`](https://github.com/gradido/gradido/pull/688)
- Community update for gdt list GDT transaction format [`#726`](https://github.com/gradido/gradido/pull/726)
- [#612] new directory structure in /docu/Concepts [`10bf3b0`](https://github.com/gradido/gradido/commit/10bf3b0cdfa6c44f879be0155e93f636601a051b)
- #612 additional documents [`ac0ed4f`](https://github.com/gradido/gradido/commit/ac0ed4fee81caff26d09b5de47dd130f12abdb45)
- #612 docu restructuring [`e67e1c4`](https://github.com/gradido/gradido/commit/e67e1c41e78264698e6fae4cf1d29751de7e7b29)
#### [1.3.0](https://github.com/gradido/gradido/compare/1.2.1...1.3.0)
> 12 August 2021
- Update README.md [`#719`](https://github.com/gradido/gradido/pull/719)
- fixed bugs, step 1 [`#718`](https://github.com/gradido/gradido/pull/718)
- update code for correct transactions listening [`#725`](https://github.com/gradido/gradido/pull/725)
- bugfix: change to new frontend passwort reset [`#724`](https://github.com/gradido/gradido/pull/724)
- feat: Add Pagination Buttons to GDT Transaction List [`#716`](https://github.com/gradido/gradido/pull/716)
- fix bug, change statment from transaction infbox [`#712`](https://github.com/gradido/gradido/pull/712)
- 650-display of the gdt transactions [`#687`](https://github.com/gradido/gradido/pull/687)
- Login-Server bug fix, allow names with 2 characters also [`#714`](https://github.com/gradido/gradido/pull/714)
- Apollo sendCoins via Login-Server [`#699`](https://github.com/gradido/gradido/pull/699)
- increase coverage to 34% [`#700`](https://github.com/gradido/gradido/pull/700)
- mobile friendly, add text, change text [`#683`](https://github.com/gradido/gradido/pull/683)
- Css color input error [`#646`](https://github.com/gradido/gradido/pull/646)
- Apollo gdt [`#686`](https://github.com/gradido/gradido/pull/686)
- Backend Setup [`#584`](https://github.com/gradido/gradido/pull/584)
- text-size in textarea and font-variante if focus [`#677`](https://github.com/gradido/gradido/pull/677)
- 680 app large maximum width [`#681`](https://github.com/gradido/gradido/pull/681)
- linting, server is working [`34b30b2`](https://github.com/gradido/gradido/commit/34b30b216b6fafcb5b686d4b023b05f2e9766bdf)
- server stack seems to work. Graphql does not load properly yet [`43f7cf8`](https://github.com/gradido/gradido/commit/43f7cf87679713d436a64d569d6af1594a12ee33)
- initial commit, base packages [`fdf0979`](https://github.com/gradido/gradido/commit/fdf0979830fece04208a6b3bb06bb5323a3c149b)
#### [1.2.1](https://github.com/gradido/gradido/compare/1.2.0...1.2.1)
> 22 July 2021
- release 1.2.1 [`#670`](https://github.com/gradido/gradido/pull/670)
- exchange positions of decay in transaction in old frontend [`#662`](https://github.com/gradido/gradido/pull/662)
- fix unneccessary migration run on fresh (docker) setup [`#654`](https://github.com/gradido/gradido/pull/654)
- move back decay as standalone transaction in old frontend [`#656`](https://github.com/gradido/gradido/pull/656)
- fix display error with creation [`#652`](https://github.com/gradido/gradido/pull/652)
- release [`a0b8056`](https://github.com/gradido/gradido/commit/a0b8056c17b22570a1b1dbb6fa6ce71e561b04af)
- update content for frontend [`d37ce09`](https://github.com/gradido/gradido/commit/d37ce0949ef97d2a6c6ffaf0be31db9f6d92e743)
- exchange positions [`bc000ef`](https://github.com/gradido/gradido/commit/bc000efd87c9701480c4aeaa7b819ab49bfe8f01)
#### [1.2.0](https://github.com/gradido/gradido/compare/1.1.1...1.2.0)
> 20 July 2021
- feat: Release v1.2.0 [`#648`](https://github.com/gradido/gradido/pull/648)
- 630 frontend text improvemets [`#645`](https://github.com/gradido/gradido/pull/645)
- #435 Visual Feedback on not sending GDD [`#647`](https://github.com/gradido/gradido/pull/647)
- Make E-Mail Text 100% German [`#643`](https://github.com/gradido/gradido/pull/643)
- Login fix crash [`#641`](https://github.com/gradido/gradido/pull/641)
- check for null date [`#640`](https://github.com/gradido/gradido/pull/640)
- Login fix wrong check email link [`#642`](https://github.com/gradido/gradido/pull/642)
- Change timeout for email resend to 10 Minutes [`#635`](https://github.com/gradido/gradido/pull/635)
- fix last Transaction are not shown in new frontend [`#633`](https://github.com/gradido/gradido/pull/633)
- fix bug with user roles and pending transactions [`#627`](https://github.com/gradido/gradido/pull/627)
- fix type not found error in listTransaction Call [`#628`](https://github.com/gradido/gradido/pull/628)
- refactor: Use Vue-Toasted to Toast Messages [`#619`](https://github.com/gradido/gradido/pull/619)
- feat: Test UserProfileTransactionList [`#624`](https://github.com/gradido/gradido/pull/624)
- fix: QRCode Error after Logout [`#623`](https://github.com/gradido/gradido/pull/623)
- fix: Fix Infinite Loop in Transaction List [`#622`](https://github.com/gradido/gradido/pull/622)
- feat: Transaction Details [`#607`](https://github.com/gradido/gradido/pull/607)
- fix: Test for ScrollTo in Transaction List [`#621`](https://github.com/gradido/gradido/pull/621)
- Allow empty description [`#618`](https://github.com/gradido/gradido/pull/618)
- fix: Do Not Pass Userdata Description to Server [`#617`](https://github.com/gradido/gradido/pull/617)
- fix: Transaction Pagination Scroll Top [`#615`](https://github.com/gradido/gradido/pull/615)
- fix: Mobile Pagination [`#614`](https://github.com/gradido/gradido/pull/614)
- feat: Test AccountOverview [`#611`](https://github.com/gradido/gradido/pull/611)
- feat: Test Validation Rules [`#610`](https://github.com/gradido/gradido/pull/610)
- refactor: Reset Password Page [`#606`](https://github.com/gradido/gradido/pull/606)
- refactor: Change Password [`#603`](https://github.com/gradido/gradido/pull/603)
- refactor: Forgot Password Form [`#602`](https://github.com/gradido/gradido/pull/602)
- refactor: Update Store Tests [`#601`](https://github.com/gradido/gradido/pull/601)
- Community fix [`#598`](https://github.com/gradido/gradido/pull/598)
- refactor: Password Component [`#564`](https://github.com/gradido/gradido/pull/564)
- Fix crashing Problem with Login-Server [`#600`](https://github.com/gradido/gradido/pull/600)
- Update Password Reset E-Mail Subject Encoding [`#579`](https://github.com/gradido/gradido/pull/579)
- move decay between transactions into the transactions [`#483`](https://github.com/gradido/gradido/pull/483)
- fix #591 [`#591`](https://github.com/gradido/gradido/issues/591)
- fix style decay startblick [`cc7778b`](https://github.com/gradido/gradido/commit/cc7778b55d1baaa7be2d9440480e0fb27bb9a930)
- Remove dynamic cast because it lead to errors again and agin (Poco::AutoPtr don't work correct with that) [`0db5912`](https://github.com/gradido/gradido/commit/0db5912a67158be8f313c01f06350f8339cb0e28)
- Remove dynamic cast because it lead to errors again and agin (Poco::AutoPtr don't work correct with that) [`cee7d7a`](https://github.com/gradido/gradido/commit/cee7d7ac3c4c8c1f481cc3a87fb15422c858413b)
#### [1.1.1](https://github.com/gradido/gradido/compare/1.1.0...1.1.1)
> 22 June 2021
- release [`#581`](https://github.com/gradido/gradido/pull/581)
- Auto deploy [`#580`](https://github.com/gradido/gradido/pull/580)
- update transfer email text [`#574`](https://github.com/gradido/gradido/pull/574)
- update mysql because tuple has changed [`#576`](https://github.com/gradido/gradido/pull/576)
- Login fix pending transactions [`#578`](https://github.com/gradido/gradido/pull/578)
- add test to prevent bug in future [`630d667`](https://github.com/gradido/gradido/commit/630d667e996870a1bf9aa9586b0467d58419e525)
- use standard path. add nginx example [`ac249b4`](https://github.com/gradido/gradido/commit/ac249b46830a8039aec52d30b48084b50a264b6f)
- add autodeploy bash scripts [`f49cf4d`](https://github.com/gradido/gradido/commit/f49cf4d7f8054d87efa1e12055a7ef0c6d3b9872)
#### [1.1.0](https://github.com/gradido/gradido/compare/1.0.2...1.1.0)
> 17 June 2021
- add first error additional as msg [`#573`](https://github.com/gradido/gradido/pull/573)
- fix: Remove Grouping for Max Error [`#572`](https://github.com/gradido/gradido/pull/572)
- fix: API Call for Reset Password [`#571`](https://github.com/gradido/gradido/pull/571)
- Login reset password [`#570`](https://github.com/gradido/gradido/pull/570)
- Hide unuse components in profil [`#566`](https://github.com/gradido/gradido/pull/566)
- fix: Thousend Dividers on GDD Send Amount Field [`#567`](https://github.com/gradido/gradido/pull/567)
- reorganisiere Dockerfiles [`#550`](https://github.com/gradido/gradido/pull/550)
- hot fixes shown by test [`#551`](https://github.com/gradido/gradido/pull/551)
- fix to big text ths [`#565`](https://github.com/gradido/gradido/pull/565)
- feat: Trim Email on Blur [`#556`](https://github.com/gradido/gradido/pull/556)
- Mobile UI send gdd form [`#562`](https://github.com/gradido/gradido/pull/562)
- fix: Change Password Form [`#561`](https://github.com/gradido/gradido/pull/561)
- change userdata button disable rules [`#548`](https://github.com/gradido/gradido/pull/548)
- Change password require old password [`#519`](https://github.com/gradido/gradido/pull/519)
- feat: Test Forget Password [`#546`](https://github.com/gradido/gradido/pull/546)
- feat: Validate Change Username [`#545`](https://github.com/gradido/gradido/pull/545)
- Remove base input example login vue [`#524`](https://github.com/gradido/gradido/pull/524)
- Feature: Change Username [`#490`](https://github.com/gradido/gradido/pull/490)
- feat: Toaster to Display Messages [`#512`](https://github.com/gradido/gradido/pull/512)
- fix: Validation of GDD Send Amount Field [`#525`](https://github.com/gradido/gradido/pull/525)
- Community coverage [`#496`](https://github.com/gradido/gradido/pull/496)
- compare with last transaction sended [`#523`](https://github.com/gradido/gradido/pull/523)
- remove check decays for being at least 100 GDD cent [`#526`](https://github.com/gradido/gradido/pull/526)
- fix: Remove Target Date in Send Coins Request [`#518`](https://github.com/gradido/gradido/pull/518)
- Feature profile page bugs [`#511`](https://github.com/gradido/gradido/pull/511)
- fix: GDD Send Amount Input Field [`#491`](https://github.com/gradido/gradido/pull/491)
- change transfer confirmation email [`#485`](https://github.com/gradido/gradido/pull/485)
- Login wait on passwords with missing chars [`#487`](https://github.com/gradido/gradido/pull/487)
- fix problem with create User [`#486`](https://github.com/gradido/gradido/pull/486)
- Feature: Profile Page + Update API [`#474`](https://github.com/gradido/gradido/pull/474)
- add new API Call checkUsername [`#482`](https://github.com/gradido/gradido/pull/482)
- feat: Pagination Buttons for Transaction List [`#473`](https://github.com/gradido/gradido/pull/473)
- Login-Server & Community-Server Coverage [`#472`](https://github.com/gradido/gradido/pull/472)
- login without hedera [`#478`](https://github.com/gradido/gradido/pull/478)
- fix: Show Correct Version Number in Footer [`#475`](https://github.com/gradido/gradido/pull/475)
- refactor: Remove Element-UI [`#476`](https://github.com/gradido/gradido/pull/476)
- remove components Charts, Notification, SearchUser, ButtonCheckbox, Button RadioGroup, Breadcrumb [`159bff7`](https://github.com/gradido/gradido/commit/159bff71df20a5c48f93389b2f990f7fe54e53b9)
- fix bug, update dockerfiles to use dependencies without grpc [`dedcebd`](https://github.com/gradido/gradido/commit/dedcebdb95ee0f3dfd2ad62074d4181af38476a2)
- add warning to able to forward warnings from community server to client [`2fc3fe9`](https://github.com/gradido/gradido/commit/2fc3fe94a09bae199bf2f34f9df90e8fc3879c2b)
#### [1.0.2](https://github.com/gradido/gradido/compare/1.0.1...1.0.2)
> 27 May 2021
- feat: Test Transaction List [`#470`](https://github.com/gradido/gradido/pull/470)
- fixed problem with finding cpsp parse binary under windows with conan [`#471`](https://github.com/gradido/gradido/pull/471)
- fix: GDD Amount is Always Displayed with Two Digits [`#468`](https://github.com/gradido/gradido/pull/468)
- fix: Date Time Formats [`#469`](https://github.com/gradido/gradido/pull/469)
- Community ipv6 localhost [`#466`](https://github.com/gradido/gradido/pull/466)
- Login html pages autoparse [`#464`](https://github.com/gradido/gradido/pull/464)
- everything I find and fix crash related in login server this week (kw 20) [`#448`](https://github.com/gradido/gradido/pull/448)
- 437 bug mobile transaction list is not easy to read [`#462`](https://github.com/gradido/gradido/pull/462)
- Require memo in send [`#455`](https://github.com/gradido/gradido/pull/455)
- bug: Thx Page Shows Content Dependent of Route From [`#459`](https://github.com/gradido/gradido/pull/459)
- bug: responsive display error on pads fixed [`#461`](https://github.com/gradido/gradido/pull/461)
- [Bug] German "Dir" is written with capital D in send validation bug [`#460`](https://github.com/gradido/gradido/pull/460)
- feat: Save Locale in Database [`#450`](https://github.com/gradido/gradido/pull/450)
- attention! notice in send area removed [`#458`](https://github.com/gradido/gradido/pull/458)
- Remove Error Message encoding [`#456`](https://github.com/gradido/gradido/pull/456)
- bug fix:mobile menu closes on logout, probs value problem on logout f… [`#454`](https://github.com/gradido/gradido/pull/454)
- bug-login password change show hide inserted [`#453`](https://github.com/gradido/gradido/pull/453)
- fix sorting and use total count [`#451`](https://github.com/gradido/gradido/pull/451)
- add dynamic error email if transaction failed [`#452`](https://github.com/gradido/gradido/pull/452)
- ceil the last decay [`#449`](https://github.com/gradido/gradido/pull/449)
- feat: Raise Coverage of Frontend Unit Tets to 18% [`#447`](https://github.com/gradido/gradido/pull/447)
- parse cpsp files automatic in build [`a4a12bb`](https://github.com/gradido/gradido/commit/a4a12bb62b4000e035ff15e17c5a5f5861653ff6)
- translate german html encoded error messages to english and use gettext for automatic translation [`d339627`](https://github.com/gradido/gradido/commit/d33962736d94c1cb7a12ff775bc2c8d7505d646e)
- 100% coverage of GddTransactionList [`96fb245`](https://github.com/gradido/gradido/commit/96fb245821c69f4d321204a663247d5eee60d92f)
#### [1.0.1](https://github.com/gradido/gradido/compare/1.0.0...1.0.1)
> 14 May 2021
- Login crash fix [`#444`](https://github.com/gradido/gradido/pull/444)
- add try catch blocks to prevent login-server from crashing [`22ff220`](https://github.com/gradido/gradido/commit/22ff22072956f8b843037c75c5b16b7ff5d6a2a3)
- fix [`14a4243`](https://github.com/gradido/gradido/commit/14a424347817b1fe6912a113bffd70e55d688112)
### [1.0.0](https://github.com/gradido/gradido/compare/0.9.4...1.0.0)
> 14 May 2021
- Login build alpine [`#423`](https://github.com/gradido/gradido/pull/423)
- Release fix [`#428`](https://github.com/gradido/gradido/pull/428)
- Send button only click [`#427`](https://github.com/gradido/gradido/pull/427)
- use new function for balance overview in old frontend, update balance… [`#422`](https://github.com/gradido/gradido/pull/422)
- thx.vue coloured background removed. design adapted to app [`#426`](https://github.com/gradido/gradido/pull/426)
- translation password rules in Register.vue and ResetPasswort.vue fixed [`#424`](https://github.com/gradido/gradido/pull/424)
- 06x style [`#419`](https://github.com/gradido/gradido/pull/419)
- feat: Dash is Shown When Balance is Loading [`#396`](https://github.com/gradido/gradido/pull/396)
- refactor: Split GddSend into Components [`#415`](https://github.com/gradido/gradido/pull/415)
- fix: Set Maximum Transactions Loaded to 1000 [`#395`](https://github.com/gradido/gradido/pull/395)
- BUG : preRELEASE : thx transaction false [`#394`](https://github.com/gradido/gradido/pull/394)
- feat: Validation of Input Fields of GddSend [`#386`](https://github.com/gradido/gradido/pull/386)
- add validation, check if user has tried to send themself gradidos [`#277`](https://github.com/gradido/gradido/pull/277)
- fix link generation [`#347`](https://github.com/gradido/gradido/pull/347)
- Community start decay [`#387`](https://github.com/gradido/gradido/pull/387)
- transaction error message, translations [`#388`](https://github.com/gradido/gradido/pull/388)
- docu: locales schöpfen-mysql befehl [`#392`](https://github.com/gradido/gradido/pull/392)
- Fix: QR Scanner Hide [`#380`](https://github.com/gradido/gradido/pull/380)
- change design- dark-blue of text-ligth [`#381`](https://github.com/gradido/gradido/pull/381)
- spelling & translation [`#385`](https://github.com/gradido/gradido/pull/385)
- 350 6 login [`#383`](https://github.com/gradido/gradido/pull/383)
- fix: breakout error [`#375`](https://github.com/gradido/gradido/pull/375)
- 233-Support-Button fix, and col error fix [`#343`](https://github.com/gradido/gradido/pull/343)
- fix: membersarea open in a new tap [`#370`](https://github.com/gradido/gradido/pull/370)
- 00x table footer componente [`#345`](https://github.com/gradido/gradido/pull/345)
- clickevent from icon to button [`#364`](https://github.com/gradido/gradido/pull/364)
- 404 link to login page fix [`#366`](https://github.com/gradido/gradido/pull/366)
- bug: session_id vs sessionId [`#365`](https://github.com/gradido/gradido/pull/365)
- eslint rule standard [`#344`](https://github.com/gradido/gradido/pull/344)
- don't show decays smaller than 0,01 GDD [`#329`](https://github.com/gradido/gradido/pull/329)
- 338 - mt-5 margin top 5 eingebunden [`#341`](https://github.com/gradido/gradido/pull/341)
- feat: Update Transactions When GddTable Is Rendered [`#339`](https://github.com/gradido/gradido/pull/339)
- my profile auskommentiert [`#337`](https://github.com/gradido/gradido/pull/337)
- give balance as float value in GDD (not longer GDD cent) [`#273`](https://github.com/gradido/gradido/pull/273)
- fix error with password reset email [`#276`](https://github.com/gradido/gradido/pull/276)
- transaction.show_all in en.json hinzugefügt [`#335`](https://github.com/gradido/gradido/pull/335)
- refactor: Add Paramerts to ListTransactions API Call [`#332`](https://github.com/gradido/gradido/pull/332)
- css abstände vom rand in mobilen style [`#272`](https://github.com/gradido/gradido/pull/272)
- error messages are displayed [`#304`](https://github.com/gradido/gradido/pull/304)
- cancel to reset, form rest event added [`#334`](https://github.com/gradido/gradido/pull/334)
- remove from error messages because its no longer exist [`#330`](https://github.com/gradido/gradido/pull/330)
- xx deleted [`#333`](https://github.com/gradido/gradido/pull/333)
- fix bug on Login group add host [`#274`](https://github.com/gradido/gradido/pull/274)
- transactionslist load and icon [`#324`](https://github.com/gradido/gradido/pull/324)
- THX changed to Thank you [`#305`](https://github.com/gradido/gradido/pull/305)
- fix_bug_template [`#284`](https://github.com/gradido/gradido/pull/284)
- feat: Restructure Overview [`#271`](https://github.com/gradido/gradido/pull/271)
- fix: Dynamic Balance in User Profile [`#278`](https://github.com/gradido/gradido/pull/278)
- refactor: Test and Clean Up Store [`#270`](https://github.com/gradido/gradido/pull/270)
- remove links to not work [`#267`](https://github.com/gradido/gradido/pull/267)
- change order of session_id [`#269`](https://github.com/gradido/gradido/pull/269)
- bug: Dynamic Balance [`#260`](https://github.com/gradido/gradido/pull/260)
- Fix problems with transactions in docker environment [`#261`](https://github.com/gradido/gradido/pull/261)
- update skeema call to allow also for unsafe db updates [`#250`](https://github.com/gradido/gradido/pull/250)
- turn registration on and off [`#266`](https://github.com/gradido/gradido/pull/266)
- 243 vue loader component - varante 2 [`#257`](https://github.com/gradido/gradido/pull/257)
- fix_issue_templates [`#262`](https://github.com/gradido/gradido/pull/262)
- fix_nginx_docker [`#253`](https://github.com/gradido/gradido/pull/253)
- 13_coverage [`#255`](https://github.com/gradido/gradido/pull/255)
- add language as fields which will be returned with user-object [`#244`](https://github.com/gradido/gradido/pull/244)
- add short description for target date in creation transaction [`#252`](https://github.com/gradido/gradido/pull/252)
- fix error "Integrity constraint violation: 1052 Column 'id' in where … [`#251`](https://github.com/gradido/gradido/pull/251)
- Clicks on button [`#246`](https://github.com/gradido/gradido/pull/246)
- Change css [`#231`](https://github.com/gradido/gradido/pull/231)
- feat: Reset Password [`#212`](https://github.com/gradido/gradido/pull/212)
- fix bug #221 [`#228`](https://github.com/gradido/gradido/pull/228)
- fix empty reply by empty group_id [`#227`](https://github.com/gradido/gradido/pull/227)
- Bugfix 22 04 21 [`#226`](https://github.com/gradido/gradido/pull/226)
- Rollback use prebuild dependencies [`#225`](https://github.com/gradido/gradido/pull/225)
- Docu [`#199`](https://github.com/gradido/gradido/pull/199)
- update login-server build [`#222`](https://github.com/gradido/gradido/pull/222)
- Login add language [`#220`](https://github.com/gradido/gradido/pull/220)
- User Object on Json Login [`#219`](https://github.com/gradido/gradido/pull/219)
- refactor: Remove loginAPI Call from Store [`#215`](https://github.com/gradido/gradido/pull/215)
- fix: Remove Rules for Password on Login [`#214`](https://github.com/gradido/gradido/pull/214)
- Community 15 04 2021 [`#198`](https://github.com/gradido/gradido/pull/198)
- refactor: Remove Right Menu [`#204`](https://github.com/gradido/gradido/pull/204)
- feat: Raise Frontend Test Coverage to 12% [`#206`](https://github.com/gradido/gradido/pull/206)
- refactor: Remove Cookies [`#202`](https://github.com/gradido/gradido/pull/202)
- fix_docker_compose [`#205`](https://github.com/gradido/gradido/pull/205)
- 62 pwd reset [`#203`](https://github.com/gradido/gradido/pull/203)
- workflows_refactor [`#200`](https://github.com/gradido/gradido/pull/200)
- feat: Link to Whitepaper in Content-Footer [`#196`](https://github.com/gradido/gradido/pull/196)
- update code which produce error which last fix (._.); [`#197`](https://github.com/gradido/gradido/pull/197)
- refactor: Redirects and Misuse of Children in Router [`#194`](https://github.com/gradido/gradido/pull/194)
- fix: Gradido-Akademie in Footer Links to gradido.net [`#170`](https://github.com/gradido/gradido/pull/170)
- fix register [`#160`](https://github.com/gradido/gradido/pull/160)
- Login 14.04.21 [`#175`](https://github.com/gradido/gradido/pull/175)
- coverage_no_report [`#176`](https://github.com/gradido/gradido/pull/176)
- Community 14.04.21 [`#174`](https://github.com/gradido/gradido/pull/174)
- feat: Add Coverage Check To GitHub Workflow [`#158`](https://github.com/gradido/gradido/pull/158)
- Stage2 [`#49`](https://github.com/gradido/gradido/pull/49)
- feat: Unit Test for KontoOverview [`#169`](https://github.com/gradido/gradido/pull/169)
- fix: Remove Hashtag From Route [`#167`](https://github.com/gradido/gradido/pull/167)
- documentation for reset password [`#166`](https://github.com/gradido/gradido/pull/166)
- 90 show release version in frontend [`#153`](https://github.com/gradido/gradido/pull/153)
- docu frontend menu page structur [`#159`](https://github.com/gradido/gradido/pull/159)
- userstorry_button.txt example [`#157`](https://github.com/gradido/gradido/pull/157)
- fix getBalance API call [`#147`](https://github.com/gradido/gradido/pull/147)
- Locale clear list merge master [`#119`](https://github.com/gradido/gradido/pull/119)
- fix-125-link-to-community [`#152`](https://github.com/gradido/gradido/pull/152)
- feat: Test Dashboard Layout [`#154`](https://github.com/gradido/gradido/pull/154)
- feat: Activate Coverage Report For Unit Tests In Frontend [`#156`](https://github.com/gradido/gradido/pull/156)
- Docu [`#120`](https://github.com/gradido/gradido/pull/120)
- Fix compose bug [`#138`](https://github.com/gradido/gradido/pull/138)
- feat: Test Login Form [`#113`](https://github.com/gradido/gradido/pull/113)
- at last, fix email-bug [`#134`](https://github.com/gradido/gradido/pull/134)
- Api password reset [`#82`](https://github.com/gradido/gradido/pull/82)
- Optimize login server build [`#101`](https://github.com/gradido/gradido/pull/101)
- markdown of login server api [`#111`](https://github.com/gradido/gradido/pull/111)
- Fix lineendings [`#112`](https://github.com/gradido/gradido/pull/112)
- Background color change [`#117`](https://github.com/gradido/gradido/pull/117)
- Delete unused files [`#116`](https://github.com/gradido/gradido/pull/116)
- store aufräumen teil 1 [`#115`](https://github.com/gradido/gradido/pull/115)
- add migrations table for automatic table data migration [`40a9a8c`](https://github.com/gradido/gradido/commit/40a9a8c2b587f5bef0fcc54136ed7bd13dd91b2b)
- update yarn.lock after running yarn install [`7f38c80`](https://github.com/gradido/gradido/commit/7f38c801213ad886e9d34a8d43b00ae423f5f2a0)
- use new function for balance overview in old frontend, update balance in session on every php-request [`97c570c`](https://github.com/gradido/gradido/commit/97c570c08cc51ed17a69eb8be8d987f95f3c2ce0)
#### [0.9.4](https://github.com/gradido/gradido/compare/0.9.3...0.9.4)
> 30 March 2021
- Vue with nginx [`#84`](https://github.com/gradido/gradido/pull/84)
- Build on run [`#103`](https://github.com/gradido/gradido/pull/103)
- update debug docker to use dependencies container pushed to docker hub [`1f002f4`](https://github.com/gradido/gradido/commit/1f002f4ed0b12d4b2bf63efceabe546d0c5b58ea)
- removed email tasks complete [`8a143be`](https://github.com/gradido/gradido/commit/8a143be8423d7bd894d4f512848895df8b9694b0)
- build login-server on docker-compose up in a docker volume so it rebuild only neccessary parts if some c++ files have changed [`0da5279`](https://github.com/gradido/gradido/commit/0da527917523530186e6effe63dc001fc99bd3e3)
#### [0.9.3](https://github.com/gradido/gradido/compare/0.9.2...0.9.3)
> 30 March 2021
- new version 0.9.3 [`#110`](https://github.com/gradido/gradido/pull/110)
- feat: Frontend tests for Content Footer [`#102`](https://github.com/gradido/gradido/pull/102)
- add check session state json function and doc [`#96`](https://github.com/gradido/gradido/pull/96)
- activity page stap 2 [`#86`](https://github.com/gradido/gradido/pull/86)
- qrcode scanner fixed, datum in activitäten fixed, sprache not null fixed [`#88`](https://github.com/gradido/gradido/pull/88)
- add additional docker container which is running skeema on startup to… [`#83`](https://github.com/gradido/gradido/pull/83)
- New fix up frontend for presentation [`#85`](https://github.com/gradido/gradido/pull/85)
- feat: Tougher eslint rules [`#77`](https://github.com/gradido/gradido/pull/77)
- feat: improve translations [`#81`](https://github.com/gradido/gradido/pull/81)
- use_correct_send_api [`#80`](https://github.com/gradido/gradido/pull/80)
- fix bug with access rights [`#78`](https://github.com/gradido/gradido/pull/78)
- make response.html better accessible [`#75`](https://github.com/gradido/gradido/pull/75)
- feat: Lokalize Validation Messages [`#67`](https://github.com/gradido/gradido/pull/67)
- feat: Localize links to gradito.net [`#74`](https://github.com/gradido/gradido/pull/74)
- fix-padding [`#73`](https://github.com/gradido/gradido/pull/73)
- Correctly use transactions API in frontend [`#69`](https://github.com/gradido/gradido/pull/69)
- Qr code scanner [`#72`](https://github.com/gradido/gradido/pull/72)
- Login fail message [`#71`](https://github.com/gradido/gradido/pull/71)
- Setup unit tests for frontend [`#55`](https://github.com/gradido/gradido/pull/55)
- Update doc [`#63`](https://github.com/gradido/gradido/pull/63)
- Add Feature in user search old frontend because Support has requested the feature long ago [`#56`](https://github.com/gradido/gradido/pull/56)
- sprache angepasst, for login, pwd, sigin [`#54`](https://github.com/gradido/gradido/pull/54)
- Improve workflows [`#53`](https://github.com/gradido/gradido/pull/53)
- setup eslint with tougher rules [`1f13507`](https://github.com/gradido/gradido/commit/1f13507eacfd93c2248fb841de5f481c9eb1e6bd)
- semicolon rule implemented [`6762a02`](https://github.com/gradido/gradido/commit/6762a028f2a3e4f2713b26bed81029defe686ad7)
- dev meeting, bernd [`a99de7f`](https://github.com/gradido/gradido/commit/a99de7f5d1f7557c0877eae565aa4263d65aaaf3)
#### [0.9.2](https://github.com/gradido/gradido/compare/0.9.1...0.9.2)
> 18 March 2021
- v0.9.2 [`#51`](https://github.com/gradido/gradido/pull/51)
- Reload after login fixed [`#50`](https://github.com/gradido/gradido/pull/50)
- Monorepo login server [`#48`](https://github.com/gradido/gradido/pull/48)
- Stage0 [`#3`](https://github.com/gradido/gradido/pull/3)
- Add auto-sign Transaction functionality [`5592275`](https://github.com/gradido/gradido/commit/55922753a7ffd9552be132501d744da491c409b5)
- read in login the real client ip X-Real-IP from nginx forwarded not from community server [`512d307`](https://github.com/gradido/gradido/commit/512d307a19b955bb6e26ae8b274def354829b50f)
- move check if all passwords allow direct into pwdValidation so it will work with every code which ask for password [`e2c38c1`](https://github.com/gradido/gradido/commit/e2c38c1a0fc25a4a2bc922c4bbc44d86b6d00d8b)
#### 0.9.1
> 13 March 2021
- Login gbb [`#12`](https://github.com/gradido/gradido/pull/12)
- Admin prework axios json [`#10`](https://github.com/gradido/gradido/pull/10)
- Merge pull request #1 from gradido/master [`#8`](https://github.com/gradido/gradido/pull/8)
- [WIP] 2 create a dockerfile for the frontend application [`#6`](https://github.com/gradido/gradido/pull/6)
- Master - first step [`#1`](https://github.com/gradido/gradido/pull/1)
- Add docker compose [`#7`](https://github.com/gradido/gradido/pull/7)
- style 404 side :) [`c7bdf89`](https://github.com/gradido/gradido/commit/c7bdf8978594b932615e48f9bb1c19d3c3bf3fcf)
- publish workflow test [`df6f66f`](https://github.com/gradido/gradido/commit/df6f66ffe70baa9ed3f70b460a6c0c14011bb944)
- many translations. translation structure [`bf68547`](https://github.com/gradido/gradido/commit/bf685479767d19c246c4d6abe3577dc3cb666346)

View File

@ -42,6 +42,8 @@ EXPOSE ${PORT}
RUN mkdir -p ${DOCKER_WORKDIR}
WORKDIR ${DOCKER_WORKDIR}
RUN mkdir -p /database
##################################################################################
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
##################################################################################
@ -51,29 +53,39 @@ FROM base as development
# local filesystem which will need a rebuild anyway
# Run command
# (for development we need to execute npm install since the
# (for development we need to execute yarn install since the
# node_modules are on another volume and need updating)
CMD /bin/sh -c "yarn install && yarn run dev"
CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev"
##################################################################################
# BUILD (Does contain all files and is therefore bloated) ########################
##################################################################################
FROM base as build
# Copy everything
COPY . .
# npm install
# Copy everything from backend
COPY ./backend/ ./
# Copy everything from database
COPY ./database/ ../database/
# yarn install backend
RUN yarn install --production=false --frozen-lockfile --non-interactive
# npm build
# yarn install database
RUN cd ../database && yarn install --production=false --frozen-lockfile --non-interactive
# yarn build
RUN yarn run build
# yarn build database
RUN cd ../database && yarn run build
##################################################################################
# TEST ###########################################################################
##################################################################################
FROM build as test
# Run command
CMD /bin/sh -c "yarn run dev"
CMD /bin/sh -c "yarn run start"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
@ -82,8 +94,10 @@ FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/build ./build
COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build
# We also copy the node_modules express and serve-static for the run script
# COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules
# Copy static files
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
# Copy package.json for script definitions (lock file should not be needed)

View File

@ -4,4 +4,7 @@ module.exports = {
preset: 'ts-jest',
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
moduleNameMapper: {
'@entity/(.*)': '<rootDir>/../database/entity/$1',
},
}

7120
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,11 +13,12 @@
"start": "node build/index.js",
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
"lint": "eslint . --ext .js,.ts",
"test": "jest --coverage"
"test": "jest --runInBand --coverage "
},
"dependencies": {
"@types/jest": "^27.0.2",
"apollo-server-express": "^2.25.2",
"apollo-server-testing": "^2.25.2",
"axios": "^0.21.1",
"class-validator": "^0.13.1",
"cors": "^2.8.5",
@ -26,17 +27,21 @@
"graphql": "^15.5.1",
"jest": "^27.2.4",
"jsonwebtoken": "^8.5.1",
"libsodium-wrappers": "^0.7.9",
"module-alias": "^2.2.2",
"mysql2": "^2.3.0",
"nodemailer": "^6.6.5",
"random-bigint": "^0.0.1",
"reflect-metadata": "^0.1.13",
"sodium-native": "^3.3.0",
"ts-jest": "^27.0.5",
"type-graphql": "^1.1.1",
"typeorm": "^0.2.37"
"typeorm": "^0.2.38"
},
"devDependencies": {
"@types/express": "^4.17.12",
"@types/jsonwebtoken": "^8.5.2",
"@types/libsodium-wrappers": "^0.7.9",
"@types/node": "^16.10.3",
"@types/nodemailer": "^6.4.4",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0",
"eslint": "^7.29.0",
@ -50,5 +55,8 @@
"prettier": "^2.3.1",
"ts-node": "^10.0.0",
"typescript": "^4.3.4"
},
"_moduleAliases": {
"@entity": "../database/build/entity"
}
}

View File

@ -39,9 +39,25 @@ const community = {
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
}
const loginServer = {
LOGIN_APP_SECRET: process.env.LOGIN_APP_SECRET || '21ffbbc616fe',
LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a',
}
const email = {
EMAIL: process.env.EMAIL === 'true' || false,
EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email',
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx',
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com',
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587',
EMAIL_LINK_VERIFICATION:
process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/vue/checkEmail/$1',
}
// This is needed by graphql-directive-auth
process.env.APP_SECRET = server.JWT_SECRET
const CONFIG = { ...server, ...database, ...klicktipp, ...community }
const CONFIG = { ...server, ...database, ...klicktipp, ...community, ...email, ...loginServer }
export default CONFIG

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,4 @@ import { ArgsType, Field } from 'type-graphql'
export default class CheckUsernameArgs {
@Field(() => String)
username: string
@Field(() => Number, { nullable: true })
groupId?: number
}

View File

@ -2,9 +2,6 @@
import { AuthChecker } from 'type-graphql'
import CONFIG from '../../config'
import { apiGet } from '../../apis/HttpRequest'
import decode from '../../jwt/decode'
import encode from '../../jwt/encode'
@ -13,15 +10,9 @@ const isAuthorized: AuthChecker<any> = async (
) => {
if (context.token) {
const decoded = decode(context.token)
if (decoded.sessionId && decoded.sessionId !== 0) {
const result = await apiGet(
`${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`,
)
context.sessionId = decoded.sessionId
context.pubKey = decoded.pubKey
context.setHeaders.push({ key: 'token', value: encode(decoded.sessionId, decoded.pubKey) })
return result.success
}
context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
return true
}
throw new Error('401 Unauthorized')
}

View File

@ -1,21 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field } from 'type-graphql'
@ObjectType()
export class CheckUsernameResponse {
constructor(json: any) {
this.state = json.state
this.msg = json.msg
this.groupId = json.group_id
}
@Field(() => String)
state: string
@Field(() => String)
msg?: string
@Field(() => Number)
groupId?: number
}

View File

@ -1,13 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field } from 'type-graphql'
@ObjectType()
export class UpdateUserInfosResponse {
constructor(json: any) {
this.validValues = json.valid_values
}
@Field(() => Number)
validValues: number
}

View File

@ -10,15 +10,17 @@ export class User {
@PrimaryGeneratedColumn()
id: number
*/
constructor(json: any) {
this.email = json.email
this.firstName = json.first_name
this.lastName = json.last_name
this.username = json.username
this.description = json.description
this.pubkey = json.public_hex
this.language = json.language
this.publisherId = json.publisher_id
constructor(json?: any) {
if (json) {
this.email = json.email
this.firstName = json.first_name
this.lastName = json.last_name
this.username = json.username
this.description = json.description
this.pubkey = json.public_hex
this.language = json.language
this.publisherId = json.publisher_id
}
}
@Field(() => String)

View File

@ -0,0 +1,123 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { createTestClient } from 'apollo-server-testing'
import createServer from '../../server/createServer'
import CONFIG from '../../config'
jest.mock('../../config')
let query: any
// to do: We need a setup for the tests that closes the connection
let con: any
beforeAll(async () => {
const server = await createServer({})
con = server.con
query = createTestClient(server.apollo).query
})
afterAll(async () => {
await con.close()
})
describe('CommunityResolver', () => {
const getCommunityInfoQuery = `
query {
getCommunityInfo {
name
description
url
registerUrl
}
}
`
const communities = `
query {
communities {
id
name
url
description
registerUrl
}
}
`
describe('getCommunityInfo', () => {
it('returns the default values', async () => {
await expect(query({ query: getCommunityInfoQuery })).resolves.toMatchObject({
data: {
getCommunityInfo: {
name: 'Gradido Entwicklung',
description: 'Die lokale Entwicklungsumgebung von Gradido.',
url: 'http://localhost/vue/',
registerUrl: 'http://localhost/vue/register',
},
},
})
})
})
describe('communities', () => {
describe('PRODUCTION = false', () => {
beforeEach(() => {
CONFIG.PRODUCTION = false
})
it('returns three communities', async () => {
await expect(query({ query: communities })).resolves.toMatchObject({
data: {
communities: [
{
id: 1,
name: 'Gradido Entwicklung',
description: 'Die lokale Entwicklungsumgebung von Gradido.',
url: 'http://localhost/vue/',
registerUrl: 'http://localhost/vue/register-community',
},
{
id: 2,
name: 'Gradido Staging',
description: 'Der Testserver der Gradido-Akademie.',
url: 'https://stage1.gradido.net/vue/',
registerUrl: 'https://stage1.gradido.net/vue/register-community',
},
{
id: 3,
name: 'Gradido-Akademie',
description: 'Freies Institut für Wirtschaftsbionik.',
url: 'https://gradido.net',
registerUrl: 'https://gdd1.gradido.com/vue/register-community',
},
],
},
})
})
})
describe('PRODUCTION = true', () => {
beforeEach(() => {
CONFIG.PRODUCTION = true
})
it('returns one community', async () => {
await expect(query({ query: communities })).resolves.toMatchObject({
data: {
communities: [
{
id: 3,
name: 'Gradido-Akademie',
description: 'Freies Institut für Wirtschaftsbionik.',
url: 'https://gradido.net',
registerUrl: 'https://gdd1.gradido.com/vue/register-community',
},
],
},
})
})
})
})
})

View File

@ -1,10 +1,12 @@
/* eslint-disable new-cap */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
import { getCustomRepository } from 'typeorm'
import { getCustomRepository, getConnection, QueryRunner } from 'typeorm'
import CONFIG from '../../config'
import { sendEMail } from '../../util/sendEMail'
import { Transaction } from '../model/Transaction'
import { TransactionList } from '../model/TransactionList'
@ -19,15 +21,185 @@ import { UserRepository } from '../../typeorm/repository/User'
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
import { TransactionRepository } from '../../typeorm/repository/Transaction'
import { User as dbUser } from '../../typeorm/entity/User'
import { UserTransaction as dbUserTransaction } from '../../typeorm/entity/UserTransaction'
import { Transaction as dbTransaction } from '../../typeorm/entity/Transaction'
import { User as dbUser } from '@entity/User'
import { UserTransaction as dbUserTransaction } from '@entity/UserTransaction'
import { Transaction as dbTransaction } from '@entity/Transaction'
import { TransactionSendCoin as dbTransactionSendCoin } from '@entity/TransactionSendCoin'
import { Balance as dbBalance } from '@entity/Balance'
import { apiPost } from '../../apis/HttpRequest'
import { roundFloorFrom4 } from '../../util/round'
import { roundFloorFrom4, roundCeilFrom4 } from '../../util/round'
import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
import { TransactionTypeId } from '../enum/TransactionTypeId'
import { TransactionType } from '../enum/TransactionType'
import { hasUserAmount, isHexPublicKey } from '../../util/validate'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
/*
# Test
## Prepare
> sudo systemctl start docker
> docker-compose up mariadb
> DROP all databases
> docker-compose down
> docker compose up mariadb database
> verify there is exactly one database `gradido_community`
TODO:
INSERT INTO `login_groups` (`id`, `alias`, `name`, `url`, `host`, `home`, `description`) VALUES
(1, 'docker', 'docker gradido group', 'localhost', 'nginx', '/', 'gradido test group for docker and stage2 with blockchain db');
>> Database is cool
### Start login server
> docker-compose up login-server community-server nginx
>> Login & community servers and nginx proxy are up and running
## Build database
> cd database
> yarn
> yarn build
> cd ..
>> Database has been built successful
### Start backend (no docker for debugging)
> cd backend
> yarn
> yarn dev
>> Backend is up and running
### Create users
> chromium http://localhost:4000/graphql
> mutation{createUser(email: "receiver@user.net", firstName: "Receiver", lastName: "user", password: "123!AAAb", language: "de")}
> mutation{createUser(email: "sender@user.net", firstName: "Sender", lastName: "user", password: "123!AAAb", language: "de")}
> mutation{createUser(email: "creator@user.net", firstName: "Creator", lastName: "user", password: "123!AAAb", language: "de")}
>> Verify you have 3 entries in `login_users`, `login_user_backups` and `state_users`
### make creator an admin
> INSERT INTO login_user_roles (id, user_id, role_id) VALUES (NULL, '3', '1');
> UPDATE login_users SET email_checked = 1 WHERE id = 3;
> uncomment line: 19 in community_server/src/Controller/ServerUsersController.php
> chromium http://localhost/server-users/add
> create user `creator` `123` `creator@different.net`
>> verify you have 1 entry in `server_users`
> login with user on http://localhost/server-users
> activate server user by changing the corresponding flag in the interface
> navigate to http://localhost/transaction-creations/create-multi
> create 1000GDD for user sender@user.net
> navigate to http://localhost
> login with `creator@user.net` `123!AAAb`
> confirm transaction (top right corner - click the thingy, click the green button `Transaktion abschließen`)
### the test:
> chromium http://localhost:4000/graphql
> query{login(email: "sender@user.net", password: "123!AAAb"){pubkey}}
>> copy token from network tab (inspect)
> mutation{sendCoins(email: "receiver@user.net", amount: 10.0, memo: "Hier!")}
> mutation{sendCoins(email: "receiver@user.net", amount: 10.0, memo: "Hier!")}
> Headers: {"Authorization": "Bearer ${token}"}
>> Verify via Database that stuff is as it should see `state_balance` & `transaction_send_coins`
### create decay block
> chromium http://localhost/transactions/add
> login with `creator` `123`
> select `decay start`
> press submit
> wait for at least 0.02 display of decay on user sender@user.net on old frontend, this should be aprox 10min
> chromium http://localhost:4000/graphql
> query{login(email: "sender@user.net", password: "123!AAAb"){pubkey}}
>> copy token from network tab (inspect)
> mutation{sendCoins(email: "receiver@user.net", amount: 10.0, memo: "Hier!")}
>> verify in `transaction_send_coins` that a decay was taken into account
>> same in `state_balances`
>> now check the old frontend
>>> sender@user.net should have a decay of 0.02
>>> while receiver@user.net should have zero decay on anything (old frontend)
### Export data
> docker-compose up phpmyadmin
> chromium http://localhost:8074/
> select gradido_community
> export
> select custom
> untick structure
> ok
## Results
NOTE: We decided not to write the `transaction_signatures` since its unused. This is the main difference.
NOTE: We fixed a bug in the `state_user_transactions code` with the new implementation of apollo
Master:
--
-- Dumping data for table `state_user_transactions`
--
INSERT INTO `state_user_transactions` (`id`, `state_user_id`, `transaction_id`, `transaction_type_id`, `balance`, `balance_date`) VALUES
(1, 2, 1, 1, 10000000, '2021-11-05 12:45:18'),
(2, 2, 2, 2, 9900000, '2021-11-05 12:48:35'),
(3, 1, 2, 2, 100000, '2021-11-05 12:48:35'),
(4, 2, 3, 2, 9800000, '2021-11-05 12:49:07'),
(5, 1, 3, 2, 200000, '2021-11-05 12:49:07'),
(6, 2, 5, 2, 9699845, '2021-11-05 13:03:50'),
(7, 1, 5, 2, 99996, '2021-11-05 13:03:50');
--
-- Dumping data for table `transactions`
--
INSERT INTO `transactions` (`id`, `state_group_id`, `transaction_type_id`, `tx_hash`, `memo`, `received`, `blockchain_type_id`) VALUES
(1, NULL, 1, 0x9ccdcd01ccb6320c09c2d1da2f0bf735a95ece0e7c1df6bbff51918fbaec061700000000000000000000000000000000, '', '2021-11-05 12:45:18', 1),
(2, NULL, 2, 0x58d7706a67fa4ff4b8038168c6be39a2963d7e28e9d3872759ad09c519fe093700000000000000000000000000000000, 'Hier!', '2021-11-05 12:48:35', 1),
(3, NULL, 2, 0x427cd214f92ef35af671129d50edc5a478c53d1e464f285b7615d9794a69f69b00000000000000000000000000000000, 'Hier!', '2021-11-05 12:49:07', 1),
(4, NULL, 9, 0x32807368f0906a21b94c072599795bc9eeab88fb565df82e85cc62a4fdcde48500000000000000000000000000000000, '', '2021-11-05 12:51:51', 1),
(5, NULL, 2, 0x75eb729e0f60a1c8cead1342955853d2440d7a2ea57dfef6d4a18bff0d94491e00000000000000000000000000000000, 'Hier!', '2021-11-05 13:03:50', 1);
--
-- Dumping data for table `transaction_signatures`
--
INSERT INTO `transaction_signatures` (`id`, `transaction_id`, `signature`, `pubkey`) VALUES
(1, 1, 0x5888edcdcf77aaadad6d321882903bc831d7416f17213fd5020a764365b5fcb336e4c7917385a1278ea44ccdb31eac4a09e448053b5e3f8f1fe5da3baf53c008, 0xd5b20f8dee415038bfa2b6b0e1b40ff54850351109444863b04d6d28825b7b7d),
(2, 2, 0xf6fef428f8f22faf7090f7d740e6088d1d90c58ae92d757117d7d91d799e659f3a3a0c65a3fd97cbde798e761f9d23eff13e8810779a184c97c411f28e7c4608, 0xdc74a589004377ab14836dce68ce2ca34e5b17147cd78ad4b3afe8137524ae8a),
(3, 3, 0x8ebe9730c6cf61f56ef401d6f2bd229f3c298ca3c2791ee9137e4827b7af6c6d6566fca616eb1fe7adc2e4d56b5c7350ae3990c9905580630fa75ecffca8e001, 0xdc74a589004377ab14836dce68ce2ca34e5b17147cd78ad4b3afe8137524ae8a),
(4, 5, 0x50cf418f7e217391e89ab9c2879ae68d7c7c597d846b4fe1c082b5b16e5d0c85c328fbf48ad3490bcfe94f446700ae0a4b0190e76d26cc752abced58f480c80f, 0xdc74a589004377ab14836dce68ce2ca34e5b17147cd78ad4b3afe8137524ae8a);
This Feature Branch:
--
-- Dumping data for table `state_user_transactions`
--
INSERT INTO `state_user_transactions` (`id`, `state_user_id`, `transaction_id`, `transaction_type_id`, `balance`, `balance_date`) VALUES
(1, 2, 1, 1, 10000000, '2021-11-05 00:25:46'),
(12, 2, 7, 2, 9900000, '2021-11-05 00:55:37'),
(13, 1, 7, 2, 100000, '2021-11-05 00:55:37'),
(14, 2, 8, 2, 9800000, '2021-11-05 01:00:04'),
(15, 1, 8, 2, 200000, '2021-11-05 01:00:04'),
(16, 2, 10, 2, 9699772, '2021-11-05 01:17:41'),
(17, 1, 10, 2, 299995, '2021-11-05 01:17:41');
--
-- Dumping data for table `transactions`
--
INSERT INTO `transactions` (`id`, `state_group_id`, `transaction_type_id`, `tx_hash`, `memo`, `received`, `blockchain_type_id`) VALUES
(1, NULL, 1, 0xdd030d475479877587d927ed9024784ba62266cf1f3d87862fc98ad68f7b26e400000000000000000000000000000000, '', '2021-11-05 00:25:46', 1),
(7, NULL, 2, NULL, 'Hier!', '2021-11-05 00:55:37', 1),
(8, NULL, 2, NULL, 'Hier!', '2021-11-05 01:00:04', 1),
(9, NULL, 9, 0xb1cbedbf126aa35f5edbf06e181c415361d05228ab4da9d19a4595285a673dfa00000000000000000000000000000000, '', '2021-11-05 01:05:34', 1),
(10, NULL, 2, NULL, 'Hier!', '2021-11-05 01:17:41', 1);
--
-- Dumping data for table `transaction_signatures`
--
INSERT INTO `transaction_signatures` (`id`, `transaction_id`, `signature`, `pubkey`) VALUES
(1, 1, 0x60d632479707e5d01cdc32c3326b5a5bae11173a0c06b719ee7b552f9fd644de1a0cd4afc207253329081d39dac1a63421f51571d836995c649fc39afac7480a, 0x48c45cb4fea925e83850f68f2fa8f27a1a4ed1bcba68cdb59fcd86adef3f52ee);
*/
// Helper function
async function calculateAndAddDecayTransactions(
@ -150,7 +322,7 @@ async function calculateAndAddDecayTransactions(
const decayTransaction = new Transaction()
decayTransaction.type = 'decay'
decayTransaction.balance = roundFloorFrom4(balance)
decayTransaction.balance = roundCeilFrom4(balance)
decayTransaction.decayDuration = decay.decayDuration
decayTransaction.decayStart = decay.decayStart
decayTransaction.decayEnd = decay.decayEnd
@ -210,6 +382,87 @@ async function listTransactions(
return transactionList
}
// helper helper function
async function updateStateBalance(
user: dbUser,
centAmount: number,
received: Date,
queryRunner: QueryRunner,
): Promise<dbBalance> {
const balanceRepository = getCustomRepository(BalanceRepository)
let balance = await balanceRepository.findByUser(user.id)
if (!balance) {
balance = new dbBalance()
balance.userId = user.id
balance.amount = centAmount
balance.modified = received
} else {
const decaiedBalance = await calculateDecay(balance.amount, balance.recordDate, received).catch(
() => {
throw new Error('error by calculating decay')
},
)
balance.amount = Number(decaiedBalance) + centAmount
balance.modified = new Date()
}
if (balance.amount <= 0) {
throw new Error('error new balance <= 0')
}
balance.recordDate = received
return queryRunner.manager.save(balance).catch((error) => {
throw new Error('error saving balance:' + error)
})
}
// helper helper function
async function addUserTransaction(
user: dbUser,
transaction: dbTransaction,
centAmount: number,
queryRunner: QueryRunner,
): Promise<dbUserTransaction> {
let newBalance = centAmount
const userTransactionRepository = getCustomRepository(UserTransactionRepository)
const lastUserTransaction = await userTransactionRepository.findLastForUser(user.id)
if (lastUserTransaction) {
newBalance += Number(
await calculateDecay(
Number(lastUserTransaction.balance),
lastUserTransaction.balanceDate,
transaction.received,
).catch(() => {
throw new Error('error by calculating decay')
}),
)
}
if (newBalance <= 0) {
throw new Error('error new balance <= 0')
}
const newUserTransaction = new dbUserTransaction()
newUserTransaction.userId = user.id
newUserTransaction.transactionId = transaction.id
newUserTransaction.transactionTypeId = transaction.transactionTypeId
newUserTransaction.balance = newBalance
newUserTransaction.balanceDate = transaction.received
return queryRunner.manager.save(newUserTransaction).catch((error) => {
throw new Error('Error saving user transaction: ' + error)
})
}
async function getPublicKey(email: string): Promise<string | null> {
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOne({ email: email })
// User not found
if (!loginUser) {
return null
}
return loginUser.pubKey.toString('hex')
}
@Resolver()
export class TransactionResolver {
@Authorized()
@ -252,19 +505,148 @@ export class TransactionResolver {
@Args() { email, amount, memo }: TransactionSendArgs,
@Ctx() context: any,
): Promise<string> {
const payload = {
session_id: context.sessionId,
target_email: email,
amount: amount * 10000,
memo,
auto_sign: true,
transaction_type: 'transfer',
blockchain_type: 'mysql',
// TODO this is subject to replay attacks
// validate sender user (logged in)
const userRepository = getCustomRepository(UserRepository)
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
if (senderUser.pubkey.length !== 32) {
throw new Error('invalid sender public key')
}
const result = await apiPost(CONFIG.LOGIN_API_URL + 'createTransaction', payload)
if (!result.success) {
throw new Error(result.data)
if (!hasUserAmount(senderUser, amount)) {
throw new Error("user hasn't enough GDD")
}
// validate recipient user
// TODO: the detour over the public key is unnecessary
const recipiantPublicKey = await getPublicKey(email)
if (!recipiantPublicKey) {
throw new Error('recipiant not known')
}
if (!isHexPublicKey(recipiantPublicKey)) {
throw new Error('invalid recipiant public key')
}
const recipiantUser = await userRepository.findByPubkeyHex(recipiantPublicKey)
if (!recipiantUser) {
throw new Error('Cannot find recipiant user by local send coins transaction')
} else if (recipiantUser.disabled) {
throw new Error('recipiant user account is disabled')
}
// validate amount
if (amount <= 0) {
throw new Error('invalid amount')
}
const centAmount = Math.trunc(amount * 10000)
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
// transaction
let transaction = new dbTransaction()
transaction.transactionTypeId = TransactionTypeId.SEND
transaction.memo = memo
// TODO: NO! this is problematic in its construction
const insertResult = await queryRunner.manager.insert(dbTransaction, transaction)
transaction = await queryRunner.manager
.findOneOrFail(dbTransaction, insertResult.generatedMaps[0].id)
.catch((error) => {
throw new Error('error loading saved transaction: ' + error)
})
// Insert Transaction: sender - amount
const senderUserTransactionBalance = await addUserTransaction(
senderUser,
transaction,
-centAmount,
queryRunner,
)
// Insert Transaction: recipient + amount
const recipiantUserTransactionBalance = await addUserTransaction(
recipiantUser,
transaction,
centAmount,
queryRunner,
)
// Update Balance: sender - amount
const senderStateBalance = await updateStateBalance(
senderUser,
-centAmount,
transaction.received,
queryRunner,
)
// Update Balance: recipiant + amount
const recipiantStateBalance = await updateStateBalance(
recipiantUser,
centAmount,
transaction.received,
queryRunner,
)
if (senderStateBalance.amount !== senderUserTransactionBalance.balance) {
throw new Error('db data corrupted, sender')
}
if (recipiantStateBalance.amount !== recipiantUserTransactionBalance.balance) {
throw new Error('db data corrupted, recipiant')
}
// transactionSendCoin
const transactionSendCoin = new dbTransactionSendCoin()
transactionSendCoin.transactionId = transaction.id
transactionSendCoin.userId = senderUser.id
transactionSendCoin.senderPublic = senderUser.pubkey
transactionSendCoin.recipiantUserId = recipiantUser.id
transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex')
transactionSendCoin.amount = centAmount
transactionSendCoin.senderFinalBalance = senderStateBalance.amount
await queryRunner.manager.save(transactionSendCoin).catch((error) => {
throw new Error('error saving transaction send coin: ' + error)
})
await queryRunner.manager.save(transaction).catch((error) => {
throw new Error('error saving transaction with tx hash: ' + error)
})
await queryRunner.commitTransaction()
} catch (e) {
await queryRunner.rollbackTransaction()
// TODO: This is broken code - we should never correct an autoincrement index in production
// according to dario it is required tho to properly work. The index of the table is used as
// index for the transaction which requires a chain without gaps
const count = await queryRunner.manager.count(dbTransaction)
// fix autoincrement value which seems not effected from rollback
await queryRunner
.query('ALTER TABLE `transactions` auto_increment = ?', [count])
.catch((error) => {
// eslint-disable-next-line no-console
console.log('problems with reset auto increment: %o', error)
})
throw e
} finally {
await queryRunner.release()
}
// send notification email
// TODO: translate
await sendEMail({
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
to: `${recipiantUser.firstName} ${recipiantUser.lastName} <${recipiantUser.email}>`,
subject: 'Gradido Überweisung',
text: `Hallo ${recipiantUser.firstName} ${recipiantUser.lastName}
Du hast soeben ${amount} GDD von ${senderUser.firstName} ${senderUser.lastName} erhalten.
${senderUser.firstName} ${senderUser.lastName} schreibt:
${memo}
Bitte antworte nicht auf diese E-Mail!
Mit freundlichen Grüßen,
dein Gradido-Team`,
})
return 'success'
}
}

View File

@ -1,15 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import fs from 'fs'
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
import { from_hex as fromHex } from 'libsodium-wrappers'
import { getConnection, getCustomRepository } from 'typeorm'
import CONFIG from '../../config'
import { CheckUsernameResponse } from '../model/CheckUsernameResponse'
import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
import { UpdateUserInfosResponse } from '../model/UpdateUserInfosResponse'
import { User } from '../model/User'
import { User as DbUser } from '../../typeorm/entity/User'
import { User as DbUser } from '@entity/User'
import encode from '../../jwt/encode'
import ChangePasswordArgs from '../arg/ChangePasswordArgs'
import CheckUsernameArgs from '../arg/CheckUsernameArgs'
@ -22,10 +21,176 @@ import {
klicktippNewsletterStateMiddleware,
} from '../../middleware/klicktippMiddleware'
import { CheckEmailResponse } from '../model/CheckEmailResponse'
import { getCustomRepository } from 'typeorm'
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { Setting } from '../enum/Setting'
import { UserRepository } from '../../typeorm/repository/User'
import { LoginUser } from '@entity/LoginUser'
import { LoginUserBackup } from '@entity/LoginUserBackup'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { sendEMail } from '../../util/sendEMail'
import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sodium = require('sodium-native')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const random = require('random-bigint')
// We will reuse this for changePassword
const isPassword = (password: string): boolean => {
return !!password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^a-zA-Z0-9 \\t\\n\\r]).{8,}$/)
}
const LANGUAGES = ['de', 'en']
const DEFAULT_LANGUAGE = 'de'
const isLanguage = (language: string): boolean => {
return LANGUAGES.includes(language)
}
const PHRASE_WORD_COUNT = 24
const WORDS = fs.readFileSync('src/config/mnemonic.english.txt').toString().split('\n')
const PassphraseGenerate = (): string[] => {
const result = []
for (let i = 0; i < PHRASE_WORD_COUNT; i++) {
result.push(WORDS[sodium.randombytes_random() % 2048])
}
return result
/*
return [
'behind',
'salmon',
'fluid',
'orphan',
'frost',
'elder',
'amateur',
'always',
'panel',
'palm',
'leopard',
'essay',
'punch',
'title',
'fun',
'annual',
'page',
'hundred',
'journey',
'select',
'figure',
'tunnel',
'casual',
'bar',
]
*/
}
/*
Test results:
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`) VALUES
// old
(1, 'peter@lustig.de', 'peter', 'lustig', '', '', 4747956395458240931, 0x8c75edd507f470e5378f927489374694d68f3d155523f1c4402c36affd35a7ed, 0xb0e310655726b088631ccfd31ad6470ee50115c161dde8559572fa90657270ff13dc1200b2d3ea90dfbe92f3a4475ee4d9cee4989e39736a0870c33284bc73a8ae690e6da89f241a121eb3b500c22885, 0x9f700e6f6ec351a140b674c0edd4479509697b023bd8bee8826915ef6c2af036, '2021-11-03 20:05:04', 0, 0, 'de', 0, 1, 0);
// new
(2, 'peter@lustig.de', 'peter', 'lustig', '', '', 4747956395458240931, 0x8c75edd507f470e5378f927489374694d68f3d155523f1c4402c36affd35a7ed, 0xb0e310655726b088631ccfd31ad6470ee50115c161dde8559572fa90657270ff13dc1200b2d3ea90dfbe92f3a4475ee4d9cee4989e39736a0870c33284bc73a8ae690e6da89f241a121eb3b500c22885, 0x9f700e6f6ec351a140b674c0edd4479509697b023bd8bee8826915ef6c2af036, '2021-11-03 20:22:15', 0, 0, 'de', 0, 1, 0);
INSERT INTO `login_user_backups` (`id`, `user_id`, `passphrase`, `mnemonic_type`) VALUES
// old
(1, 1, 'behind salmon fluid orphan frost elder amateur always panel palm leopard essay punch title fun annual page hundred journey select figure tunnel casual bar ', 2);
// new
(2, 2, 'behind salmon fluid orphan frost elder amateur always panel palm leopard essay punch title fun annual page hundred journey select figure tunnel casual bar ', 2);
*/
const KeyPairEd25519Create = (passphrase: string[]): Buffer[] => {
if (!passphrase.length || passphrase.length < PHRASE_WORD_COUNT) {
throw new Error('passphrase empty or to short')
}
const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES)
sodium.crypto_hash_sha512_init(state)
// To prevent breaking existing passphrase-hash combinations word indices will be put into 64 Bit Variable to mimic first implementation of algorithms
for (let i = 0; i < PHRASE_WORD_COUNT; i++) {
const value = Buffer.alloc(8)
const wordIndex = WORDS.indexOf(passphrase[i])
value.writeBigInt64LE(BigInt(wordIndex))
sodium.crypto_hash_sha512_update(state, value)
}
// trailing space is part of the login_server implementation
const clearPassphrase = passphrase.join(' ') + ' '
sodium.crypto_hash_sha512_update(state, Buffer.from(clearPassphrase))
const outputHashBuffer = Buffer.alloc(sodium.crypto_hash_sha512_BYTES)
sodium.crypto_hash_sha512_final(state, outputHashBuffer)
const pubKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES)
const privKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)
sodium.crypto_sign_seed_keypair(
pubKey,
privKey,
outputHashBuffer.slice(0, sodium.crypto_sign_SEEDBYTES),
)
return [pubKey, privKey]
}
const SecretKeyCryptographyCreateKey = (salt: string, password: string): Buffer[] => {
const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex')
const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex')
if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) {
throw new Error(
`ServerKey has an invalid size. The size must be ${sodium.crypto_shorthash_KEYBYTES} bytes.`,
)
}
const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES)
sodium.crypto_hash_sha512_init(state)
sodium.crypto_hash_sha512_update(state, Buffer.from(salt))
sodium.crypto_hash_sha512_update(state, configLoginAppSecret)
const hash = Buffer.alloc(sodium.crypto_hash_sha512_BYTES)
sodium.crypto_hash_sha512_final(state, hash)
const encryptionKey = Buffer.alloc(sodium.crypto_box_SEEDBYTES)
const opsLimit = 10
const memLimit = 33554432
const algo = 2
sodium.crypto_pwhash(
encryptionKey,
Buffer.from(password),
hash.slice(0, sodium.crypto_pwhash_SALTBYTES),
opsLimit,
memLimit,
algo,
)
const encryptionKeyHash = Buffer.alloc(sodium.crypto_shorthash_BYTES)
sodium.crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey)
return [encryptionKeyHash, encryptionKey]
}
const getEmailHash = (email: string): Buffer => {
const emailHash = Buffer.alloc(sodium.crypto_generichash_BYTES)
sodium.crypto_generichash(emailHash, Buffer.from(email))
return emailHash
}
const SecretKeyCryptographyEncrypt = (message: Buffer, encryptionKey: Buffer): Buffer => {
const encrypted = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES)
const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES)
nonce.fill(31) // static nonce
sodium.crypto_secretbox_easy(encrypted, message, nonce, encryptionKey)
return encrypted
}
const SecretKeyCryptographyDecrypt = (encryptedMessage: Buffer, encryptionKey: Buffer): Buffer => {
const message = Buffer.alloc(encryptedMessage.length - sodium.crypto_secretbox_MACBYTES)
const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES)
nonce.fill(31) // static nonce
sodium.crypto_secretbox_open_easy(message, encryptedMessage, nonce, encryptionKey)
return message
}
@Resolver()
export class UserResolver {
@ -36,35 +201,34 @@ export class UserResolver {
@Ctx() context: any,
): Promise<User> {
email = email.trim().toLowerCase()
const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
// if there is no user, throw an authentication error
if (!result.success) {
throw new Error(result.data)
}
context.setHeaders.push({
key: 'token',
value: encode(result.data.session_id, result.data.user.public_hex),
// const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
// UnsecureLogin
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findByEmail(email).catch(() => {
throw new Error('No user with this credentials')
})
const user = new User(result.data.user)
// Hack: Database Field is not validated properly and not nullable
if (user.publisherId === 0) {
user.publisherId = undefined
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
const loginUserPassword = BigInt(loginUser.password.toString())
if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) {
throw new Error('No user with this credentials')
}
user.hasElopage = result.data.hasElopage
// read additional settings from settings table
// TODO: If user has no pubKey Create it again and update user.
const userRepository = getCustomRepository(UserRepository)
let userEntity: void | DbUser
userEntity = await userRepository.findByPubkeyHex(user.pubkey).catch(() => {
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 = user.firstName
userEntity.lastName = user.lastName
userEntity.username = user.username
userEntity.email = user.email
userEntity.pubkey = Buffer.from(fromHex(user.pubkey))
userEntity.firstName = loginUser.firstName
userEntity.lastName = loginUser.lastName
userEntity.username = loginUser.username
userEntity.email = loginUser.email
userEntity.pubkey = loginUser.pubKey
userEntity.save().catch(() => {
userRepository.save(userEntity).catch(() => {
throw new Error('error by save userEntity')
})
})
@ -72,15 +236,28 @@ export class UserResolver {
throw new Error('error with cannot happen')
}
// Save publisherId if Elopage is not yet registered
const user = new User()
user.email = email
user.firstName = loginUser.firstName
user.lastName = loginUser.lastName
user.username = loginUser.username
user.description = loginUser.description
user.pubkey = loginUserPubKeyString
user.language = loginUser.language
// Elopage Status & Stored PublisherId
user.hasElopage = await this.hasElopage({ pubKey: loginUserPubKeyString })
if (!user.hasElopage && publisherId) {
user.publisherId = publisherId
await this.updateUserInfos(
{ publisherId },
{ sessionId: result.data.session_id, pubKey: result.data.user.public_hex },
)
// TODO: Check if we can use updateUserInfos
// await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey })
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
loginUser.publisherId = publisherId
loginUserRepository.save(loginUser)
}
// coinAnimation
const userSettingRepository = getCustomRepository(UserSettingRepository)
const coinanimation = await userSettingRepository
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
@ -88,6 +265,12 @@ export class UserResolver {
throw new Error(error)
})
user.coinanimation = coinanimation
context.setHeaders.push({
key: 'token',
value: encode(loginUser.pubKey),
})
return user
}
@ -108,46 +291,152 @@ export class UserResolver {
@Authorized()
@Query(() => String)
async logout(@Ctx() context: any): Promise<string> {
const payload = { session_id: context.sessionId }
const result = await apiPost(CONFIG.LOGIN_API_URL + 'logout', payload)
if (!result.success) {
throw new Error(result.data)
}
return 'success'
async logout(): Promise<boolean> {
// TODO: We dont need this anymore, but might need this in the future in oder to invalidate a valid JWT-Token.
// Furthermore this hook can be useful for tracking user behaviour (did he logout or not? Warn him if he didn't on next login)
// The functionality is fully client side - the client just needs to delete his token with the current implementation.
// we could try to force this by sending `token: null` or `token: ''` with this call. But since it bares no real security
// we should just return true for now.
return true
}
@Mutation(() => String)
async createUser(
@Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs,
): Promise<string> {
const payload = {
email,
first_name: firstName,
last_name: lastName,
password,
emailType: 2,
login_after_register: true,
language: language,
publisher_id: publisherId,
}
const result = await apiPost(CONFIG.LOGIN_API_URL + 'createUser', payload)
if (!result.success) {
throw new Error(result.data)
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
// default int publisher_id = 0;
// Validate Language (no throw)
if (!isLanguage(language)) {
language = DEFAULT_LANGUAGE
}
const user = new User(result.data.user)
const dbuser = new DbUser()
dbuser.pubkey = Buffer.from(fromHex(user.pubkey))
dbuser.email = user.email
dbuser.firstName = user.firstName
dbuser.lastName = user.lastName
dbuser.username = user.username
// Validate Password
if (!isPassword(password)) {
throw new Error(
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
)
}
dbuser.save().catch(() => {
throw new Error('error saving user')
})
// Validate username
// TODO: never true
const username = ''
if (username.length > 3 && !this.checkUsername({ username })) {
throw new Error('Username already in use')
}
// Validate email unique
// TODO: i can register an email in upper/lower case twice
const userRepository = getCustomRepository(UserRepository)
const usersFound = await userRepository.count({ email })
if (usersFound !== 0) {
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
throw new Error(`User already exists.`)
}
const passphrase = PassphraseGenerate()
const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
const emailHash = getEmailHash(email)
const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
// Table: login_users
const loginUser = new LoginUser()
loginUser.email = email
loginUser.firstName = firstName
loginUser.lastName = lastName
loginUser.username = username
loginUser.description = ''
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.privKey = encryptedPrivkey
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
const { id: loginUserId } = await queryRunner.manager.save(loginUser).catch((error) => {
// eslint-disable-next-line no-console
console.log('insert LoginUser failed', 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.pubkey = keyPair[0]
dbUser.email = email
dbUser.firstName = firstName
dbUser.lastName = lastName
dbUser.username = username
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')
})
// Store EmailOptIn in DB
const emailOptIn = new LoginEmailOptIn()
emailOptIn.userId = loginUserId
emailOptIn.verificationCode = random(64)
emailOptIn.emailOptInTypeId = 2
await queryRunner.manager.save(emailOptIn).catch((error) => {
// eslint-disable-next-line no-console
console.log('Error while saving emailOptIn', error)
throw new Error('error saving email opt in')
})
// Send EMail to user
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
/\$1/g,
emailOptIn.verificationCode.toString(),
)
const emailSent = await sendEMail({
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
to: `${firstName} ${lastName} <${email}>`,
subject: 'Gradido: E-Mail Überprüfung',
text: `Hallo ${firstName} ${lastName},
Deine EMail wurde soeben bei Gradido registriert.
Klicke bitte auf diesen Link, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren:
${activationLink}
oder kopiere den obigen Link in dein Browserfenster.
Mit freundlichen Grüßen,
dein Gradido-Team`,
})
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
// eslint-disable-next-line no-console
console.log(`Account confirmation link: ${activationLink}`)
}
await queryRunner.commitTransaction()
} catch (e) {
await queryRunner.rollbackTransaction()
throw e
} finally {
await queryRunner.release()
}
return 'success'
}
@ -185,7 +474,7 @@ export class UserResolver {
}
@Authorized()
@Mutation(() => UpdateUserInfosResponse)
@Mutation(() => Boolean)
async updateUserInfos(
@Args()
{
@ -200,90 +489,120 @@ export class UserResolver {
coinanimation,
}: UpdateUserInfosArgs,
@Ctx() context: any,
): Promise<UpdateUserInfosResponse> {
const payload = {
session_id: context.sessionId,
update: {
'User.first_name': firstName || undefined,
'User.last_name': lastName || undefined,
'User.description': description || undefined,
'User.username': username || undefined,
'User.language': language || undefined,
'User.publisher_id': publisherId || undefined,
'User.password': passwordNew || undefined,
'User.password_old': password || undefined,
},
}
let response: UpdateUserInfosResponse | undefined
): Promise<boolean> {
const userRepository = getCustomRepository(UserRepository)
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
if (
firstName ||
lastName ||
description ||
username ||
language ||
publisherId ||
passwordNew ||
password
) {
const result = await apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
if (!result.success) throw new Error(result.data)
response = new UpdateUserInfosResponse(result.data)
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
let userEntityChanged = false
if (firstName) {
userEntity.firstName = firstName
userEntityChanged = true
}
if (lastName) {
userEntity.lastName = lastName
userEntityChanged = true
}
if (username) {
userEntity.username = username
userEntityChanged = true
}
if (userEntityChanged) {
userEntity.save().catch((error) => {
throw new Error(error)
})
}
if (username) {
throw new Error('change username currently not supported!')
// TODO: this error was thrown on login_server whenever you tried to change the username
// to anything except "" which is an exception to the rules below. Those were defined
// aswell, even tho never used.
// ^[a-zA-Z][a-zA-Z0-9_-]*$
// username must start with [a-z] or [A-Z] and than can contain also [0-9], - and _
// username already used
// userEntity.username = username
}
if (coinanimation !== undefined) {
// load user and balance
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
if (firstName) {
loginUser.firstName = firstName
userEntity.firstName = firstName
}
const userSettingRepository = getCustomRepository(UserSettingRepository)
userSettingRepository
.setOrUpdate(userEntity.id, Setting.COIN_ANIMATION, coinanimation.toString())
.catch((error) => {
throw new Error(error)
})
if (lastName) {
loginUser.lastName = lastName
userEntity.lastName = lastName
}
if (!response) {
response = new UpdateUserInfosResponse({ valid_values: 1 })
} else {
response.validValues++
if (description) {
loginUser.description = description
}
if (language) {
if (!isLanguage(language)) {
throw new Error(`"${language}" isn't a valid language`)
}
loginUser.language = language
}
if (!response) {
throw new Error('no valid response')
if (password && passwordNew) {
// TODO: This had some error cases defined - like missing private key. This is no longer checked.
const oldPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, password)
if (loginUser.password !== oldPasswordHash[0].readBigUInt64LE()) {
throw new Error(`Old password is invalid`)
}
const privKey = SecretKeyCryptographyDecrypt(loginUser.privKey, oldPasswordHash[1])
const newPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, passwordNew) // return short and long hash
const encryptedPrivkey = SecretKeyCryptographyEncrypt(privKey, newPasswordHash[1])
// Save new password hash and newly encrypted private key
loginUser.password = newPasswordHash[0].readBigInt64LE()
loginUser.privKey = encryptedPrivkey
}
return response
// Save publisherId only if Elopage is not yet registered
if (publisherId && !(await this.hasElopage(context))) {
loginUser.publisherId = publisherId
}
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
if (coinanimation) {
queryRunner.manager
.getCustomRepository(UserSettingRepository)
.setOrUpdate(userEntity.id, Setting.COIN_ANIMATION, coinanimation.toString())
.catch((error) => {
throw new Error('error saving coinanimation: ' + error)
})
}
await queryRunner.manager.save(loginUser).catch((error) => {
throw new Error('error saving loginUser: ' + error)
})
await queryRunner.manager.save(userEntity).catch((error) => {
throw new Error('error saving user: ' + error)
})
await queryRunner.commitTransaction()
} catch (e) {
await queryRunner.rollbackTransaction()
throw e
} finally {
await queryRunner.release()
}
return true
}
@Query(() => CheckUsernameResponse)
async checkUsername(
@Args() { username, groupId = 1 }: CheckUsernameArgs,
): Promise<CheckUsernameResponse> {
const response = await apiGet(
CONFIG.LOGIN_API_URL + `checkUsername?username=${username}&group_id=${groupId}`,
)
if (!response.success) throw new Error(response.data)
return new CheckUsernameResponse(response.data)
@Query(() => Boolean)
async checkUsername(@Args() { username }: CheckUsernameArgs): Promise<boolean> {
// Username empty?
if (username === '') {
throw new Error('Username must be set.')
}
// Do we fullfil the minimum character length?
const MIN_CHARACTERS_USERNAME = 2
if (username.length < MIN_CHARACTERS_USERNAME) {
throw new Error(`Username must be at minimum ${MIN_CHARACTERS_USERNAME} characters long.`)
}
const usersFound = await LoginUser.count({ username })
// Username already present?
if (usersFound !== 0) {
throw new Error(`Username "${username}" already taken.`)
}
return true
}
@Query(() => CheckEmailResponse)
@ -298,12 +617,17 @@ export class UserResolver {
return new CheckEmailResponse(result.data)
}
@Authorized()
@Query(() => Boolean)
async hasElopage(@Ctx() context: any): Promise<boolean> {
const result = await apiGet(CONFIG.LOGIN_API_URL + 'hasElopage?session_id=' + context.sessionId)
if (!result.success) {
throw new Error(result.data)
const userRepository = getCustomRepository(UserRepository)
const userEntity = await userRepository.findByPubkeyHex(context.pubKey).catch()
if (!userEntity) {
return false
}
return result.data.hasElopage
const loginElopageBuysRepository = getCustomRepository(LoginElopageBuysRepository)
const elopageBuyCount = await loginElopageBuysRepository.count({ payerEmail: userEntity.email })
return elopageBuyCount > 0
}
}

View File

@ -6,7 +6,7 @@ import isAuthorized from './directive/isAuthorized'
const schema = async (): Promise<GraphQLSchema> => {
return buildSchema({
resolvers: [path.join(__dirname, 'resolver', `*.{js,ts}`)],
resolvers: [path.join(__dirname, 'resolver', `!(*.test).{js,ts}`)],
authChecker: isAuthorized,
})
}

View File

@ -1,63 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import 'reflect-metadata'
import express from 'express'
import { ApolloServer } from 'apollo-server-express'
import createServer from './server/createServer'
// config
import CONFIG from './config'
// database
import connection from './typeorm/connection'
import getDBVersion from './typeorm/getDBVersion'
// server
import cors from './server/cors'
import context from './server/context'
import plugins from './server/plugins'
// graphql
import schema from './graphql/schema'
// TODO implement
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
const DB_VERSION = '0002-add_settings'
async function main() {
// open mysql connection
const con = await connection()
if (!con || !con.isConnected) {
throw new Error(`Couldn't open connection to database`)
}
const { app } = await createServer()
// check for correct database version
const dbVersion = await getDBVersion()
if (!dbVersion || dbVersion.indexOf(DB_VERSION) === -1) {
throw new Error(
`Wrong database version - the backend requires '${DB_VERSION}' but found '${
dbVersion || 'None'
}'`,
)
}
// Express Server
const server = express()
// cors
server.use(cors)
// Apollo Server
const apollo = new ApolloServer({
schema: await schema(),
playground: CONFIG.GRAPHIQL,
context,
plugins,
})
apollo.applyMiddleware({ app: server })
// Start Server
server.listen(CONFIG.PORT, () => {
app.listen(CONFIG.PORT, () => {
// eslint-disable-next-line no-console
console.log(`Server is running at http://localhost:${CONFIG.PORT}`)
if (CONFIG.GRAPHIQL) {

View File

@ -2,27 +2,22 @@ import jwt, { JwtPayload } from 'jsonwebtoken'
import CONFIG from '../config/'
interface CustomJwtPayload extends JwtPayload {
sessionId: number
pubKey: Buffer
}
type DecodedJwt = {
token: string
sessionId: number
pubKey: Buffer
}
export default (token: string): DecodedJwt => {
if (!token) throw new Error('401 Unauthorized')
let sessionId = null
let pubKey = null
try {
const decoded = <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
sessionId = decoded.sessionId
pubKey = decoded.pubKey
return {
token,
sessionId,
pubKey,
}
} catch (err) {

View File

@ -5,10 +5,9 @@ import jwt from 'jsonwebtoken'
import CONFIG from '../config/'
// Generate an Access Token
export default function encode(sessionId: number, pubKey: Buffer): string {
const token = jwt.sign({ sessionId, pubKey }, CONFIG.JWT_SECRET, {
export default function encode(pubKey: Buffer): string {
const token = jwt.sign({ pubKey }, CONFIG.JWT_SECRET, {
expiresIn: CONFIG.JWT_EXPIRES_IN,
subject: sessionId.toString(),
})
return token
}

View File

@ -0,0 +1,64 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import 'reflect-metadata'
import 'module-alias/register'
import { ApolloServer } from 'apollo-server-express'
import express from 'express'
// database
import connection from '../typeorm/connection'
import getDBVersion from '../typeorm/getDBVersion'
// server
import cors from './cors'
import serverContext from './context'
import plugins from './plugins'
// config
import CONFIG from '../config'
// graphql
import schema from '../graphql/schema'
// TODO implement
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
const DB_VERSION = '0004-login_server_data'
const createServer = async (context: any = serverContext): Promise<any> => {
// open mysql connection
const con = await connection()
if (!con || !con.isConnected) {
throw new Error(`Couldn't open connection to database`)
}
// check for correct database version
const dbVersion = await getDBVersion()
if (!dbVersion || dbVersion.indexOf(DB_VERSION) === -1) {
throw new Error(
`Wrong database version - the backend requires '${DB_VERSION}' but found '${
dbVersion || 'None'
}'`,
)
}
// Express Server
const app = express()
// cors
app.use(cors)
// Apollo Server
const apollo = new ApolloServer({
schema: await schema(),
playground: CONFIG.GRAPHIQL,
context,
plugins,
})
apollo.applyMiddleware({ app })
return { apollo, app, con }
}
export default createServer

View File

@ -1,6 +1,6 @@
import { createConnection, Connection } from 'typeorm'
import CONFIG from '../config'
import path from 'path'
import { entities } from '@entity/index'
const connection = async (): Promise<Connection | null> => {
let con = null
@ -13,10 +13,13 @@ const connection = async (): Promise<Connection | null> => {
username: CONFIG.DB_USER,
password: CONFIG.DB_PASSWORD,
database: CONFIG.DB_DATABASE,
entities: [path.join(__dirname, 'entity', '*.{ts,js}')],
entities,
synchronize: false,
})
} catch (error) {}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
return con
}

View File

@ -1,10 +1,13 @@
import { Migration } from './entity/Migration'
import { getRepository } from 'typeorm'
import { Migration } from '@entity/Migration'
const getDBVersion = async (): Promise<string | null> => {
try {
const dbVersion = await Migration.findOne({ order: { version: 'DESC' } })
const dbVersion = await getRepository(Migration).findOne({ order: { version: 'DESC' } })
return dbVersion ? dbVersion.fileName : null
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
return null
}
}

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { Balance } from '../entity/Balance'
import { Balance } from '@entity/Balance'
@EntityRepository(Balance)
export class BalanceRepository extends Repository<Balance> {

View File

@ -0,0 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
@EntityRepository(LoginElopageBuys)
export class LoginElopageBuysRepository extends Repository<LoginElopageBuys> {}

View File

@ -0,0 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
@EntityRepository(LoginEmailOptIn)
export class LoginEmailOptInRepository extends Repository<LoginEmailOptIn> {}

View File

@ -0,0 +1,11 @@
import { EntityRepository, Repository } from '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()
}
}

View File

@ -0,0 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { LoginUserBackup } from '@entity/LoginUserBackup'
@EntityRepository(LoginUserBackup)
export class LoginUserBackupRepository extends Repository<LoginUserBackup> {}

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { Transaction } from '../entity/Transaction'
import { Transaction } from '@entity/Transaction'
@EntityRepository(Transaction)
export class TransactionRepository extends Repository<Transaction> {

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { User } from '../entity/User'
import { User } from '@entity/User'
@EntityRepository(User)
export class UserRepository extends Repository<User> {
@ -9,6 +9,15 @@ export class UserRepository extends Repository<User> {
.getOneOrFail()
}
async findByPubkeyHexBuffer(pubkeyHexBuffer: Buffer): Promise<User> {
const pubKeyString = pubkeyHexBuffer.toString('hex')
return await this.findByPubkeyHex(pubKeyString)
}
async findByEmail(email: string): Promise<User> {
return this.createQueryBuilder('user').where('user.email = :email', { email }).getOneOrFail()
}
async getUsersIndiced(userIds: number[]): Promise<User[]> {
if (!userIds.length) return []
const users = await this.createQueryBuilder('user')

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { UserSetting } from '../entity/UserSetting'
import { UserSetting } from '@entity/UserSetting'
import { Setting } from '../../graphql/enum/Setting'
import { isStringBoolean } from '../../util/validate'

View File

@ -1,6 +1,6 @@
import { EntityRepository, Repository } from 'typeorm'
import { Order } from '../../graphql/enum/Order'
import { UserTransaction } from '../entity/UserTransaction'
import { UserTransaction } from '@entity/UserTransaction'
@EntityRepository(UserTransaction)
export class UserTransactionRepository extends Repository<UserTransaction> {
@ -17,4 +17,11 @@ export class UserTransactionRepository extends Repository<UserTransaction> {
.offset(offset)
.getManyAndCount()
}
findLastForUser(userId: number): Promise<UserTransaction | undefined> {
return this.createQueryBuilder('userTransaction')
.where('userTransaction.userId = :userId', { userId })
.orderBy('userTransaction.transactionId', 'DESC')
.getOne()
}
}

View File

@ -2,24 +2,25 @@ import { decayFormula, calculateDecay } from './decay'
describe('utils/decay', () => {
describe('decayFormula', () => {
it('has base 0.99999997802044727', async () => {
it('has base 0.99999997802044727', () => {
const amount = 1.0
const seconds = 1
expect(await decayFormula(amount, seconds)).toBe(0.99999997802044727)
expect(decayFormula(amount, seconds)).toBe(0.99999997802044727)
})
// Not sure if the following skiped tests make sence!?
it.skip('has negative decay?', async () => {
it('has negative decay?', async () => {
const amount = -1.0
const seconds = 1
expect(await decayFormula(amount, seconds)).toBe(-0.99999997802044727)
})
it.skip('has correct backward calculation', async () => {
it('has correct backward calculation', async () => {
const amount = 1.0
const seconds = -1
expect(await decayFormula(amount, seconds)).toBe(1.0000000219795533)
})
it.skip('has correct forward calculation', async () => {
const amount = 1.000000219795533
// not possible, nodejs hasn't enough accuracy
it('has correct forward calculation', async () => {
const amount = 1.0 / 0.99999997802044727
const seconds = 1
expect(await decayFormula(amount, seconds)).toBe(1.0)
})
@ -32,7 +33,7 @@ describe('utils/decay', () => {
expect(await calculateDecay(1.0, oneSecondAgo, now)).toBe(0.99999997802044727)
})
it.skip('returns input amount when from and to is the same', async () => {
it('returns input amount when from and to is the same', async () => {
const now = new Date()
expect(await calculateDecay(100.0, now, now)).toBe(100.0)
})

View File

@ -7,6 +7,15 @@ function decayFormula(amount: number, seconds: number): number {
}
async function calculateDecay(amount: number, from: Date, to: Date): Promise<number> {
if (amount === undefined || !from || !to) {
throw new Error('at least one parameter is undefined')
}
if (from === to) {
return amount
}
if (to < from) {
throw new Error('to < from, so the target date is in the past?')
}
// load decay start block
const transactionRepository = getCustomRepository(TransactionRepository)
const decayStartBlock = await transactionRepository.findDecayStartBlock()

View File

@ -0,0 +1,22 @@
import { roundCeilFrom4, roundFloorFrom4, roundCeilFrom2, roundFloorFrom2 } from './round'
describe('utils/round', () => {
it('roundCeilFrom4', () => {
const amount = 11617
expect(roundCeilFrom4(amount)).toBe(1.17)
})
// Not sure if the following skiped tests make sence!?
it('roundFloorFrom4', () => {
const amount = 11617
expect(roundFloorFrom4(amount)).toBe(1.16)
})
it('roundCeilFrom2', () => {
const amount = 1216
expect(roundCeilFrom2(amount)).toBe(13)
})
// not possible, nodejs hasn't enough accuracy
it('roundFloorFrom2', () => {
const amount = 1216
expect(roundFloorFrom2(amount)).toBe(12)
})
})

View File

@ -0,0 +1,31 @@
import { createTransport } from 'nodemailer'
import CONFIG from '../config'
export const sendEMail = async (emailDef: {
from: string
to: string
subject: string
text: string
}): Promise<boolean> => {
if (!CONFIG.EMAIL) {
// eslint-disable-next-line no-console
console.log('Emails are disabled via config')
return false
}
const transporter = createTransport({
host: CONFIG.EMAIL_SMTP_URL,
port: Number(CONFIG.EMAIL_SMTP_PORT),
secure: false, // true for 465, false for other ports
requireTLS: true,
auth: {
user: CONFIG.EMAIL_USERNAME,
pass: CONFIG.EMAIL_PASSWORD,
},
})
const info = await transporter.sendMail(emailDef)
if (!info.messageId) {
throw new Error('error sending notification email, but transaction succeed')
}
return true
}

View File

@ -1,3 +1,8 @@
import { User as dbUser } from '@entity/User'
import { Balance as dbBalance } from '@entity/Balance'
import { getRepository } from 'typeorm'
import { calculateDecay } from './decay'
function isStringBoolean(value: string): boolean {
const lowerValue = value.toLowerCase()
if (lowerValue === 'true' || lowerValue === 'false') {
@ -6,4 +11,18 @@ function isStringBoolean(value: string): boolean {
return false
}
export { isStringBoolean }
function isHexPublicKey(publicKey: string): boolean {
return /^[0-9A-Fa-f]{64}$/i.test(publicKey)
}
async function hasUserAmount(user: dbUser, amount: number): Promise<boolean> {
if (amount < 0) return false
const balanceRepository = getRepository(dbBalance)
const balance = await balanceRepository.findOne({ userId: user.id })
if (!balance) return false
const decay = await calculateDecay(balance.amount, balance.recordDate, new Date())
return decay > amount
}
export { isHexPublicKey, hasUserAmount, isStringBoolean }

View File

@ -46,7 +46,9 @@
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"@entity/*": ["../database/entity/*"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
@ -68,5 +70,12 @@
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
},
"references": [
{
"path": "../database/tsconfig.json",
// add 'prepend' if you want to include the referenced project in your output file
// "prepend": true,
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,8 @@ class ServerUsersController extends AppController
public function initialize()
{
parent::initialize();
$this->Auth->allow(['add', 'edit']);
// uncomment in devmode to add new community server admin user, but don't!!! commit it
//$this->Auth->allow(['add', 'edit']);
$this->Auth->deny('index');
}

View File

@ -17,7 +17,7 @@ phpServer.host = nginx
loginServer.path = http://localhost/account
loginServer.default_locale = de
loginServer.db.host = mariadb
loginServer.db.name = gradido_login
loginServer.db.name = gradido_community
loginServer.db.user = root
loginServer.db.password =
loginServer.db.port = 3306

View File

@ -17,7 +17,7 @@ phpServer.host = nginx
loginServer.path = http://localhost/account
loginServer.default_locale = de
loginServer.db.host = mariadb
loginServer.db.name = gradido_login_test
loginServer.db.name = gradido_community_test
loginServer.db.user = root
loginServer.db.password =
loginServer.db.port = 3306

View File

@ -1,6 +0,0 @@
INSERT INTO `groups` (`id`, `alias`, `name`, `url`, `host`, `home`, `description`) VALUES
(1, 'docker', 'docker gradido group', 'localhost', 'nginx', '/', 'gradido test group for docker and stage2 with blockchain db');

View File

@ -73,7 +73,7 @@ RUN yarn run build
FROM build as test_up
# Run command
CMD /bin/sh -c "yarn run dev_up"
CMD /bin/sh -c "yarn install && yarn run dev_up"
##################################################################################
# TEST RESET #####################################################################
@ -81,7 +81,7 @@ CMD /bin/sh -c "yarn run dev_up"
FROM build as test_reset
# Run command
CMD /bin/sh -c "yarn run dev_reset"
CMD /bin/sh -c "yarn install && yarn run dev_reset"
##################################################################################
# TEST DOWN ######################################################################
@ -89,7 +89,7 @@ CMD /bin/sh -c "yarn run dev_reset"
FROM build as test_down
# Run command
CMD /bin/sh -c "yarn run dev_down"
CMD /bin/sh -c "yarn install && yarn run dev_down"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #

View File

@ -24,6 +24,9 @@ export class TransactionSendCoin extends BaseEntity {
@Column()
amount: number
@Column({ name: 'sender_final_balance' })
senderFinalBalance: number
@OneToOne(() => Transaction)
@JoinColumn({ name: 'transaction_id' })
transaction: Transaction

View File

@ -0,0 +1,26 @@
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()
id: number
@Column({ type: 'binary', length: 32, name: 'public_key' })
pubkey: Buffer
@Column()
email: string
@Column({ name: 'first_name' })
firstName: string
@Column({ name: 'last_name' })
lastName: string
@Column()
username: string
@Column()
disabled: boolean
}

View File

@ -7,9 +7,6 @@ export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
// @ManyToOne(type => Group, group => group.users)
// group: Group;
@Column({ type: 'binary', length: 32, name: 'public_key' })
pubkey: Buffer

View File

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

View File

@ -0,0 +1,26 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
// Moriz: I do not like the idea of having two user tables
@Entity('login_email_opt_in')
export class LoginEmailOptIn extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'user_id' })
userId: number
@Column({ name: 'verification_code', type: 'bigint', unsigned: true, unique: true })
verificationCode: BigInt
@Column({ name: 'email_opt_in_type_id' })
emailOptInTypeId: number
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date
@Column({ name: 'resend_count', default: 0 })
resendCount: number
@Column({ name: 'updated', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date
}

View File

@ -0,0 +1,56 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
// Moriz: I do not like the idea of having two user tables
@Entity('login_users')
export class LoginUser extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ length: 191, unique: true })
email: string
@Column({ name: 'first_name', length: 150 })
firstName: string
@Column({ name: 'last_name', length: 255, default: '' })
lastName: string
@Column({ length: 255, default: '' })
username: string
@Column({ default: '' })
description: string
@Column({ type: 'bigint', default: 0, unsigned: true })
password: BigInt
@Column({ name: 'pubkey', type: 'binary', length: 32, default: null, nullable: true })
pubKey: Buffer
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
privKey: Buffer
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date
@Column({ name: 'email_checked', default: 0 })
emailChecked: boolean
@Column({ name: 'passphrase_shown', default: 0 })
passphraseShown: boolean
@Column({ length: 4, default: 'de' })
language: string
@Column({ default: 0 })
disabled: boolean
@Column({ name: 'group_id', default: 0, unsigned: true })
groupId: number
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
}

View 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({ name: 'user_id', nullable: false })
userId: number
@Column({ type: 'text', name: 'passphrase', nullable: false })
passphrase: string
@Column({ name: 'mnemonic_type', default: -1 })
mnemonicType: number
}

View File

@ -0,0 +1 @@
export { Balance } from './0001-init_db/Balance'

Some files were not shown because too many files have changed in this diff Show More