mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 650-View-for-the-GDT-Transaction-List
This commit is contained in:
commit
281a6beadd
52
.github/workflows/publish.yml
vendored
52
.github/workflows/publish.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD COMMUNITY NEO4J ##########################################
|
||||
# JOB: DOCKER BUILD PRODUCTION FRONTEND ######################################
|
||||
##############################################################################
|
||||
build_production_frontend:
|
||||
name: Docker Build Production - Frontend
|
||||
@ -43,6 +43,43 @@ jobs:
|
||||
name: docker-frontend-production
|
||||
path: /tmp/frontend.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD PRODUCTION BACKEND #######################################
|
||||
##############################################################################
|
||||
build_production_backend:
|
||||
name: Docker Build Production - Backend
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# SET ENVS ###############################################################
|
||||
##########################################################################
|
||||
- name: ENV - VERSION
|
||||
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_DATE
|
||||
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_VERSION
|
||||
run: echo "BUILD_VERSION=${VERSION}.${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_COMMIT
|
||||
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
|
||||
##########################################################################
|
||||
# BACKEND ################################################################
|
||||
##########################################################################
|
||||
- name: Backend | Build `production` image
|
||||
run: |
|
||||
docker build --target production -t "gradido/backend:latest" -t "gradido/backend:production" -t "gradido/backend:${VERSION}" -t "gradido/backend:${BUILD_VERSION}" backend/
|
||||
docker save "gradido/backend" > /tmp/backend.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker-backend-production
|
||||
path: /tmp/backend.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD PRODUCTION LOGIN SERVER ##################################
|
||||
##############################################################################
|
||||
@ -199,7 +236,7 @@ jobs:
|
||||
upload_to_dockerhub:
|
||||
name: Upload to Dockerhub
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_production_frontend, build_production_login_server, build_production_community_server, build_production_mariadb, build_production_nginx]
|
||||
needs: [build_production_frontend, build_production_backend, build_production_login_server, build_production_community_server, build_production_mariadb, build_production_nginx]
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@ -212,13 +249,20 @@ jobs:
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGES #################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Neo4J)
|
||||
- name: Download Docker Image (Frontend)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-frontend-production
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/frontend.tar
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-backend-production
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
- name: Download Docker Image (Login Server)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
@ -254,6 +298,8 @@ jobs:
|
||||
run: echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
|
||||
- name: Push frontend
|
||||
run: docker push --all-tags gradido/frontend
|
||||
- name: Push backend
|
||||
run: docker push --all-tags gradido/backend
|
||||
- name: Push login_server
|
||||
run: docker push --all-tags gradido/login_server
|
||||
- name: Push community_server
|
||||
|
||||
57
.github/workflows/test.yml
vendored
57
.github/workflows/test.yml
vendored
@ -30,6 +30,32 @@ jobs:
|
||||
name: docker-frontend-test
|
||||
path: /tmp/frontend.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST BACKEND #############################################
|
||||
##############################################################################
|
||||
build_test_backend:
|
||||
name: Docker Build Test - Backend
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# FRONTEND ###############################################################
|
||||
##########################################################################
|
||||
- name: Backend | Build `test` image
|
||||
run: |
|
||||
docker build --target test -t "gradido/backend:test" backend/
|
||||
docker save "gradido/backend:test" > /tmp/backend.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp/backend.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST LOGIN SERVER ########################################
|
||||
##############################################################################
|
||||
@ -159,6 +185,35 @@ jobs:
|
||||
- name: frontend | Lint
|
||||
run: docker run --rm gradido/frontend:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT BACKEND #########################################################
|
||||
##############################################################################
|
||||
lint_backend:
|
||||
name: Lint - Backend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_backend]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
##########################################################################
|
||||
# LINT FRONTEND ###########################################################
|
||||
##########################################################################
|
||||
- name: backend | Lint
|
||||
run: docker run --rm gradido/backend:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST FRONTEND ###################################################
|
||||
##############################################################################
|
||||
@ -262,7 +317,7 @@ jobs:
|
||||
report_name: Coverage Backend Login
|
||||
type: lcov
|
||||
result_path: ./coverage/coverage.info
|
||||
min_coverage: 25
|
||||
min_coverage: 34
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -31,6 +31,3 @@
|
||||
[submodule "login_server/src/proto"]
|
||||
path = login_server/src/proto
|
||||
url = https://github.com/gradido/gradido_protocol.git
|
||||
[submodule "login_server/dependencies/protobuf"]
|
||||
path = login_server/dependencies/protobuf
|
||||
url = https://github.com/protocolbuffers/protobuf.git
|
||||
|
||||
5
backend/.env.dist
Normal file
5
backend/.env.dist
Normal file
@ -0,0 +1,5 @@
|
||||
PORT=4000
|
||||
GRAPHIQL=false
|
||||
// LOGIN_API_URL=http://login-server:1201/
|
||||
// COMMUNITY_API_URL=http://nginx/api/
|
||||
// GDT_API_URL=https://gdt.gradido.net
|
||||
3
backend/.eslintignore
Normal file
3
backend/.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
**/*.min.js
|
||||
build
|
||||
26
backend/.eslintrc.js
Normal file
26
backend/.eslintrc.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
// jest: true,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['prettier', '@typescript-eslint' /*, 'jest' */],
|
||||
extends: [
|
||||
'standard',
|
||||
'eslint:recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
'no-console': ['error'],
|
||||
'no-debugger': 'error',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
6
backend/.gitignore
vendored
Normal file
6
backend/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/node_modules/
|
||||
/.env
|
||||
/build/
|
||||
|
||||
# emacs
|
||||
*~
|
||||
8
backend/.prettierrc.js
Normal file
8
backend/.prettierrc.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
};
|
||||
95
backend/Dockerfile
Normal file
95
backend/Dockerfile
Normal file
@ -0,0 +1,95 @@
|
||||
##################################################################################
|
||||
# BASE ###########################################################################
|
||||
##################################################################################
|
||||
FROM node:12.19.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="4000"
|
||||
|
||||
# Labels
|
||||
LABEL org.label-schema.build-date="${BUILD_DATE}"
|
||||
LABEL org.label-schema.name="gradido:backend"
|
||||
LABEL org.label-schema.description="Gradido GraphQL Backend"
|
||||
LABEL org.label-schema.usage="https://github.com/gradido/gradido/blob/master/README.md"
|
||||
LABEL org.label-schema.url="https://gradido.net"
|
||||
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/tree/master/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@gradido.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 npm 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 . .
|
||||
# npm install
|
||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||
# npm build
|
||||
RUN yarn run build
|
||||
|
||||
##################################################################################
|
||||
# TEST ###########################################################################
|
||||
##################################################################################
|
||||
FROM build as test
|
||||
|
||||
# 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}/build ./build
|
||||
# We also copy the node_modules express and serve-static for the run script
|
||||
# COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
|
||||
# Copy 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"
|
||||
6
backend/ormconfig.json
Normal file
6
backend/ormconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"type": "sqlite",
|
||||
"database": "./db.sqlite3",
|
||||
"entities": ["./src/graphql/models/*.ts"],
|
||||
"synchronize": true
|
||||
}
|
||||
45
backend/package.json
Normal file
45
backend/package.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "gradido-backend",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/backend",
|
||||
"author": "Ulf Gebhardt",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "tsc --build --clean",
|
||||
"start": "node build/index.js",
|
||||
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
|
||||
"lint": "eslint . --ext .js,.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"apollo-server-express": "^2.25.2",
|
||||
"axios": "^0.21.1",
|
||||
"class-validator": "^0.13.1",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^15.5.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typeorm": "^0.2.34"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.12",
|
||||
"@types/jsonwebtoken": "^8.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
||||
"@typescript-eslint/parser": "^4.28.0",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"nodemon": "^2.0.7",
|
||||
"prettier": "^2.3.1",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^4.3.4"
|
||||
}
|
||||
}
|
||||
37
backend/src/apis/loginAPI.ts
Normal file
37
backend/src/apis/loginAPI.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import axios from 'axios'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const apiPost = async (url: string, payload: unknown): Promise<any> => {
|
||||
return axios
|
||||
.post(url, payload)
|
||||
.then((result) => {
|
||||
if (result.status !== 200) {
|
||||
throw new Error('HTTP Status Error ' + result.status)
|
||||
}
|
||||
if (result.data.state !== 'success') {
|
||||
throw new Error(result.data.msg)
|
||||
}
|
||||
return { success: true, data: result.data }
|
||||
})
|
||||
.catch((error) => {
|
||||
return { success: false, data: error.message }
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const apiGet = async (url: string): Promise<any> => {
|
||||
return axios
|
||||
.get(url)
|
||||
.then((result) => {
|
||||
if (result.status !== 200) {
|
||||
throw new Error('HTTP Status Error ' + result.status)
|
||||
}
|
||||
if (!['success', 'warning'].includes(result.data.state)) {
|
||||
throw new Error(result.data.msg)
|
||||
}
|
||||
return { success: true, data: result.data }
|
||||
})
|
||||
.catch((error) => {
|
||||
return { success: false, data: error.message }
|
||||
})
|
||||
}
|
||||
21
backend/src/config/index.ts
Normal file
21
backend/src/config/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
const server = {
|
||||
PORT: process.env.PORT || 4000,
|
||||
JWT_SECRET: process.env.JWT_SECRET || 'secret123',
|
||||
JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m',
|
||||
GRAPHIQL: process.env.GRAPHIQL === 'true' || false,
|
||||
LOGIN_API_URL: process.env.LOGIN_API_URL || 'http://login-server:1201/',
|
||||
COMMUNITY_API_URL: process.env.COMMUNITY_API_URL || 'http://ngninx/api/',
|
||||
GDT_API_URL: process.env.GDT_API_URL || 'https://gdt.gradido.net',
|
||||
}
|
||||
|
||||
// This is needed by graphql-directive-auth
|
||||
process.env.APP_SECRET = server.JWT_SECRET
|
||||
|
||||
const CONFIG = { ...server }
|
||||
|
||||
export default CONFIG
|
||||
16
backend/src/graphql/inputs/GdtInputs.ts
Normal file
16
backend/src/graphql/inputs/GdtInputs.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class GdtTransactionInput {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
firstPage?: number
|
||||
|
||||
@Field(() => Number)
|
||||
items?: number
|
||||
|
||||
@Field(() => String)
|
||||
order?: string
|
||||
}
|
||||
76
backend/src/graphql/inputs/LoginUserInput.ts
Normal file
76
backend/src/graphql/inputs/LoginUserInput.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class UnsecureLoginArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class CreateUserArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
lastName: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class ChangePasswordArgs {
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class UpdateUserInfosArgs {
|
||||
@Field(() => Number)
|
||||
sessionId!: number
|
||||
|
||||
@Field(() => String)
|
||||
email!: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
firstName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
lastName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
description?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
username?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
language?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
password?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
passwordNew?: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class CheckUsernameArgs {
|
||||
@Field(() => String)
|
||||
username: string
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
groupId?: number
|
||||
}
|
||||
31
backend/src/graphql/inputs/TransactionInput.ts
Normal file
31
backend/src/graphql/inputs/TransactionInput.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class TransactionListInput {
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => Int)
|
||||
firstPage: number
|
||||
|
||||
@Field(() => Int)
|
||||
items: number
|
||||
|
||||
@Field(() => String)
|
||||
order: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class TransactionSendArgs {
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
amount: number
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
}
|
||||
21
backend/src/graphql/models/Balance.ts
Normal file
21
backend/src/graphql/models/Balance.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class Balance {
|
||||
constructor(json: any) {
|
||||
this.balance = Number(json.balance)
|
||||
this.decay = Number(json.decay)
|
||||
this.decayDate = json.decay_date
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field(() => Number)
|
||||
decay: number
|
||||
|
||||
@Field(() => String)
|
||||
decayDate: string
|
||||
}
|
||||
21
backend/src/graphql/models/CheckUsernameResponse.ts
Normal file
21
backend/src/graphql/models/CheckUsernameResponse.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/* 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
|
||||
}
|
||||
25
backend/src/graphql/models/Decay.ts
Normal file
25
backend/src/graphql/models/Decay.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class Decay {
|
||||
constructor(json: any) {
|
||||
this.balance = Number(json.balance)
|
||||
this.decayStart = json.decay_start
|
||||
this.decayEnd = json.decay_end
|
||||
this.decayDuration = json.decay_duration
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
decayStart?: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
decayEnd?: number
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
decayDuration?: string
|
||||
}
|
||||
57
backend/src/graphql/models/GdtEntry.ts
Normal file
57
backend/src/graphql/models/GdtEntry.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
export enum GdtEntryType {
|
||||
FORM = 1,
|
||||
CVS = 2,
|
||||
ELOPAGE = 3,
|
||||
ELOPAGE_PUBLISHER = 4,
|
||||
DIGISTORE = 5,
|
||||
CVS2 = 6,
|
||||
GLOBAL_MODIFICATOR = 7,
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class GdtEntry {
|
||||
constructor(json: any) {
|
||||
this.amount = json.amount
|
||||
this.date = json.date
|
||||
this.email = json.email
|
||||
this.comment = json.comment
|
||||
this.couponCode = json.coupon_code
|
||||
this.gdtEntryType = json.gdt_entry_type_id
|
||||
this.factor = json.factor
|
||||
this.amount2 = json.amount2
|
||||
this.factor2 = json.factor2
|
||||
this.gdt = json.gdt
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
amount: number
|
||||
|
||||
@Field(() => String)
|
||||
date: string
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
comment: string
|
||||
|
||||
@Field(() => String)
|
||||
couponCode: string
|
||||
|
||||
@Field(() => Number)
|
||||
gdtEntryType: GdtEntryType
|
||||
|
||||
@Field(() => Number)
|
||||
factor: number
|
||||
|
||||
@Field(() => Number)
|
||||
amount2: number
|
||||
|
||||
@Field(() => Number)
|
||||
factor2: number
|
||||
|
||||
@Field(() => Number)
|
||||
gdt: number
|
||||
}
|
||||
42
backend/src/graphql/models/GdtEntryList.ts
Normal file
42
backend/src/graphql/models/GdtEntryList.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { GdtEntry } from './GdtEntry'
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class GdtSumPerEmail {
|
||||
constructor(email: string, summe: number) {
|
||||
this.email = email
|
||||
this.summe = summe
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
summe: number
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class GdtEntryList {
|
||||
constructor(json: any) {
|
||||
this.state = json.state
|
||||
this.count = json.count
|
||||
this.gdtEntries = json.gdtEntries ? json.gdtEntries.map((json: any) => new GdtEntry(json)) : []
|
||||
this.gdtSum = json.gdtSum
|
||||
this.timeUsed = json.timeUsed
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
state: string
|
||||
|
||||
@Field(() => Number)
|
||||
count: number
|
||||
|
||||
@Field(() => [GdtEntry])
|
||||
gdtEntries: GdtEntry[]
|
||||
|
||||
@Field(() => Number)
|
||||
gdtSum: number
|
||||
|
||||
@Field(() => Number)
|
||||
timeUsed: number
|
||||
}
|
||||
20
backend/src/graphql/models/LoginResponse.ts
Normal file
20
backend/src/graphql/models/LoginResponse.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import { User } from './User'
|
||||
|
||||
// temporaray solution until we have JWT implemented
|
||||
|
||||
@ObjectType()
|
||||
export class LoginResponse {
|
||||
constructor(json: any) {
|
||||
this.sessionId = json.session_id
|
||||
this.user = new User(json.user)
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => User)
|
||||
user: User
|
||||
}
|
||||
17
backend/src/graphql/models/LoginViaVerificationCode.ts
Normal file
17
backend/src/graphql/models/LoginViaVerificationCode.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class LoginViaVerificationCode {
|
||||
constructor(json: any) {
|
||||
this.sessionId = json.session_id
|
||||
this.email = json.user.email
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
}
|
||||
17
backend/src/graphql/models/SendPasswordResetEmailResponse.ts
Normal file
17
backend/src/graphql/models/SendPasswordResetEmailResponse.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class SendPasswordResetEmailResponse {
|
||||
constructor(json: any) {
|
||||
this.state = json.state
|
||||
this.msg = json.msg
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
state: string
|
||||
|
||||
@Field(() => String)
|
||||
msg?: string
|
||||
}
|
||||
92
backend/src/graphql/models/Transaction.ts
Normal file
92
backend/src/graphql/models/Transaction.ts
Normal file
@ -0,0 +1,92 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import { Decay } from './Decay'
|
||||
|
||||
// we need a better solution for the decay block:
|
||||
// the first transaction on the first page shows the decay since the last transaction
|
||||
// the format is actually a Decay and not a Transaction.
|
||||
// Therefore we have a lot of nullable fields, which should be always present
|
||||
|
||||
@ObjectType()
|
||||
export class Transaction {
|
||||
constructor(json: any) {
|
||||
// console.log('Transaction constructor', json)
|
||||
this.type = json.type
|
||||
this.balance = Number(json.balance)
|
||||
this.decayStart = json.decay_start
|
||||
this.decayEnd = json.decay_end
|
||||
this.decayDuration = json.decay_duration
|
||||
this.memo = json.memo
|
||||
this.transactionId = json.transaction_id
|
||||
this.name = json.name
|
||||
this.email = json.email
|
||||
this.date = json.date
|
||||
this.decay = json.decay ? new Decay(json.decay) : undefined
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
type: string
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayStart?: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayEnd?: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayDuration?: string
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
|
||||
@Field(() => Number)
|
||||
transactionId: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
name?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
email?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
date?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
decay?: Decay
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionList {
|
||||
constructor(json: any) {
|
||||
this.gdtSum = Number(json.gdtSum)
|
||||
this.count = json.count
|
||||
this.balance = Number(json.balance)
|
||||
this.decay = Number(json.decay)
|
||||
this.decayDate = json.decay_date
|
||||
this.transactions = json.transactions.map((el: any) => {
|
||||
return new Transaction(el)
|
||||
})
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
gdtSum: number
|
||||
|
||||
@Field(() => Number)
|
||||
count: number
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field(() => Number)
|
||||
decay: number
|
||||
|
||||
@Field(() => String)
|
||||
decayDate: string
|
||||
|
||||
@Field(() => [Transaction])
|
||||
transactions: Transaction[]
|
||||
}
|
||||
13
backend/src/graphql/models/UpdateUserInfosResponse.ts
Normal file
13
backend/src/graphql/models/UpdateUserInfosResponse.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/* 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
|
||||
}
|
||||
67
backend/src/graphql/models/User.ts
Normal file
67
backend/src/graphql/models/User.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class User {
|
||||
/*
|
||||
@Field(() => ID)
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
*/
|
||||
constructor(json: any) {
|
||||
this.email = json.email
|
||||
this.firstName = json.first_name
|
||||
this.lastName = json.last_name
|
||||
this.username = json.username
|
||||
this.description = json.description
|
||||
this.language = json.language
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
lastName: string
|
||||
|
||||
@Field(() => String)
|
||||
username: string
|
||||
|
||||
@Field(() => String)
|
||||
description: string
|
||||
|
||||
/*
|
||||
@Field(() => String)
|
||||
pubkey: string
|
||||
|
||||
// not sure about the type here. Maybe better to have a string
|
||||
@Field(() => number)
|
||||
created: number
|
||||
|
||||
@Field(() => Boolean)
|
||||
emailChecked: boolean
|
||||
|
||||
@Field(() => Boolean)
|
||||
passphraseShown: boolean
|
||||
*/
|
||||
|
||||
@Field(() => String)
|
||||
language: string
|
||||
|
||||
/*
|
||||
@Field(() => Boolean)
|
||||
disabled: boolean
|
||||
*/
|
||||
|
||||
/* I suggest to have a group as type here
|
||||
@Field(() => ID)
|
||||
groupId: number
|
||||
|
||||
// what is puvlisherId?
|
||||
@Field(() => ID)
|
||||
publisherId: number
|
||||
*/
|
||||
}
|
||||
14
backend/src/graphql/resolvers/BalanceResolver.ts
Normal file
14
backend/src/graphql/resolvers/BalanceResolver.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Resolver, Query, /* Mutation, */ Arg } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { Balance } from '../models/Balance'
|
||||
import { apiGet } from '../../apis/loginAPI'
|
||||
|
||||
@Resolver()
|
||||
export class BalanceResolver {
|
||||
@Query(() => Balance)
|
||||
async balance(@Arg('sessionId') sessionId: number): Promise<Balance> {
|
||||
const result = await apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + sessionId)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return new Balance(result.data)
|
||||
}
|
||||
}
|
||||
26
backend/src/graphql/resolvers/GdtResolver.ts
Normal file
26
backend/src/graphql/resolvers/GdtResolver.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// import jwt from 'jsonwebtoken'
|
||||
import { Resolver, Query, /* Mutation, */ Args } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { GdtEntryList } from '../models/GdtEntryList'
|
||||
import { GdtTransactionInput } from '../inputs/GdtInputs'
|
||||
import { apiGet } from '../../apis/loginAPI'
|
||||
|
||||
@Resolver()
|
||||
export class GdtResolver {
|
||||
@Query(() => GdtEntryList)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async listGDTEntries(
|
||||
@Args() { email, firstPage = 1, items = 5, order = 'DESC' }: GdtTransactionInput,
|
||||
): Promise<GdtEntryList> {
|
||||
email = email.trim().toLowerCase()
|
||||
const result = await apiGet(
|
||||
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${email}/${firstPage}/${items}/${order}`,
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
|
||||
return new GdtEntryList(result.data)
|
||||
}
|
||||
}
|
||||
39
backend/src/graphql/resolvers/TransactionResolver.ts
Normal file
39
backend/src/graphql/resolvers/TransactionResolver.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { Resolver, Query, /* Mutation, */ Args } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { TransactionList } from '../models/Transaction'
|
||||
import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput'
|
||||
import { apiGet, apiPost } from '../../apis/loginAPI'
|
||||
|
||||
@Resolver()
|
||||
export class TransactionResolver {
|
||||
@Query(() => TransactionList)
|
||||
async transactionList(
|
||||
@Args() { sessionId, firstPage = 1, items = 25, order = 'DESC' }: TransactionListInput,
|
||||
): Promise<TransactionList> {
|
||||
const result = await apiGet(
|
||||
`${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${sessionId}`,
|
||||
)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return new TransactionList(result.data)
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
async sendCoins(
|
||||
@Args() { sessionId, email, amount, memo }: TransactionSendArgs,
|
||||
): Promise<string> {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
target_email: email,
|
||||
amount: amount * 10000,
|
||||
memo,
|
||||
auto_sign: true,
|
||||
transaction_type: 'transfer',
|
||||
blockchain_type: 'mysql',
|
||||
}
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'createTransaction', payload)
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
return 'success'
|
||||
}
|
||||
}
|
||||
161
backend/src/graphql/resolvers/UserResolver.ts
Normal file
161
backend/src/graphql/resolvers/UserResolver.ts
Normal file
@ -0,0 +1,161 @@
|
||||
// import jwt from 'jsonwebtoken'
|
||||
import { Resolver, Query, Args, Arg } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { CheckUsernameResponse } from '../models/CheckUsernameResponse'
|
||||
import { LoginResponse } from '../models/LoginResponse'
|
||||
import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode'
|
||||
import { SendPasswordResetEmailResponse } from '../models/SendPasswordResetEmailResponse'
|
||||
import { UpdateUserInfosResponse } from '../models/UpdateUserInfosResponse'
|
||||
import {
|
||||
ChangePasswordArgs,
|
||||
CheckUsernameArgs,
|
||||
CreateUserArgs,
|
||||
UnsecureLoginArgs,
|
||||
UpdateUserInfosArgs,
|
||||
} from '../inputs/LoginUserInput'
|
||||
import { apiPost, apiGet } from '../../apis/loginAPI'
|
||||
|
||||
@Resolver()
|
||||
export class UserResolver {
|
||||
@Query(() => LoginResponse)
|
||||
async login(@Args() { email, password }: UnsecureLoginArgs): Promise<LoginResponse> {
|
||||
email = email.trim().toLowerCase()
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
|
||||
|
||||
// if there is no user, throw an authentication error
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
|
||||
// temporary solution until we have JWT implemented
|
||||
return new LoginResponse(result.data)
|
||||
|
||||
// create and return the json web token
|
||||
// The expire doesn't help us here. The client needs to track when the token expires on its own,
|
||||
// since every action prolongs the time the session is valid.
|
||||
/*
|
||||
return jwt.sign(
|
||||
{ result, role: 'todo' },
|
||||
CONFIG.JWT_SECRET, // * , { expiresIn: CONFIG.JWT_EXPIRES_IN } ,
|
||||
)
|
||||
*/
|
||||
// return (await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', login)).result.data
|
||||
// const loginResult: LoginResult = await loginAPI.login(data)
|
||||
// return loginResult.user ? loginResult.user : new User()
|
||||
}
|
||||
|
||||
@Query(() => LoginViaVerificationCode)
|
||||
async loginViaEmailVerificationCode(
|
||||
@Arg('optin') optin: string,
|
||||
): Promise<LoginViaVerificationCode> {
|
||||
// I cannot use number as type here.
|
||||
// The value received is not the same as sent by the query
|
||||
const result = await apiGet(
|
||||
CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin,
|
||||
)
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
return new LoginViaVerificationCode(result.data)
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
async logout(@Arg('sessionId') sessionId: number): Promise<string> {
|
||||
const payload = { session_id: sessionId }
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'logout', payload)
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
async create(@Args() { email, firstName, lastName, password }: CreateUserArgs): Promise<string> {
|
||||
const payload = {
|
||||
email,
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
password,
|
||||
emailType: 2,
|
||||
login_after_register: true,
|
||||
}
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'createUser', payload)
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@Query(() => SendPasswordResetEmailResponse)
|
||||
async sendResetPasswordEmail(
|
||||
@Arg('email') email: string,
|
||||
): Promise<SendPasswordResetEmailResponse> {
|
||||
const payload = {
|
||||
email,
|
||||
email_text: 7,
|
||||
email_verification_code_type: 'resetPassword',
|
||||
}
|
||||
const response = await apiPost(CONFIG.LOGIN_API_URL + 'sendEmail', payload)
|
||||
if (!response.success) throw new Error(response.data)
|
||||
return new SendPasswordResetEmailResponse(response.data)
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
async resetPassword(
|
||||
@Args()
|
||||
{ sessionId, email, password }: ChangePasswordArgs,
|
||||
): Promise<string> {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
email,
|
||||
password,
|
||||
}
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'resetPassword', payload)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return 'sucess'
|
||||
}
|
||||
|
||||
@Query(() => UpdateUserInfosResponse)
|
||||
async updateUserInfos(
|
||||
@Args()
|
||||
{
|
||||
sessionId,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
description,
|
||||
username,
|
||||
language,
|
||||
password,
|
||||
passwordNew,
|
||||
}: UpdateUserInfosArgs,
|
||||
): Promise<UpdateUserInfosResponse> {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
email,
|
||||
update: {
|
||||
'User.first_name': firstName || undefined,
|
||||
'User.last_name': lastName || undefined,
|
||||
'User.description': description || undefined,
|
||||
'User.username': username || undefined,
|
||||
'User.language': language || undefined,
|
||||
'User.password': passwordNew || undefined,
|
||||
'User.password_old': password || undefined,
|
||||
},
|
||||
}
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return new UpdateUserInfosResponse(result.data)
|
||||
}
|
||||
|
||||
@Query(() => CheckUsernameResponse)
|
||||
async checkUsername(
|
||||
@Args() { username, groupId = 1 }: CheckUsernameArgs,
|
||||
): Promise<CheckUsernameResponse> {
|
||||
const response = await apiGet(
|
||||
CONFIG.LOGIN_API_URL + `checkUsername?username=${username}&group_id=${groupId}`,
|
||||
)
|
||||
if (!response.success) throw new Error(response.data)
|
||||
return new CheckUsernameResponse(response.data)
|
||||
}
|
||||
}
|
||||
48
backend/src/index.ts
Normal file
48
backend/src/index.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import 'reflect-metadata'
|
||||
import express from 'express'
|
||||
import { buildSchema } from 'type-graphql'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
// import { createConnection } from 'typeorm'
|
||||
import CONFIG from './config'
|
||||
|
||||
// TODO move to extern
|
||||
// import { BookResolver } from './graphql/resolvers/BookResolver'
|
||||
import { UserResolver } from './graphql/resolvers/UserResolver'
|
||||
import { BalanceResolver } from './graphql/resolvers/BalanceResolver'
|
||||
import { GdtResolver } from './graphql/resolvers/GdtResolver'
|
||||
import { TransactionResolver } from './graphql/resolvers/TransactionResolver'
|
||||
|
||||
// TODO implement
|
||||
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
|
||||
|
||||
async function main() {
|
||||
// const connection = await createConnection()
|
||||
const schema = await buildSchema({
|
||||
resolvers: [UserResolver, BalanceResolver, TransactionResolver, GdtResolver],
|
||||
})
|
||||
|
||||
// Graphiql interface
|
||||
let playground = false
|
||||
if (CONFIG.GRAPHIQL) {
|
||||
playground = true
|
||||
}
|
||||
|
||||
// Express Server
|
||||
const server = express()
|
||||
|
||||
// Apollo Server
|
||||
const apollo = new ApolloServer({ schema, playground })
|
||||
apollo.applyMiddleware({ app: server })
|
||||
|
||||
// Start Server
|
||||
server.listen(CONFIG.PORT, () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Server is running at http://localhost:${CONFIG.PORT}`)
|
||||
if (CONFIG.GRAPHIQL) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`GraphIQL available at http://localhost:${CONFIG.PORT}/graphql`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
72
backend/tsconfig.json
Normal file
72
backend/tsconfig.json
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./build", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
}
|
||||
}
|
||||
3706
backend/yarn.lock
Normal file
3706
backend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -274,6 +274,8 @@ class AppRequestsController extends AppController
|
||||
public function getBalance($session_id = 0)
|
||||
{
|
||||
$this->viewBuilder()->setLayout('ajax');
|
||||
$this->response = $this->response->withType('application/json');
|
||||
|
||||
$login_result = $this->requestLogin($session_id, false);
|
||||
if($login_result !== true) {
|
||||
$this->set('body', $login_result);
|
||||
@ -304,12 +306,16 @@ class AppRequestsController extends AppController
|
||||
}
|
||||
|
||||
$body['decay_date'] = $now;
|
||||
$this->addAdminError("AppRequests", "getBalance", $body, $user['id']);
|
||||
$this->set('body', $body);
|
||||
}
|
||||
|
||||
public function listTransactions($page = 1, $count = 25, $orderDirection = 'ASC', $session_id = 0)
|
||||
{
|
||||
|
||||
$this->viewBuilder()->setLayout('ajax');
|
||||
$this->response = $this->response->withType('application/json');
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
$login_result = $this->requestLogin($session_id, false);
|
||||
|
||||
@ -6,6 +6,10 @@
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
$body['balance'] = $this->element('centToFloat', ['cent' => $body['balance'], 'precision' => 4]);
|
||||
$body['decay'] = $this->element('centToFloat', ['cent' => $body['decay'], 'precision' => 4]);
|
||||
if(isset($body['balance'])) {
|
||||
$body['balance'] = $this->element('centToFloat', ['cent' => $body['balance'], 'precision' => 4]);
|
||||
}
|
||||
if(isset($body['decay'])) {
|
||||
$body['decay'] = $this->element('centToFloat', ['cent' => $body['decay'], 'precision' => 4]);
|
||||
}
|
||||
?><?= json_encode($body) ?>
|
||||
@ -21,6 +21,25 @@ services:
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./frontend:/app
|
||||
|
||||
########################################################
|
||||
# BACKEND ##############################################
|
||||
########################################################
|
||||
backend:
|
||||
image: gradido/backend:development
|
||||
build:
|
||||
target: development
|
||||
networks:
|
||||
- external-net
|
||||
- internal-net
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
volumes:
|
||||
# This makes sure the docker container has its own node modules.
|
||||
# Therefore it is possible to have a different node version on the host machine
|
||||
- backend_node_modules:/app/node_modules
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./backend:/app
|
||||
|
||||
#########################################################
|
||||
## LOGIN SERVER #########################################
|
||||
#########################################################
|
||||
@ -100,4 +119,5 @@ services:
|
||||
|
||||
volumes:
|
||||
frontend_node_modules:
|
||||
login_build_ubuntu_3.1:
|
||||
backend_node_modules:
|
||||
login_build_ubuntu_3.1:
|
||||
@ -47,7 +47,31 @@ services:
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
- db_vol:/var/lib/mysql
|
||||
- db_vol:/var/lib/mysql
|
||||
|
||||
########################################################
|
||||
# BACKEND ##############################################
|
||||
########################################################
|
||||
backend:
|
||||
image: gradido/backend:latest
|
||||
build:
|
||||
context: ./backend
|
||||
target: production
|
||||
networks:
|
||||
- internal-net
|
||||
ports:
|
||||
- 4000:4000
|
||||
environment:
|
||||
# Envs used in Dockerfile
|
||||
# - DOCKER_WORKDIR="/app"
|
||||
# - PORT=4000
|
||||
- BUILD_DATE
|
||||
- BUILD_VERSION
|
||||
- BUILD_COMMIT
|
||||
- NODE_ENV="production"
|
||||
# Application only envs
|
||||
#env_file:
|
||||
# - ./frontend/.env
|
||||
|
||||
#########################################################
|
||||
## LOGIN SERVER #########################################
|
||||
|
||||
@ -6,22 +6,19 @@
|
||||
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
|
||||
>
|
||||
<b-form-group :label="label" :label-for="labelFor">
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
v-model="currentValue"
|
||||
v-bind="ariaInput"
|
||||
:id="labelFor"
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
type="email"
|
||||
:state="validated ? valid : false"
|
||||
trim
|
||||
class="email-form-input"
|
||||
></b-form-input>
|
||||
<b-form-invalid-feedback v-bind="ariaMsg">
|
||||
{{ errors[0] }}
|
||||
</b-form-invalid-feedback>
|
||||
</b-input-group>
|
||||
<b-form-input
|
||||
v-model="currentValue"
|
||||
v-bind="ariaInput"
|
||||
:id="labelFor"
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
type="email"
|
||||
:state="validated ? valid : false"
|
||||
trim
|
||||
></b-form-input>
|
||||
<b-form-invalid-feedback v-bind="ariaMsg">
|
||||
{{ errors[0] }}
|
||||
</b-form-invalid-feedback>
|
||||
</b-form-group>
|
||||
</validation-provider>
|
||||
</template>
|
||||
@ -62,12 +59,3 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.email-form-input {
|
||||
border-right-style: solid !important;
|
||||
border-right-width: 1px !important;
|
||||
padding-right: 12px !important;
|
||||
border-top-right-radius: 6px !important;
|
||||
border-bottom-right-radius: 6px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -19,7 +19,11 @@
|
||||
:state="validated ? valid : false"
|
||||
></b-form-input>
|
||||
<b-input-group-append>
|
||||
<b-button variant="outline-primary" @click="toggleShowPassword">
|
||||
<b-button
|
||||
variant="outline-light"
|
||||
@click="toggleShowPassword"
|
||||
class="border-left-0 rounded-right"
|
||||
>
|
||||
<b-icon :icon="showPassword ? 'eye' : 'eye-slash'" />
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
|
||||
@ -10,11 +10,11 @@
|
||||
containsNumericCharacter: true,
|
||||
atLeastEightCharactera: true,
|
||||
}"
|
||||
:label="$t('form.password_new')"
|
||||
:label="register ? $t('form.password') : $t('form.password_new')"
|
||||
:showAllErrors="true"
|
||||
:immediate="true"
|
||||
:name="$t('form.password_new')"
|
||||
:placeholder="$t('form.password_new')"
|
||||
:name="createId(register ? $t('form.password') : $t('form.password_new'))"
|
||||
:placeholder="register ? $t('form.password') : $t('form.password_new')"
|
||||
v-model="password"
|
||||
></input-password>
|
||||
</b-col>
|
||||
@ -23,8 +23,9 @@
|
||||
<b-col>
|
||||
<input-password
|
||||
:rules="{ samePassword: value.password }"
|
||||
:label="$t('form.password_new_repeat')"
|
||||
:placeholder="$t('form.password_new_repeat')"
|
||||
:label="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
|
||||
:name="createId(register ? $t('form.passwordRepeat') : $t('form.password_new_repeat'))"
|
||||
:placeholder="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
|
||||
v-model="passwordRepeat"
|
||||
></input-password>
|
||||
</b-col>
|
||||
@ -44,6 +45,10 @@ export default {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
register: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -51,6 +56,11 @@ export default {
|
||||
passwordRepeat: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createId(text) {
|
||||
return text.replace(/ +/g, '-')
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
passwordObject() {
|
||||
return { password: this.password, passwordRepeat: this.passwordRepeat }
|
||||
|
||||
@ -16,7 +16,11 @@
|
||||
<ul class="nav align-items-center d-md-none">
|
||||
<div class="media align-items-center">
|
||||
<span class="avatar avatar-sm">
|
||||
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
|
||||
<vue-qrcode
|
||||
v-if="$store.state.email"
|
||||
:value="$store.state.email"
|
||||
type="image/png"
|
||||
></vue-qrcode>
|
||||
</span>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
@ -59,6 +59,7 @@
|
||||
"password_new_repeat":"neues Passwort wiederholen",
|
||||
"change": "ändern",
|
||||
"change-password": "Passwort ändern",
|
||||
"change-name": "Name ändern",
|
||||
"amount":"Betrag",
|
||||
"memo":"Nachricht",
|
||||
"message":"Nachricht",
|
||||
|
||||
@ -59,6 +59,7 @@
|
||||
"password_new_repeat":"Repeat new password",
|
||||
"change": "change",
|
||||
"change-password": "Change password",
|
||||
"change-name": "Change name",
|
||||
"amount":"Amount",
|
||||
"memo":"Message",
|
||||
"message":"Message",
|
||||
|
||||
@ -61,15 +61,15 @@ describe('Register', () => {
|
||||
})
|
||||
|
||||
it('has email input fields', () => {
|
||||
expect(wrapper.find('#registerEmail').exists()).toBeTruthy()
|
||||
expect(wrapper.find('#Email-input-field').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has password input fields', () => {
|
||||
expect(wrapper.find('#registerPassword').exists()).toBeTruthy()
|
||||
expect(wrapper.find('input[name="form.password"]').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has password repeat input fields', () => {
|
||||
expect(wrapper.find('#registerPasswordRepeat').exists()).toBeTruthy()
|
||||
expect(wrapper.find('input[name="form.passwordRepeat"]').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has 1 checkbox input fields', () => {
|
||||
@ -80,30 +80,31 @@ describe('Register', () => {
|
||||
expect(wrapper.find('button[type="submit"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows a warning when no valid Email is entered', async () => {
|
||||
wrapper.find('#registerEmail').setValue('no_valid@Email')
|
||||
it('displays a message that Email is required', async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
await expect(wrapper.find('#registerEmailLiveFeedback').text()).toEqual(
|
||||
'validations.messages.email',
|
||||
expect(wrapper.findAll('div.invalid-feedback').at(0).text()).toBe(
|
||||
'validations.messages.required',
|
||||
)
|
||||
})
|
||||
|
||||
it('shows 4 warnings when no password is set', async () => {
|
||||
const passwords = wrapper.findAll('input[type="password"]')
|
||||
passwords.at(0).setValue('')
|
||||
passwords.at(1).setValue('')
|
||||
it('displays a message that password is required', async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
await expect(wrapper.find('div.hints').text()).toContain(
|
||||
'site.signup.lowercase',
|
||||
'site.signup.uppercase',
|
||||
'site.signup.minimum',
|
||||
'site.signup.one_number',
|
||||
expect(wrapper.findAll('div.invalid-feedback').at(1).text()).toBe(
|
||||
'validations.messages.required',
|
||||
)
|
||||
})
|
||||
|
||||
// TODO test different invalid password combinations
|
||||
it('displays a message that passwordConfirm is required', async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
expect(wrapper.findAll('div.invalid-feedback').at(2).text()).toBe(
|
||||
'validations.messages.required',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO test submit button
|
||||
// To Do: Test lines 156-197,210-213
|
||||
})
|
||||
})
|
||||
|
||||
@ -76,99 +76,14 @@
|
||||
</b-form-group>
|
||||
</validation-provider>
|
||||
|
||||
<validation-provider
|
||||
name="Email"
|
||||
:rules="{ required: true, email: true }"
|
||||
v-slot="validationContext"
|
||||
>
|
||||
<b-form-group class="mb-3" label="Email" label-for="registerEmail">
|
||||
<b-form-input
|
||||
id="registerEmail"
|
||||
name="Email"
|
||||
v-model="form.email"
|
||||
placeholder="Email"
|
||||
:state="getValidationState(validationContext)"
|
||||
aria-describedby="registerEmailLiveFeedback"
|
||||
></b-form-input>
|
||||
|
||||
<b-form-invalid-feedback id="registerEmailLiveFeedback">
|
||||
{{ validationContext.errors[0] }}
|
||||
</b-form-invalid-feedback>
|
||||
</b-form-group>
|
||||
</validation-provider>
|
||||
<input-email v-model="form.email"></input-email>
|
||||
|
||||
<hr />
|
||||
<input-password-confirmation
|
||||
v-model="form.password"
|
||||
:register="register"
|
||||
></input-password-confirmation>
|
||||
|
||||
<validation-provider
|
||||
:name="$t('form.password')"
|
||||
:rules="{ required: true }"
|
||||
v-slot="validationContext"
|
||||
>
|
||||
<b-form-group
|
||||
class="mb-5"
|
||||
:label="$t('form.password')"
|
||||
label-for="registerPassword"
|
||||
>
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
id="registerPassword"
|
||||
:name="$t('form.password')"
|
||||
v-model="form.password"
|
||||
:placeholder="$t('form.password')"
|
||||
:type="passwordVisible ? 'text' : 'password'"
|
||||
:state="getValidationState(validationContext)"
|
||||
aria-describedby="registerPasswordLiveFeedback"
|
||||
></b-form-input>
|
||||
|
||||
<b-input-group-append>
|
||||
<b-button variant="outline-primary" @click="togglePasswordVisibility">
|
||||
<b-icon :icon="passwordVisible ? 'eye' : 'eye-slash'" />
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<b-form-invalid-feedback id="registerPasswordLiveFeedback">
|
||||
{{ validationContext.errors[0] }}
|
||||
</b-form-invalid-feedback>
|
||||
</b-form-group>
|
||||
</validation-provider>
|
||||
|
||||
<b-form-group
|
||||
class="mb-5"
|
||||
:label="$t('form.passwordRepeat')"
|
||||
label-for="registerPasswordRepeat"
|
||||
>
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
id="registerPasswordRepeat"
|
||||
:name="$t('form.passwordRepeat')"
|
||||
v-model.lazy="form.passwordRepeat"
|
||||
:placeholder="$t('form.passwordRepeat')"
|
||||
:type="passwordVisibleRepeat ? 'text' : 'password'"
|
||||
></b-form-input>
|
||||
|
||||
<b-input-group-append>
|
||||
<b-button variant="outline-primary" @click="togglePasswordRepeatVisibility">
|
||||
<b-icon :icon="passwordVisibleRepeat ? 'eye' : 'eye-slash'" />
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</b-form-group>
|
||||
|
||||
<transition name="hint" appear>
|
||||
<div v-if="passwordValidation.errors.length > 0 && !submitted" class="hints">
|
||||
<ul>
|
||||
<li v-for="error in passwordValidation.errors" :key="error">
|
||||
<small>{{ error }}</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="matches" v-else-if="!samePasswords">
|
||||
<p>
|
||||
{{ $t('site.signup.dont_match') }}
|
||||
<i class="ni ni-active-40" color="danger"></i>
|
||||
</p>
|
||||
</div>
|
||||
</transition>
|
||||
<b-row class="my-4">
|
||||
<b-col cols="12">
|
||||
<b-form-checkbox
|
||||
@ -194,17 +109,7 @@
|
||||
</span>
|
||||
</b-alert>
|
||||
|
||||
<div
|
||||
class="text-center"
|
||||
v-if="
|
||||
passwordsFilled &&
|
||||
samePasswords &&
|
||||
passwordValidation.valid &&
|
||||
namesFilled &&
|
||||
emailFilled &&
|
||||
form.agree
|
||||
"
|
||||
>
|
||||
<div class="text-center" v-if="namesFilled && emailFilled && form.agree">
|
||||
<div class="text-center">
|
||||
<b-button class="ml-2" @click="resetForm()">{{ $t('form.reset') }}</b-button>
|
||||
<b-button type="submit" variant="primary">{{ $t('signup') }}</b-button>
|
||||
@ -224,8 +129,11 @@
|
||||
</template>
|
||||
<script>
|
||||
import loginAPI from '../../apis/loginAPI'
|
||||
import InputEmail from '../../components/Inputs/InputEmail.vue'
|
||||
import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation.vue'
|
||||
|
||||
export default {
|
||||
components: { InputPasswordConfirmation, InputEmail },
|
||||
name: 'register',
|
||||
data() {
|
||||
return {
|
||||
@ -234,15 +142,15 @@ export default {
|
||||
lastname: '',
|
||||
email: '',
|
||||
agree: false,
|
||||
password: '',
|
||||
passwordRepeat: '',
|
||||
password: {
|
||||
password: '',
|
||||
passwordRepeat: '',
|
||||
},
|
||||
},
|
||||
|
||||
passwordVisible: false,
|
||||
passwordVisibleRepeat: false,
|
||||
submitted: false,
|
||||
showError: false,
|
||||
messageError: '',
|
||||
register: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -254,36 +162,39 @@ export default {
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
email: '',
|
||||
password: '',
|
||||
passwordRepeat: '',
|
||||
password: {
|
||||
password: '',
|
||||
passwordRepeat: '',
|
||||
},
|
||||
agree: false,
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.observer.reset()
|
||||
})
|
||||
},
|
||||
togglePasswordVisibility() {
|
||||
this.passwordVisible = !this.passwordVisible
|
||||
},
|
||||
togglePasswordRepeatVisibility() {
|
||||
this.passwordVisibleRepeat = !this.passwordVisibleRepeat
|
||||
},
|
||||
async onSubmit() {
|
||||
const result = await loginAPI.create(
|
||||
this.form.email,
|
||||
this.form.firstname,
|
||||
this.form.lastname,
|
||||
this.form.password,
|
||||
this.form.password.password,
|
||||
)
|
||||
if (result.success) {
|
||||
this.$store.dispatch('login', {
|
||||
/* this.$store.dispatch('login', {
|
||||
sessionId: result.result.data.session_id,
|
||||
email: this.form.email,
|
||||
user: {
|
||||
email: this.form.email,
|
||||
firstName: this.form.firstname,
|
||||
lastName: this.form.lastname
|
||||
}
|
||||
|
||||
})
|
||||
*/
|
||||
this.form.email = ''
|
||||
this.form.firstname = ''
|
||||
this.form.lastname = ''
|
||||
this.password = ''
|
||||
this.passwordVisibleRepeat = ''
|
||||
this.form.password.password = ''
|
||||
|
||||
this.$router.push('/thx/register')
|
||||
} else {
|
||||
this.showError = true
|
||||
@ -296,16 +207,10 @@ export default {
|
||||
this.form.email = ''
|
||||
this.form.firstname = ''
|
||||
this.form.lastname = ''
|
||||
this.form.password = ''
|
||||
this.form.password.password = ''
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
samePasswords() {
|
||||
return this.form.password === this.form.passwordRepeat
|
||||
},
|
||||
passwordsFilled() {
|
||||
return this.form.password !== '' && this.form.passwordRepeat !== ''
|
||||
},
|
||||
namesFilled() {
|
||||
return (
|
||||
this.form.firstname !== '' &&
|
||||
@ -317,26 +222,6 @@ export default {
|
||||
emailFilled() {
|
||||
return this.form.email !== ''
|
||||
},
|
||||
rules() {
|
||||
return [
|
||||
{ message: this.$t('site.signup.lowercase'), regex: /[a-z]+/ },
|
||||
{ message: this.$t('site.signup.uppercase'), regex: /[A-Z]+/ },
|
||||
{ message: this.$t('site.signup.minimum'), regex: /.{8,}/ },
|
||||
{ message: this.$t('site.signup.one_number'), regex: /[0-9]+/ },
|
||||
]
|
||||
},
|
||||
passwordValidation() {
|
||||
const errors = []
|
||||
for (const condition of this.rules) {
|
||||
if (!condition.regex.test(this.form.password)) {
|
||||
errors.push(condition.message)
|
||||
}
|
||||
}
|
||||
if (errors.length === 0) {
|
||||
return { valid: true, errors }
|
||||
}
|
||||
return { valid: false, errors }
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
<b-card-body class="p-4">
|
||||
<validation-observer ref="observer" v-slot="{ handleSubmit }">
|
||||
<b-form role="form" @submit.prevent="handleSubmit(onSubmit)">
|
||||
<input-password-confirmation v-model="form" />
|
||||
<input-password-confirmation v-model="form" :register="register" />
|
||||
<div class="text-center">
|
||||
<b-button type="submit" variant="primary" class="mt-4">
|
||||
{{ $t('reset') }}
|
||||
@ -65,6 +65,7 @@ export default {
|
||||
sessionId: null,
|
||||
email: null,
|
||||
pending: true,
|
||||
register: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -3,26 +3,23 @@
|
||||
<div class="w-100 text-center">
|
||||
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
|
||||
</div>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<div class="card-profile-stats d-flex justify-content-center mt-md-5">
|
||||
<div>
|
||||
<span class="heading">
|
||||
{{ $n(balance, 'decimal') }}
|
||||
</span>
|
||||
<span class="description">GDD</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">{{ transactionCount }}</span>
|
||||
<span class="description">{{ $t('transactions') }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">--</span>
|
||||
<span class="description">{{ $t('community') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<div class="card-profile-stats d-flex justify-content-center mt-md-5">
|
||||
<div>
|
||||
<span class="heading">
|
||||
{{ $n(balance, 'decimal') }}
|
||||
</span>
|
||||
<span class="description">GDD</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">{{ transactionCount }}</span>
|
||||
<span class="description">{{ $t('transactions') }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">--</span>
|
||||
<span class="description">{{ $t('community') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@ -1,29 +1,22 @@
|
||||
<template>
|
||||
<div class="userdata_form">
|
||||
<b-card
|
||||
<div class="userdata_form" fluid="sm">
|
||||
<div
|
||||
id="userdata_form"
|
||||
class="bg-transparent"
|
||||
class="bg-transparent pt-3 pb-3"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-container>
|
||||
<b-row class="text-right">
|
||||
<b-col class="mb-3">
|
||||
<b-icon
|
||||
v-if="showUserData"
|
||||
@click="showUserData = !showUserData"
|
||||
class="pointer"
|
||||
icon="pencil"
|
||||
>
|
||||
{{ $t('form.change') }}
|
||||
</b-icon>
|
||||
<a @click="showUserData ? (showUserData = !showUserData) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-name') }}</span>
|
||||
|
||||
<b-icon
|
||||
v-else
|
||||
@click="cancelEdit"
|
||||
class="pointer"
|
||||
icon="x-circle"
|
||||
variant="danger"
|
||||
></b-icon>
|
||||
<b-icon v-if="showUserData" class="pointer" icon="pencil">
|
||||
{{ $t('form.change') }}
|
||||
</b-icon>
|
||||
|
||||
<b-icon v-else class="pointer" icon="x-circle" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
@ -81,7 +74,7 @@
|
||||
</b-row>
|
||||
</b-form>
|
||||
</b-container>
|
||||
</b-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<b-card id="change_pwd" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<div
|
||||
id="change_pwd"
|
||||
class="bg-transparent pt-3 pb-3"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-container>
|
||||
<div v-if="!editPassword">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a href="#change_pwd" @click="editPassword = !editPassword">
|
||||
<span>{{ $t('form.change-password') }}</span>
|
||||
<b-icon class="pointer ml-3" icon="pencil" />
|
||||
<a @click="!editPassword ? (editPassword = !editPassword) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-password') }}</span>
|
||||
<b-icon v-if="!editPassword" class="pointer ml-3" icon="pencil" />
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
<div v-if="editPassword">
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<b-icon @click="cancelEdit()" class="pointer" icon="x-circle" variant="danger"></b-icon>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<validation-observer ref="observer" v-slot="{ handleSubmit }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||
<b-row class="mb-2">
|
||||
@ -28,7 +28,7 @@
|
||||
></input-password>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<input-password-confirmation v-model="form.newPassword" />
|
||||
<input-password-confirmation v-model="form.newPassword" :register="register" />
|
||||
<b-row class="text-right">
|
||||
<b-col>
|
||||
<div class="text-right">
|
||||
@ -42,7 +42,7 @@
|
||||
</validation-observer>
|
||||
</div>
|
||||
</b-container>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import loginAPI from '../../../apis/loginAPI'
|
||||
@ -66,6 +66,7 @@ export default {
|
||||
passwordRepeat: '',
|
||||
},
|
||||
},
|
||||
register: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<b-container fluid>
|
||||
<div fluid="sm">
|
||||
<user-card :balance="balance" :transactionCount="transactionCount"></user-card>
|
||||
<form-user-data />
|
||||
<p><form-user-data /></p>
|
||||
<!--<form-username />-->
|
||||
<form-user-passwort />
|
||||
</b-container>
|
||||
<hr />
|
||||
<p><form-user-passwort /></p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UserCard from './UserProfile/UserCard.vue'
|
||||
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit 0b8d13a1d4cd9be16ed8a2230577aa9c296aa1ca
|
||||
Loading…
x
Reference in New Issue
Block a user