mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into bugfix_registration
This commit is contained in:
commit
73c9f7c2ab
6
.github/ISSUE_TEMPLATE/release.md
vendored
6
.github/ISSUE_TEMPLATE/release.md
vendored
@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
name: 🏅 Release
|
name: 🎂 Release
|
||||||
about: Define a Release
|
about: Define a Release
|
||||||
labels: release
|
labels: release
|
||||||
title: 🏅 [RELEASE]
|
title: 🎂 [RELEASE]
|
||||||
---
|
---
|
||||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||||
|
|
||||||
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
|
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
|
||||||
<!-- Proceed only if you know what you are doing - have a chat with Project's Team first -->
|
<!-- 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 -->
|
<!-- Describe your Release in detail. Include screenshots and drawings -->
|
||||||
|
|||||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@ -72,7 +72,7 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Backend | Build `production` image
|
- name: Backend | Build `production` image
|
||||||
run: |
|
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
|
docker save "gradido/backend" > /tmp/backend.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
|||||||
152
.github/workflows/test.yml
vendored
152
.github/workflows/test.yml
vendored
@ -29,6 +29,32 @@ jobs:
|
|||||||
name: docker-frontend-test
|
name: docker-frontend-test
|
||||||
path: /tmp/frontend.tar
|
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 #############################################
|
# JOB: DOCKER BUILD TEST BACKEND #############################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -47,7 +73,7 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Backend | Build `test` image
|
- name: Backend | Build `test` image
|
||||||
run: |
|
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
|
docker save "gradido/backend:test" > /tmp/backend.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
@ -147,7 +173,7 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
- name: mariadb | Build `test` image
|
- name: mariadb | Build `test` image
|
||||||
run: |
|
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
|
docker save "gradido/mariadb:test" > /tmp/mariadb.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
@ -240,7 +266,36 @@ jobs:
|
|||||||
run: docker run --rm gradido/frontend:test yarn run lint
|
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:
|
lint_backend:
|
||||||
name: Lint - Backend
|
name: Lint - Backend
|
||||||
@ -344,16 +399,16 @@ jobs:
|
|||||||
report_name: Coverage Frontend
|
report_name: Coverage Frontend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./coverage/lcov.info
|
||||||
min_coverage: 76
|
min_coverage: 86
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: UNIT TEST BACKEND ####################################################
|
# JOB: UNIT TEST ADMIN INTERFACE #############################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
unit_test_backend:
|
unit_test_admin:
|
||||||
name: Unit tests - Backend
|
name: Unit tests - Admin Interface
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_backend]
|
needs: [build_test_admin]
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -363,6 +418,55 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGES #################################################
|
# 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)
|
- name: Download Docker Image (Backend)
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
@ -373,10 +477,11 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
# UNIT TESTS BACKEND #####################################################
|
# UNIT TESTS BACKEND #####################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: backend | Unit tests
|
- name: backend | docker-compose
|
||||||
run: |
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb database
|
||||||
docker run -v ~/coverage:/app/coverage --rm gradido/backend:test yarn run test
|
- name: backend Unit tests | test
|
||||||
cp -r ~/coverage ./coverage
|
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 #################################################
|
# COVERAGE CHECK BACKEND #################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -385,8 +490,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
report_name: Coverage Backend
|
report_name: Coverage Backend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./backend/coverage/lcov.info
|
||||||
min_coverage: 3
|
min_coverage: 38
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -409,10 +514,16 @@ jobs:
|
|||||||
--health-timeout=3s
|
--health-timeout=3s
|
||||||
--health-retries=4
|
--health-retries=4
|
||||||
steps:
|
steps:
|
||||||
- name: Debug service
|
# - name: Debug service
|
||||||
run: echo "$(docker ps)"
|
# run: echo "$(docker ps)"
|
||||||
- name: Debug container choosing script
|
#- name: Debug container choosing script
|
||||||
run: echo "$(docker container ls | grep mariadb | awk '{ print $1 }')"
|
# 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 ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -420,6 +531,11 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
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 ###################################
|
# Build Login-Server Test Docker image ###################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,7 +2,6 @@
|
|||||||
/node_modules/*
|
/node_modules/*
|
||||||
.vscode
|
.vscode
|
||||||
messages.pot
|
messages.pot
|
||||||
.skeema
|
|
||||||
nbproject
|
nbproject
|
||||||
.metadata
|
.metadata
|
||||||
/.env
|
/.env
|
||||||
|
|||||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"streetsidesoftware.code-spell-checker",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
147
CHANGELOG.md
147
CHANGELOG.md
@ -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).
|
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 <b-col> [`#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)
|
- 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)
|
- 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)
|
- feat: Add Pagination Buttons to GDT Transaction List [`#716`](https://github.com/gradido/gradido/pull/716)
|
||||||
|
|||||||
58
README.md
58
README.md
@ -8,34 +8,74 @@ The Gradido model can create global prosperity and peace
|
|||||||
The Corona crisis has fundamentally changed our world within a very short time.
|
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.
|
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
|
## 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?
|
## How to run?
|
||||||
|
|
||||||
1. Clone the repo and pull all submodules
|
### 1. Clone Sources
|
||||||
|
Clone the repo and pull all submodules
|
||||||
```bash
|
```bash
|
||||||
git clone git@github.com:gradido/gradido.git
|
git clone git@github.com:gradido/gradido.git
|
||||||
git submodule update --recursive --init
|
git submodule update --recursive --init
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Run docker compose
|
### 2. Run docker-compose
|
||||||
1. Run docker compose for the debug build
|
Run docker-compose to bring up the development environment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
### Additional Build options
|
||||||
2. Or run docker compose in production build
|
If you want to build for production you can do this aswell:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose -f docker-compose.yml up
|
docker-compose -f docker-compose.yml up
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Services defined in this package
|
||||||
|
|
||||||
|
- [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
|
||||||
|
|
||||||
|
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
|
## Useful Links
|
||||||
|
|
||||||
- [Gradido.net](https://gradido.net/)
|
- [Gradido.net](https://gradido.net/)
|
||||||
|
|||||||
3
admin/.dockerignore
Normal file
3
admin/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
4
admin/.eslintignore
Normal file
4
admin/.eslintignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
coverage
|
||||||
|
**/*.min.js
|
||||||
|
dist
|
||||||
26
admin/.eslintrc.js
Normal file
26
admin/.eslintrc.js
Normal 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
15
admin/.gitattributes
vendored
Normal 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
11
admin/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
.env
|
||||||
|
|
||||||
|
# coverage folder
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# emacs
|
||||||
|
*~
|
||||||
8
admin/.prettierrc.js
Normal file
8
admin/.prettierrc.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
semi: false,
|
||||||
|
printWidth: 100,
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: "all",
|
||||||
|
tabWidth: 2,
|
||||||
|
bracketSpacing: true
|
||||||
|
};
|
||||||
98
admin/Dockerfile
Normal file
98
admin/Dockerfile
Normal 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
26
admin/README.md
Normal 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
4
admin/babel.config.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ['@babel/preset-env'],
|
||||||
|
plugins: ['transform-require-context'],
|
||||||
|
}
|
||||||
25
admin/jest.config.js
Normal file
25
admin/jest.config.js
Normal 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
70
admin/package.json
Normal 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
BIN
admin/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
22
admin/public/index.html
Normal file
22
admin/public/index.html
Normal 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
15
admin/run/server.js
Normal 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
68
admin/src/App.spec.js
Normal 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
9
admin/src/App.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
22
admin/src/components/NotFoundPage.spec.js
Normal file
22
admin/src/components/NotFoundPage.spec.js
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
1261
admin/src/components/NotFoundPage.vue
Executable file
1261
admin/src/components/NotFoundPage.vue
Executable file
File diff suppressed because it is too large
Load Diff
33
admin/src/config/index.js
Normal file
33
admin/src/config/index.js
Normal 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
89
admin/src/i18n.js
Normal 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
|
||||||
1
admin/src/locales/de.json
Normal file
1
admin/src/locales/de.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
admin/src/locales/en.json
Normal file
1
admin/src/locales/en.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
63
admin/src/main.js
Normal file
63
admin/src/main.js
Normal 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
56
admin/src/main.test.js
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
12
admin/src/router/guards.js
Normal file
12
admin/src/router/guards.js
Normal 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
|
||||||
23
admin/src/router/router.js
Normal file
23
admin/src/router/router.js
Normal 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
|
||||||
15
admin/src/router/routes.js
Normal file
15
admin/src/router/routes.js
Normal 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
19
admin/src/store/store.js
Normal 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
|
||||||
15
admin/src/store/store.test.js
Normal file
15
admin/src/store/store.test.js
Normal 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
15
admin/test/testSetup.js
Normal 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
51
admin/vue.config.js
Normal 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
13027
admin/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,16 @@ DB_PORT=3306
|
|||||||
DB_USER=root
|
DB_USER=root
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
DB_DATABASE=gradido_community
|
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_USER=
|
||||||
#KLICKTIPP_PASSWORD=
|
#KLICKTIPP_PASSWORD=
|
||||||
#KLICKTIPP_APIKEY_DE=
|
#KLICKTIPP_APIKEY_DE=
|
||||||
@ -19,3 +29,5 @@ COMMUNITY_NAME=
|
|||||||
COMMUNITY_URL=
|
COMMUNITY_URL=
|
||||||
COMMUNITY_REGISTER_URL=
|
COMMUNITY_REGISTER_URL=
|
||||||
COMMUNITY_DESCRIPTION=
|
COMMUNITY_DESCRIPTION=
|
||||||
|
LOGIN_APP_SECRET=21ffbbc616fe
|
||||||
|
LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
||||||
@ -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 <b-col> [`#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)
|
|
||||||
@ -42,6 +42,8 @@ EXPOSE ${PORT}
|
|||||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||||
WORKDIR ${DOCKER_WORKDIR}
|
WORKDIR ${DOCKER_WORKDIR}
|
||||||
|
|
||||||
|
RUN mkdir -p /database
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
|
# 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
|
# local filesystem which will need a rebuild anyway
|
||||||
|
|
||||||
# Run command
|
# 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)
|
# 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) ########################
|
# BUILD (Does contain all files and is therefore bloated) ########################
|
||||||
##################################################################################
|
##################################################################################
|
||||||
FROM base as build
|
FROM base as build
|
||||||
|
|
||||||
# Copy everything
|
# Copy everything from backend
|
||||||
COPY . .
|
COPY ./backend/ ./
|
||||||
# npm install
|
# Copy everything from database
|
||||||
|
COPY ./database/ ../database/
|
||||||
|
|
||||||
|
# yarn install backend
|
||||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
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
|
RUN yarn run build
|
||||||
|
|
||||||
|
# yarn build database
|
||||||
|
RUN cd ../database && yarn run build
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# TEST ###########################################################################
|
# TEST ###########################################################################
|
||||||
##################################################################################
|
##################################################################################
|
||||||
FROM build as test
|
FROM build as test
|
||||||
|
|
||||||
# Run command
|
# 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) #
|
# 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 "binary"-files from build image
|
||||||
COPY --from=build ${DOCKER_WORKDIR}/build ./build
|
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
|
# 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 static files
|
||||||
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
|
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
|
||||||
# Copy package.json for script definitions (lock file should not be needed)
|
# Copy package.json for script definitions (lock file should not be needed)
|
||||||
|
|||||||
@ -4,4 +4,7 @@ module.exports = {
|
|||||||
preset: 'ts-jest',
|
preset: 'ts-jest',
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
|
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'@entity/(.*)': '<rootDir>/../database/entity/$1',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
7120
backend/package-lock.json
generated
7120
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,11 +13,12 @@
|
|||||||
"start": "node build/index.js",
|
"start": "node build/index.js",
|
||||||
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
|
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
|
||||||
"lint": "eslint . --ext .js,.ts",
|
"lint": "eslint . --ext .js,.ts",
|
||||||
"test": "jest --coverage"
|
"test": "jest --runInBand --coverage "
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
"apollo-server-express": "^2.25.2",
|
"apollo-server-express": "^2.25.2",
|
||||||
|
"apollo-server-testing": "^2.25.2",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"class-validator": "^0.13.1",
|
"class-validator": "^0.13.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@ -26,17 +27,21 @@
|
|||||||
"graphql": "^15.5.1",
|
"graphql": "^15.5.1",
|
||||||
"jest": "^27.2.4",
|
"jest": "^27.2.4",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"libsodium-wrappers": "^0.7.9",
|
"module-alias": "^2.2.2",
|
||||||
"mysql2": "^2.3.0",
|
"mysql2": "^2.3.0",
|
||||||
|
"nodemailer": "^6.6.5",
|
||||||
|
"random-bigint": "^0.0.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"sodium-native": "^3.3.0",
|
||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^27.0.5",
|
||||||
"type-graphql": "^1.1.1",
|
"type-graphql": "^1.1.1",
|
||||||
"typeorm": "^0.2.37"
|
"typeorm": "^0.2.38"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.12",
|
"@types/express": "^4.17.12",
|
||||||
"@types/jsonwebtoken": "^8.5.2",
|
"@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/eslint-plugin": "^4.28.0",
|
||||||
"@typescript-eslint/parser": "^4.28.0",
|
"@typescript-eslint/parser": "^4.28.0",
|
||||||
"eslint": "^7.29.0",
|
"eslint": "^7.29.0",
|
||||||
@ -50,5 +55,8 @@
|
|||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^4.3.4"
|
"typescript": "^4.3.4"
|
||||||
|
},
|
||||||
|
"_moduleAliases": {
|
||||||
|
"@entity": "../database/build/entity"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,9 +39,25 @@ const community = {
|
|||||||
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
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
|
// This is needed by graphql-directive-auth
|
||||||
process.env.APP_SECRET = server.JWT_SECRET
|
process.env.APP_SECRET = server.JWT_SECRET
|
||||||
|
|
||||||
const CONFIG = { ...server, ...database, ...klicktipp, ...community }
|
const CONFIG = { ...server, ...database, ...klicktipp, ...community, ...email, ...loginServer }
|
||||||
|
|
||||||
export default CONFIG
|
export default CONFIG
|
||||||
|
|||||||
2048
backend/src/config/mnemonic.english.txt
Normal file
2048
backend/src/config/mnemonic.english.txt
Normal file
File diff suppressed because it is too large
Load Diff
2048
backend/src/config/mnemonic.words_ulf.encoding.txt
Normal file
2048
backend/src/config/mnemonic.words_ulf.encoding.txt
Normal file
File diff suppressed because it is too large
Load Diff
2048
backend/src/config/mnemonic.words_ulf.txt
Normal file
2048
backend/src/config/mnemonic.words_ulf.txt
Normal file
File diff suppressed because it is too large
Load Diff
2048
backend/src/config/mnemonic.words_ulf_org.txt
Normal file
2048
backend/src/config/mnemonic.words_ulf_org.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,4 @@ import { ArgsType, Field } from 'type-graphql'
|
|||||||
export default class CheckUsernameArgs {
|
export default class CheckUsernameArgs {
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
username: string
|
username: string
|
||||||
|
|
||||||
@Field(() => Number, { nullable: true })
|
|
||||||
groupId?: number
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
import { AuthChecker } from 'type-graphql'
|
import { AuthChecker } from 'type-graphql'
|
||||||
|
|
||||||
import CONFIG from '../../config'
|
|
||||||
import { apiGet } from '../../apis/HttpRequest'
|
|
||||||
|
|
||||||
import decode from '../../jwt/decode'
|
import decode from '../../jwt/decode'
|
||||||
import encode from '../../jwt/encode'
|
import encode from '../../jwt/encode'
|
||||||
|
|
||||||
@ -13,15 +10,9 @@ const isAuthorized: AuthChecker<any> = async (
|
|||||||
) => {
|
) => {
|
||||||
if (context.token) {
|
if (context.token) {
|
||||||
const decoded = decode(context.token)
|
const decoded = decode(context.token)
|
||||||
if (decoded.sessionId && decoded.sessionId !== 0) {
|
context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
|
||||||
const result = await apiGet(
|
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
|
||||||
`${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`,
|
return true
|
||||||
)
|
|
||||||
context.sessionId = decoded.sessionId
|
|
||||||
context.pubKey = decoded.pubKey
|
|
||||||
context.setHeaders.push({ key: 'token', value: encode(decoded.sessionId, decoded.pubKey) })
|
|
||||||
return result.success
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new Error('401 Unauthorized')
|
throw new Error('401 Unauthorized')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
@ -10,7 +10,8 @@ export class User {
|
|||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number
|
id: number
|
||||||
*/
|
*/
|
||||||
constructor(json: any) {
|
constructor(json?: any) {
|
||||||
|
if (json) {
|
||||||
this.email = json.email
|
this.email = json.email
|
||||||
this.firstName = json.first_name
|
this.firstName = json.first_name
|
||||||
this.lastName = json.last_name
|
this.lastName = json.last_name
|
||||||
@ -20,6 +21,7 @@ export class User {
|
|||||||
this.language = json.language
|
this.language = json.language
|
||||||
this.publisherId = json.publisher_id
|
this.publisherId = json.publisher_id
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
email: string
|
email: string
|
||||||
|
|||||||
123
backend/src/graphql/resolver/CommunityResolver.test.ts
Normal file
123
backend/src/graphql/resolver/CommunityResolver.test.ts
Normal 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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,10 +1,12 @@
|
|||||||
|
/* eslint-disable new-cap */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
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 CONFIG from '../../config'
|
||||||
|
import { sendEMail } from '../../util/sendEMail'
|
||||||
|
|
||||||
import { Transaction } from '../model/Transaction'
|
import { Transaction } from '../model/Transaction'
|
||||||
import { TransactionList } from '../model/TransactionList'
|
import { TransactionList } from '../model/TransactionList'
|
||||||
@ -19,15 +21,185 @@ import { UserRepository } from '../../typeorm/repository/User'
|
|||||||
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
||||||
import { TransactionRepository } from '../../typeorm/repository/Transaction'
|
import { TransactionRepository } from '../../typeorm/repository/Transaction'
|
||||||
|
|
||||||
import { User as dbUser } from '../../typeorm/entity/User'
|
import { User as dbUser } from '@entity/User'
|
||||||
import { UserTransaction as dbUserTransaction } from '../../typeorm/entity/UserTransaction'
|
import { UserTransaction as dbUserTransaction } from '@entity/UserTransaction'
|
||||||
import { Transaction as dbTransaction } from '../../typeorm/entity/Transaction'
|
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 { apiPost } from '../../apis/HttpRequest'
|
||||||
import { roundFloorFrom4 } from '../../util/round'
|
import { roundFloorFrom4, roundCeilFrom4 } from '../../util/round'
|
||||||
import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
|
import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
|
||||||
import { TransactionTypeId } from '../enum/TransactionTypeId'
|
import { TransactionTypeId } from '../enum/TransactionTypeId'
|
||||||
import { TransactionType } from '../enum/TransactionType'
|
import { TransactionType } from '../enum/TransactionType'
|
||||||
|
import { hasUserAmount, isHexPublicKey } from '../../util/validate'
|
||||||
|
import { 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
|
// Helper function
|
||||||
async function calculateAndAddDecayTransactions(
|
async function calculateAndAddDecayTransactions(
|
||||||
@ -150,7 +322,7 @@ async function calculateAndAddDecayTransactions(
|
|||||||
|
|
||||||
const decayTransaction = new Transaction()
|
const decayTransaction = new Transaction()
|
||||||
decayTransaction.type = 'decay'
|
decayTransaction.type = 'decay'
|
||||||
decayTransaction.balance = roundFloorFrom4(balance)
|
decayTransaction.balance = roundCeilFrom4(balance)
|
||||||
decayTransaction.decayDuration = decay.decayDuration
|
decayTransaction.decayDuration = decay.decayDuration
|
||||||
decayTransaction.decayStart = decay.decayStart
|
decayTransaction.decayStart = decay.decayStart
|
||||||
decayTransaction.decayEnd = decay.decayEnd
|
decayTransaction.decayEnd = decay.decayEnd
|
||||||
@ -210,6 +382,87 @@ async function listTransactions(
|
|||||||
return transactionList
|
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()
|
@Resolver()
|
||||||
export class TransactionResolver {
|
export class TransactionResolver {
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@ -252,19 +505,148 @@ export class TransactionResolver {
|
|||||||
@Args() { email, amount, memo }: TransactionSendArgs,
|
@Args() { email, amount, memo }: TransactionSendArgs,
|
||||||
@Ctx() context: any,
|
@Ctx() context: any,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const payload = {
|
// TODO this is subject to replay attacks
|
||||||
session_id: context.sessionId,
|
// validate sender user (logged in)
|
||||||
target_email: email,
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
amount: amount * 10000,
|
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
memo,
|
if (senderUser.pubkey.length !== 32) {
|
||||||
auto_sign: true,
|
throw new Error('invalid sender public key')
|
||||||
transaction_type: 'transfer',
|
|
||||||
blockchain_type: 'mysql',
|
|
||||||
}
|
}
|
||||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'createTransaction', payload)
|
if (!hasUserAmount(senderUser, amount)) {
|
||||||
if (!result.success) {
|
throw new Error("user hasn't enough GDD")
|
||||||
throw new Error(result.data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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'
|
return 'success'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* 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 { 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 CONFIG from '../../config'
|
||||||
import { CheckUsernameResponse } from '../model/CheckUsernameResponse'
|
|
||||||
import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
|
import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
|
||||||
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
|
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
|
||||||
import { UpdateUserInfosResponse } from '../model/UpdateUserInfosResponse'
|
|
||||||
import { User } from '../model/User'
|
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 encode from '../../jwt/encode'
|
||||||
import ChangePasswordArgs from '../arg/ChangePasswordArgs'
|
import ChangePasswordArgs from '../arg/ChangePasswordArgs'
|
||||||
import CheckUsernameArgs from '../arg/CheckUsernameArgs'
|
import CheckUsernameArgs from '../arg/CheckUsernameArgs'
|
||||||
@ -22,10 +21,176 @@ import {
|
|||||||
klicktippNewsletterStateMiddleware,
|
klicktippNewsletterStateMiddleware,
|
||||||
} from '../../middleware/klicktippMiddleware'
|
} from '../../middleware/klicktippMiddleware'
|
||||||
import { CheckEmailResponse } from '../model/CheckEmailResponse'
|
import { CheckEmailResponse } from '../model/CheckEmailResponse'
|
||||||
import { getCustomRepository } from 'typeorm'
|
|
||||||
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
|
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
|
||||||
|
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
||||||
import { Setting } from '../enum/Setting'
|
import { Setting } from '../enum/Setting'
|
||||||
import { UserRepository } from '../../typeorm/repository/User'
|
import { UserRepository } from '../../typeorm/repository/User'
|
||||||
|
import { LoginUser } from '@entity/LoginUser'
|
||||||
|
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
||||||
|
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||||
|
import { 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()
|
@Resolver()
|
||||||
export class UserResolver {
|
export class UserResolver {
|
||||||
@ -36,35 +201,34 @@ export class UserResolver {
|
|||||||
@Ctx() context: any,
|
@Ctx() context: any,
|
||||||
): Promise<User> {
|
): Promise<User> {
|
||||||
email = email.trim().toLowerCase()
|
email = email.trim().toLowerCase()
|
||||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
|
// const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
|
||||||
|
// UnsecureLogin
|
||||||
// if there is no user, throw an authentication error
|
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||||
if (!result.success) {
|
const loginUser = await loginUserRepository.findByEmail(email).catch(() => {
|
||||||
throw new Error(result.data)
|
throw new Error('No user with this credentials')
|
||||||
}
|
|
||||||
|
|
||||||
context.setHeaders.push({
|
|
||||||
key: 'token',
|
|
||||||
value: encode(result.data.session_id, result.data.user.public_hex),
|
|
||||||
})
|
})
|
||||||
const user = new User(result.data.user)
|
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
|
||||||
// Hack: Database Field is not validated properly and not nullable
|
const loginUserPassword = BigInt(loginUser.password.toString())
|
||||||
if (user.publisherId === 0) {
|
if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) {
|
||||||
user.publisherId = undefined
|
throw new Error('No user with this credentials')
|
||||||
}
|
}
|
||||||
user.hasElopage = result.data.hasElopage
|
// TODO: If user has no pubKey Create it again and update user.
|
||||||
// read additional settings from settings table
|
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
let userEntity: void | DbUser
|
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 = new DbUser()
|
||||||
userEntity.firstName = user.firstName
|
userEntity.firstName = loginUser.firstName
|
||||||
userEntity.lastName = user.lastName
|
userEntity.lastName = loginUser.lastName
|
||||||
userEntity.username = user.username
|
userEntity.username = loginUser.username
|
||||||
userEntity.email = user.email
|
userEntity.email = loginUser.email
|
||||||
userEntity.pubkey = Buffer.from(fromHex(user.pubkey))
|
userEntity.pubkey = loginUser.pubKey
|
||||||
|
|
||||||
userEntity.save().catch(() => {
|
userRepository.save(userEntity).catch(() => {
|
||||||
throw new Error('error by save userEntity')
|
throw new Error('error by save userEntity')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -72,15 +236,28 @@ export class UserResolver {
|
|||||||
throw new Error('error with cannot happen')
|
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) {
|
if (!user.hasElopage && publisherId) {
|
||||||
user.publisherId = publisherId
|
user.publisherId = publisherId
|
||||||
await this.updateUserInfos(
|
// TODO: Check if we can use updateUserInfos
|
||||||
{ publisherId },
|
// await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey })
|
||||||
{ sessionId: result.data.session_id, pubKey: result.data.user.public_hex },
|
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||||
)
|
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
|
||||||
|
loginUser.publisherId = publisherId
|
||||||
|
loginUserRepository.save(loginUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// coinAnimation
|
||||||
const userSettingRepository = getCustomRepository(UserSettingRepository)
|
const userSettingRepository = getCustomRepository(UserSettingRepository)
|
||||||
const coinanimation = await userSettingRepository
|
const coinanimation = await userSettingRepository
|
||||||
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
|
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
|
||||||
@ -88,6 +265,12 @@ export class UserResolver {
|
|||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
})
|
})
|
||||||
user.coinanimation = coinanimation
|
user.coinanimation = coinanimation
|
||||||
|
|
||||||
|
context.setHeaders.push({
|
||||||
|
key: 'token',
|
||||||
|
value: encode(loginUser.pubKey),
|
||||||
|
})
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,46 +291,152 @@ export class UserResolver {
|
|||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@Query(() => String)
|
@Query(() => String)
|
||||||
async logout(@Ctx() context: any): Promise<string> {
|
async logout(): Promise<boolean> {
|
||||||
const payload = { session_id: context.sessionId }
|
// TODO: We dont need this anymore, but might need this in the future in oder to invalidate a valid JWT-Token.
|
||||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'logout', payload)
|
// Furthermore this hook can be useful for tracking user behaviour (did he logout or not? Warn him if he didn't on next login)
|
||||||
if (!result.success) {
|
// The functionality is fully client side - the client just needs to delete his token with the current implementation.
|
||||||
throw new Error(result.data)
|
// 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 'success'
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => String)
|
@Mutation(() => String)
|
||||||
async createUser(
|
async createUser(
|
||||||
@Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs,
|
@Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const payload = {
|
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
|
||||||
email,
|
// default int publisher_id = 0;
|
||||||
first_name: firstName,
|
|
||||||
last_name: lastName,
|
// Validate Language (no throw)
|
||||||
password,
|
if (!isLanguage(language)) {
|
||||||
emailType: 2,
|
language = DEFAULT_LANGUAGE
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = new User(result.data.user)
|
// Validate Password
|
||||||
const dbuser = new DbUser()
|
if (!isPassword(password)) {
|
||||||
dbuser.pubkey = Buffer.from(fromHex(user.pubkey))
|
throw new Error(
|
||||||
dbuser.email = user.email
|
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||||
dbuser.firstName = user.firstName
|
)
|
||||||
dbuser.lastName = user.lastName
|
}
|
||||||
dbuser.username = user.username
|
|
||||||
|
|
||||||
dbuser.save().catch(() => {
|
// 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')
|
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'
|
return 'success'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +474,7 @@ export class UserResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@Mutation(() => UpdateUserInfosResponse)
|
@Mutation(() => Boolean)
|
||||||
async updateUserInfos(
|
async updateUserInfos(
|
||||||
@Args()
|
@Args()
|
||||||
{
|
{
|
||||||
@ -200,90 +489,120 @@ export class UserResolver {
|
|||||||
coinanimation,
|
coinanimation,
|
||||||
}: UpdateUserInfosArgs,
|
}: UpdateUserInfosArgs,
|
||||||
@Ctx() context: any,
|
@Ctx() context: any,
|
||||||
): Promise<UpdateUserInfosResponse> {
|
): Promise<boolean> {
|
||||||
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
|
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
|
|
||||||
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)
|
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
let userEntityChanged = false
|
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||||
if (firstName) {
|
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
|
||||||
userEntity.firstName = firstName
|
|
||||||
userEntityChanged = true
|
|
||||||
}
|
|
||||||
if (lastName) {
|
|
||||||
userEntity.lastName = lastName
|
|
||||||
userEntityChanged = true
|
|
||||||
}
|
|
||||||
if (username) {
|
if (username) {
|
||||||
userEntity.username = username
|
throw new Error('change username currently not supported!')
|
||||||
userEntityChanged = true
|
// 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 (userEntityChanged) {
|
|
||||||
userEntity.save().catch((error) => {
|
|
||||||
throw new Error(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
if (lastName) {
|
||||||
userSettingRepository
|
loginUser.lastName = lastName
|
||||||
|
userEntity.lastName = lastName
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
loginUser.description = description
|
||||||
|
}
|
||||||
|
|
||||||
|
if (language) {
|
||||||
|
if (!isLanguage(language)) {
|
||||||
|
throw new Error(`"${language}" isn't a valid language`)
|
||||||
|
}
|
||||||
|
loginUser.language = language
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
.setOrUpdate(userEntity.id, Setting.COIN_ANIMATION, coinanimation.toString())
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw new Error(error)
|
throw new Error('error saving coinanimation: ' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryRunner.manager.save(loginUser).catch((error) => {
|
||||||
|
throw new Error('error saving loginUser: ' + error)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response) {
|
await queryRunner.manager.save(userEntity).catch((error) => {
|
||||||
response = new UpdateUserInfosResponse({ valid_values: 1 })
|
throw new Error('error saving user: ' + error)
|
||||||
} else {
|
})
|
||||||
response.validValues++
|
|
||||||
}
|
await queryRunner.commitTransaction()
|
||||||
}
|
} catch (e) {
|
||||||
if (!response) {
|
await queryRunner.rollbackTransaction()
|
||||||
throw new Error('no valid response')
|
throw e
|
||||||
}
|
} finally {
|
||||||
return response
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query(() => CheckUsernameResponse)
|
return true
|
||||||
async checkUsername(
|
}
|
||||||
@Args() { username, groupId = 1 }: CheckUsernameArgs,
|
|
||||||
): Promise<CheckUsernameResponse> {
|
@Query(() => Boolean)
|
||||||
const response = await apiGet(
|
async checkUsername(@Args() { username }: CheckUsernameArgs): Promise<boolean> {
|
||||||
CONFIG.LOGIN_API_URL + `checkUsername?username=${username}&group_id=${groupId}`,
|
// Username empty?
|
||||||
)
|
if (username === '') {
|
||||||
if (!response.success) throw new Error(response.data)
|
throw new Error('Username must be set.')
|
||||||
return new CheckUsernameResponse(response.data)
|
}
|
||||||
|
|
||||||
|
// 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)
|
@Query(() => CheckEmailResponse)
|
||||||
@ -298,12 +617,17 @@ export class UserResolver {
|
|||||||
return new CheckEmailResponse(result.data)
|
return new CheckEmailResponse(result.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authorized()
|
||||||
@Query(() => Boolean)
|
@Query(() => Boolean)
|
||||||
async hasElopage(@Ctx() context: any): Promise<boolean> {
|
async hasElopage(@Ctx() context: any): Promise<boolean> {
|
||||||
const result = await apiGet(CONFIG.LOGIN_API_URL + 'hasElopage?session_id=' + context.sessionId)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
if (!result.success) {
|
const userEntity = await userRepository.findByPubkeyHex(context.pubKey).catch()
|
||||||
throw new Error(result.data)
|
if (!userEntity) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return result.data.hasElopage
|
|
||||||
|
const loginElopageBuysRepository = getCustomRepository(LoginElopageBuysRepository)
|
||||||
|
const elopageBuyCount = await loginElopageBuysRepository.count({ payerEmail: userEntity.email })
|
||||||
|
return elopageBuyCount > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import isAuthorized from './directive/isAuthorized'
|
|||||||
|
|
||||||
const schema = async (): Promise<GraphQLSchema> => {
|
const schema = async (): Promise<GraphQLSchema> => {
|
||||||
return buildSchema({
|
return buildSchema({
|
||||||
resolvers: [path.join(__dirname, 'resolver', `*.{js,ts}`)],
|
resolvers: [path.join(__dirname, 'resolver', `!(*.test).{js,ts}`)],
|
||||||
authChecker: isAuthorized,
|
authChecker: isAuthorized,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,63 +1,14 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import 'reflect-metadata'
|
import createServer from './server/createServer'
|
||||||
import express from 'express'
|
|
||||||
import { ApolloServer } from 'apollo-server-express'
|
|
||||||
|
|
||||||
// config
|
// config
|
||||||
import CONFIG from './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() {
|
async function main() {
|
||||||
// open mysql connection
|
const { app } = await createServer()
|
||||||
const con = await connection()
|
|
||||||
if (!con || !con.isConnected) {
|
|
||||||
throw new Error(`Couldn't open connection to database`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for correct database version
|
app.listen(CONFIG.PORT, () => {
|
||||||
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, () => {
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Server is running at http://localhost:${CONFIG.PORT}`)
|
console.log(`Server is running at http://localhost:${CONFIG.PORT}`)
|
||||||
if (CONFIG.GRAPHIQL) {
|
if (CONFIG.GRAPHIQL) {
|
||||||
|
|||||||
@ -2,27 +2,22 @@ import jwt, { JwtPayload } from 'jsonwebtoken'
|
|||||||
import CONFIG from '../config/'
|
import CONFIG from '../config/'
|
||||||
|
|
||||||
interface CustomJwtPayload extends JwtPayload {
|
interface CustomJwtPayload extends JwtPayload {
|
||||||
sessionId: number
|
|
||||||
pubKey: Buffer
|
pubKey: Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
type DecodedJwt = {
|
type DecodedJwt = {
|
||||||
token: string
|
token: string
|
||||||
sessionId: number
|
|
||||||
pubKey: Buffer
|
pubKey: Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (token: string): DecodedJwt => {
|
export default (token: string): DecodedJwt => {
|
||||||
if (!token) throw new Error('401 Unauthorized')
|
if (!token) throw new Error('401 Unauthorized')
|
||||||
let sessionId = null
|
|
||||||
let pubKey = null
|
let pubKey = null
|
||||||
try {
|
try {
|
||||||
const decoded = <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
|
const decoded = <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
|
||||||
sessionId = decoded.sessionId
|
|
||||||
pubKey = decoded.pubKey
|
pubKey = decoded.pubKey
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
sessionId,
|
|
||||||
pubKey,
|
pubKey,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -5,10 +5,9 @@ import jwt from 'jsonwebtoken'
|
|||||||
import CONFIG from '../config/'
|
import CONFIG from '../config/'
|
||||||
|
|
||||||
// Generate an Access Token
|
// Generate an Access Token
|
||||||
export default function encode(sessionId: number, pubKey: Buffer): string {
|
export default function encode(pubKey: Buffer): string {
|
||||||
const token = jwt.sign({ sessionId, pubKey }, CONFIG.JWT_SECRET, {
|
const token = jwt.sign({ pubKey }, CONFIG.JWT_SECRET, {
|
||||||
expiresIn: CONFIG.JWT_EXPIRES_IN,
|
expiresIn: CONFIG.JWT_EXPIRES_IN,
|
||||||
subject: sessionId.toString(),
|
|
||||||
})
|
})
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|||||||
64
backend/src/server/createServer.ts
Normal file
64
backend/src/server/createServer.ts
Normal 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
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { createConnection, Connection } from 'typeorm'
|
import { createConnection, Connection } from 'typeorm'
|
||||||
import CONFIG from '../config'
|
import CONFIG from '../config'
|
||||||
import path from 'path'
|
import { entities } from '@entity/index'
|
||||||
|
|
||||||
const connection = async (): Promise<Connection | null> => {
|
const connection = async (): Promise<Connection | null> => {
|
||||||
let con = null
|
let con = null
|
||||||
@ -13,10 +13,13 @@ const connection = async (): Promise<Connection | null> => {
|
|||||||
username: CONFIG.DB_USER,
|
username: CONFIG.DB_USER,
|
||||||
password: CONFIG.DB_PASSWORD,
|
password: CONFIG.DB_PASSWORD,
|
||||||
database: CONFIG.DB_DATABASE,
|
database: CONFIG.DB_DATABASE,
|
||||||
entities: [path.join(__dirname, 'entity', '*.{ts,js}')],
|
entities,
|
||||||
synchronize: false,
|
synchronize: false,
|
||||||
})
|
})
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
return con
|
return con
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { Migration } from './entity/Migration'
|
import { getRepository } from 'typeorm'
|
||||||
|
import { Migration } from '@entity/Migration'
|
||||||
|
|
||||||
const getDBVersion = async (): Promise<string | null> => {
|
const getDBVersion = async (): Promise<string | null> => {
|
||||||
try {
|
try {
|
||||||
const dbVersion = await Migration.findOne({ order: { version: 'DESC' } })
|
const dbVersion = await getRepository(Migration).findOne({ order: { version: 'DESC' } })
|
||||||
return dbVersion ? dbVersion.fileName : null
|
return dbVersion ? dbVersion.fileName : null
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { EntityRepository, Repository } from 'typeorm'
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
import { Balance } from '../entity/Balance'
|
import { Balance } from '@entity/Balance'
|
||||||
|
|
||||||
@EntityRepository(Balance)
|
@EntityRepository(Balance)
|
||||||
export class BalanceRepository extends Repository<Balance> {
|
export class BalanceRepository extends Repository<Balance> {
|
||||||
|
|||||||
5
backend/src/typeorm/repository/LoginElopageBuys.ts
Normal file
5
backend/src/typeorm/repository/LoginElopageBuys.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
|
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
|
||||||
|
|
||||||
|
@EntityRepository(LoginElopageBuys)
|
||||||
|
export class LoginElopageBuysRepository extends Repository<LoginElopageBuys> {}
|
||||||
5
backend/src/typeorm/repository/LoginEmailOptIn.ts
Normal file
5
backend/src/typeorm/repository/LoginEmailOptIn.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
|
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||||
|
|
||||||
|
@EntityRepository(LoginEmailOptIn)
|
||||||
|
export class LoginEmailOptInRepository extends Repository<LoginEmailOptIn> {}
|
||||||
11
backend/src/typeorm/repository/LoginUser.ts
Normal file
11
backend/src/typeorm/repository/LoginUser.ts
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
5
backend/src/typeorm/repository/LoginUserBackup.ts
Normal file
5
backend/src/typeorm/repository/LoginUserBackup.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
|
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
||||||
|
|
||||||
|
@EntityRepository(LoginUserBackup)
|
||||||
|
export class LoginUserBackupRepository extends Repository<LoginUserBackup> {}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { EntityRepository, Repository } from 'typeorm'
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
import { Transaction } from '../entity/Transaction'
|
import { Transaction } from '@entity/Transaction'
|
||||||
|
|
||||||
@EntityRepository(Transaction)
|
@EntityRepository(Transaction)
|
||||||
export class TransactionRepository extends Repository<Transaction> {
|
export class TransactionRepository extends Repository<Transaction> {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { EntityRepository, Repository } from 'typeorm'
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
import { User } from '../entity/User'
|
import { User } from '@entity/User'
|
||||||
|
|
||||||
@EntityRepository(User)
|
@EntityRepository(User)
|
||||||
export class UserRepository extends Repository<User> {
|
export class UserRepository extends Repository<User> {
|
||||||
@ -9,6 +9,15 @@ export class UserRepository extends Repository<User> {
|
|||||||
.getOneOrFail()
|
.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[]> {
|
async getUsersIndiced(userIds: number[]): Promise<User[]> {
|
||||||
if (!userIds.length) return []
|
if (!userIds.length) return []
|
||||||
const users = await this.createQueryBuilder('user')
|
const users = await this.createQueryBuilder('user')
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { EntityRepository, Repository } from 'typeorm'
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
import { UserSetting } from '../entity/UserSetting'
|
import { UserSetting } from '@entity/UserSetting'
|
||||||
import { Setting } from '../../graphql/enum/Setting'
|
import { Setting } from '../../graphql/enum/Setting'
|
||||||
import { isStringBoolean } from '../../util/validate'
|
import { isStringBoolean } from '../../util/validate'
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { EntityRepository, Repository } from 'typeorm'
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
import { Order } from '../../graphql/enum/Order'
|
import { Order } from '../../graphql/enum/Order'
|
||||||
import { UserTransaction } from '../entity/UserTransaction'
|
import { UserTransaction } from '@entity/UserTransaction'
|
||||||
|
|
||||||
@EntityRepository(UserTransaction)
|
@EntityRepository(UserTransaction)
|
||||||
export class UserTransactionRepository extends Repository<UserTransaction> {
|
export class UserTransactionRepository extends Repository<UserTransaction> {
|
||||||
@ -17,4 +17,11 @@ export class UserTransactionRepository extends Repository<UserTransaction> {
|
|||||||
.offset(offset)
|
.offset(offset)
|
||||||
.getManyAndCount()
|
.getManyAndCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findLastForUser(userId: number): Promise<UserTransaction | undefined> {
|
||||||
|
return this.createQueryBuilder('userTransaction')
|
||||||
|
.where('userTransaction.userId = :userId', { userId })
|
||||||
|
.orderBy('userTransaction.transactionId', 'DESC')
|
||||||
|
.getOne()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,24 +2,25 @@ import { decayFormula, calculateDecay } from './decay'
|
|||||||
|
|
||||||
describe('utils/decay', () => {
|
describe('utils/decay', () => {
|
||||||
describe('decayFormula', () => {
|
describe('decayFormula', () => {
|
||||||
it('has base 0.99999997802044727', async () => {
|
it('has base 0.99999997802044727', () => {
|
||||||
const amount = 1.0
|
const amount = 1.0
|
||||||
const seconds = 1
|
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!?
|
// 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 amount = -1.0
|
||||||
const seconds = 1
|
const seconds = 1
|
||||||
expect(await decayFormula(amount, seconds)).toBe(-0.99999997802044727)
|
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 amount = 1.0
|
||||||
const seconds = -1
|
const seconds = -1
|
||||||
expect(await decayFormula(amount, seconds)).toBe(1.0000000219795533)
|
expect(await decayFormula(amount, seconds)).toBe(1.0000000219795533)
|
||||||
})
|
})
|
||||||
it.skip('has correct forward calculation', async () => {
|
// not possible, nodejs hasn't enough accuracy
|
||||||
const amount = 1.000000219795533
|
it('has correct forward calculation', async () => {
|
||||||
|
const amount = 1.0 / 0.99999997802044727
|
||||||
const seconds = 1
|
const seconds = 1
|
||||||
expect(await decayFormula(amount, seconds)).toBe(1.0)
|
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)
|
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()
|
const now = new Date()
|
||||||
expect(await calculateDecay(100.0, now, now)).toBe(100.0)
|
expect(await calculateDecay(100.0, now, now)).toBe(100.0)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -7,6 +7,15 @@ function decayFormula(amount: number, seconds: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function calculateDecay(amount: number, from: Date, to: Date): Promise<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
|
// load decay start block
|
||||||
const transactionRepository = getCustomRepository(TransactionRepository)
|
const transactionRepository = getCustomRepository(TransactionRepository)
|
||||||
const decayStartBlock = await transactionRepository.findDecayStartBlock()
|
const decayStartBlock = await transactionRepository.findDecayStartBlock()
|
||||||
|
|||||||
22
backend/src/util/round.test.ts
Normal file
22
backend/src/util/round.test.ts
Normal 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)
|
||||||
|
})
|
||||||
|
})
|
||||||
31
backend/src/util/sendEMail.ts
Normal file
31
backend/src/util/sendEMail.ts
Normal 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
|
||||||
|
}
|
||||||
@ -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 {
|
function isStringBoolean(value: string): boolean {
|
||||||
const lowerValue = value.toLowerCase()
|
const lowerValue = value.toLowerCase()
|
||||||
if (lowerValue === 'true' || lowerValue === 'false') {
|
if (lowerValue === 'true' || lowerValue === 'false') {
|
||||||
@ -6,4 +11,18 @@ function isStringBoolean(value: string): boolean {
|
|||||||
return false
|
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 }
|
||||||
|
|||||||
@ -46,7 +46,9 @@
|
|||||||
/* Module Resolution Options */
|
/* Module Resolution Options */
|
||||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
// "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. */
|
// "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. */
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
@ -68,5 +70,12 @@
|
|||||||
/* Advanced Options */
|
/* Advanced Options */
|
||||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
"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,
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
2681
backend/yarn.lock
2681
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,8 @@ class ServerUsersController extends AppController
|
|||||||
public function initialize()
|
public function initialize()
|
||||||
{
|
{
|
||||||
parent::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');
|
$this->Auth->deny('index');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ phpServer.host = nginx
|
|||||||
loginServer.path = http://localhost/account
|
loginServer.path = http://localhost/account
|
||||||
loginServer.default_locale = de
|
loginServer.default_locale = de
|
||||||
loginServer.db.host = mariadb
|
loginServer.db.host = mariadb
|
||||||
loginServer.db.name = gradido_login
|
loginServer.db.name = gradido_community
|
||||||
loginServer.db.user = root
|
loginServer.db.user = root
|
||||||
loginServer.db.password =
|
loginServer.db.password =
|
||||||
loginServer.db.port = 3306
|
loginServer.db.port = 3306
|
||||||
|
|||||||
@ -17,7 +17,7 @@ phpServer.host = nginx
|
|||||||
loginServer.path = http://localhost/account
|
loginServer.path = http://localhost/account
|
||||||
loginServer.default_locale = de
|
loginServer.default_locale = de
|
||||||
loginServer.db.host = mariadb
|
loginServer.db.host = mariadb
|
||||||
loginServer.db.name = gradido_login_test
|
loginServer.db.name = gradido_community_test
|
||||||
loginServer.db.user = root
|
loginServer.db.user = root
|
||||||
loginServer.db.password =
|
loginServer.db.password =
|
||||||
loginServer.db.port = 3306
|
loginServer.db.port = 3306
|
||||||
|
|||||||
@ -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');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ RUN yarn run build
|
|||||||
FROM build as test_up
|
FROM build as test_up
|
||||||
|
|
||||||
# Run command
|
# Run command
|
||||||
CMD /bin/sh -c "yarn run dev_up"
|
CMD /bin/sh -c "yarn install && yarn run dev_up"
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# TEST RESET #####################################################################
|
# TEST RESET #####################################################################
|
||||||
@ -81,7 +81,7 @@ CMD /bin/sh -c "yarn run dev_up"
|
|||||||
FROM build as test_reset
|
FROM build as test_reset
|
||||||
|
|
||||||
# Run command
|
# Run command
|
||||||
CMD /bin/sh -c "yarn run dev_reset"
|
CMD /bin/sh -c "yarn install && yarn run dev_reset"
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# TEST DOWN ######################################################################
|
# TEST DOWN ######################################################################
|
||||||
@ -89,7 +89,7 @@ CMD /bin/sh -c "yarn run dev_reset"
|
|||||||
FROM build as test_down
|
FROM build as test_down
|
||||||
|
|
||||||
# Run command
|
# 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) #
|
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
|
||||||
|
|||||||
@ -24,6 +24,9 @@ export class TransactionSendCoin extends BaseEntity {
|
|||||||
@Column()
|
@Column()
|
||||||
amount: number
|
amount: number
|
||||||
|
|
||||||
|
@Column({ name: 'sender_final_balance' })
|
||||||
|
senderFinalBalance: number
|
||||||
|
|
||||||
@OneToOne(() => Transaction)
|
@OneToOne(() => Transaction)
|
||||||
@JoinColumn({ name: 'transaction_id' })
|
@JoinColumn({ name: 'transaction_id' })
|
||||||
transaction: Transaction
|
transaction: Transaction
|
||||||
26
database/entity/0001-init_db/User.ts
Normal file
26
database/entity/0001-init_db/User.ts
Normal 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
|
||||||
|
}
|
||||||
@ -7,9 +7,6 @@ export class User extends BaseEntity {
|
|||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
// @ManyToOne(type => Group, group => group.users)
|
|
||||||
// group: Group;
|
|
||||||
|
|
||||||
@Column({ type: 'binary', length: 32, name: 'public_key' })
|
@Column({ type: 'binary', length: 32, name: 'public_key' })
|
||||||
pubkey: Buffer
|
pubkey: Buffer
|
||||||
|
|
||||||
52
database/entity/0003-login_server_tables/LoginElopageBuys.ts
Normal file
52
database/entity/0003-login_server_tables/LoginElopageBuys.ts
Normal 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
|
||||||
|
}
|
||||||
26
database/entity/0003-login_server_tables/LoginEmailOptIn.ts
Normal file
26
database/entity/0003-login_server_tables/LoginEmailOptIn.ts
Normal 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
|
||||||
|
}
|
||||||
56
database/entity/0003-login_server_tables/LoginUser.ts
Normal file
56
database/entity/0003-login_server_tables/LoginUser.ts
Normal 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
|
||||||
|
}
|
||||||
16
database/entity/0003-login_server_tables/LoginUserBackup.ts
Normal file
16
database/entity/0003-login_server_tables/LoginUserBackup.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||||
|
|
||||||
|
@Entity('login_user_backups')
|
||||||
|
export class LoginUserBackup extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'user_id', nullable: false })
|
||||||
|
userId: number
|
||||||
|
|
||||||
|
@Column({ type: 'text', name: 'passphrase', nullable: false })
|
||||||
|
passphrase: string
|
||||||
|
|
||||||
|
@Column({ name: 'mnemonic_type', default: -1 })
|
||||||
|
mnemonicType: number
|
||||||
|
}
|
||||||
1
database/entity/Balance.ts
Normal file
1
database/entity/Balance.ts
Normal 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
Loading…
x
Reference in New Issue
Block a user