mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into bugfix_coinanimation_update_only_send_for_true
This commit is contained in:
commit
635c7b6e51
99
.github/workflows/test.yml
vendored
99
.github/workflows/test.yml
vendored
@ -29,6 +29,32 @@ jobs:
|
||||
name: docker-frontend-test
|
||||
path: /tmp/frontend.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST ADMIN INTERFACE #####################################
|
||||
##############################################################################
|
||||
build_test_admin:
|
||||
name: Docker Build Test - Admin Interface
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# ADMIN INTERFACE ########################################################
|
||||
##########################################################################
|
||||
- name: Admin | Build `test` image
|
||||
run: |
|
||||
docker build --target test -t "gradido/admin:test" admin/
|
||||
docker save "gradido/admin:test" > /tmp/admin.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker-admin-test
|
||||
path: /tmp/admin.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST BACKEND #############################################
|
||||
##############################################################################
|
||||
@ -240,7 +266,36 @@ jobs:
|
||||
run: docker run --rm gradido/frontend:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT BACKEND #########################################################
|
||||
# JOB: LINT ADMIN INTERFACE ##################################################
|
||||
##############################################################################
|
||||
lint_admin:
|
||||
name: Lint - Admin Interface
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_admin]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Admin Interface)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-admin-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/admin.tar
|
||||
##########################################################################
|
||||
# LINT ADMIN INTERFACE ###################################################
|
||||
##########################################################################
|
||||
- name: Admin Interface | Lint
|
||||
run: docker run --rm gradido/admin:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT BACKEND ##########################################################
|
||||
##############################################################################
|
||||
lint_backend:
|
||||
name: Lint - Backend
|
||||
@ -347,6 +402,48 @@ jobs:
|
||||
min_coverage: 86
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST ADMIN INTERFACE #############################################
|
||||
##############################################################################
|
||||
unit_test_admin:
|
||||
name: Unit tests - Admin Interface
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_admin]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# 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 ####################################################
|
||||
##############################################################################
|
||||
|
||||
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
@ -51,7 +51,6 @@ const email = {
|
||||
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',
|
||||
}
|
||||
|
||||
@ -2,9 +2,6 @@
|
||||
|
||||
import { AuthChecker } from 'type-graphql'
|
||||
|
||||
import CONFIG from '../../config'
|
||||
import { apiGet } from '../../apis/HttpRequest'
|
||||
|
||||
import decode from '../../jwt/decode'
|
||||
import encode from '../../jwt/encode'
|
||||
|
||||
@ -13,7 +10,7 @@ const isAuthorized: AuthChecker<any> = async (
|
||||
) => {
|
||||
if (context.token) {
|
||||
const decoded = decode(context.token)
|
||||
context.pubKey = decoded.pubKey
|
||||
context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
|
||||
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
|
||||
return true
|
||||
}
|
||||
|
||||
@ -10,15 +10,17 @@ export class User {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
*/
|
||||
constructor(json: any) {
|
||||
this.email = json.email
|
||||
this.firstName = json.first_name
|
||||
this.lastName = json.last_name
|
||||
this.username = json.username
|
||||
this.description = json.description
|
||||
this.pubkey = json.public_hex
|
||||
this.language = json.language
|
||||
this.publisherId = json.publisher_id
|
||||
constructor(json?: any) {
|
||||
if (json) {
|
||||
this.email = json.email
|
||||
this.firstName = json.first_name
|
||||
this.lastName = json.last_name
|
||||
this.username = json.username
|
||||
this.description = json.description
|
||||
this.pubkey = json.public_hex
|
||||
this.language = json.language
|
||||
this.publisherId = json.publisher_id
|
||||
}
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
|
||||
@ -613,9 +613,6 @@ export class TransactionResolver {
|
||||
await queryRunner.commitTransaction()
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw e
|
||||
} finally {
|
||||
await queryRunner.release()
|
||||
// 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
|
||||
@ -627,6 +624,9 @@ export class TransactionResolver {
|
||||
// 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
|
||||
|
||||
@ -22,14 +22,14 @@ import {
|
||||
} from '../../middleware/klicktippMiddleware'
|
||||
import { CheckEmailResponse } from '../model/CheckEmailResponse'
|
||||
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
|
||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
||||
import { Setting } from '../enum/Setting'
|
||||
import { UserRepository } from '../../typeorm/repository/User'
|
||||
import { LoginUser } from '@entity/LoginUser'
|
||||
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
|
||||
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
||||
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||
import { sendEMail } from '../../util/sendEMail'
|
||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
||||
import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const sodium = require('sodium-native')
|
||||
@ -201,33 +201,32 @@ export class UserResolver {
|
||||
@Ctx() context: any,
|
||||
): Promise<User> {
|
||||
email = email.trim().toLowerCase()
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
|
||||
|
||||
// if there is no user, throw an authentication error
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
|
||||
context.setHeaders.push({
|
||||
key: 'token',
|
||||
value: encode(result.data.user.public_hex),
|
||||
// const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
|
||||
// UnsecureLogin
|
||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||
const loginUser = await loginUserRepository.findByEmail(email).catch(() => {
|
||||
throw new Error('No user with this credentials')
|
||||
})
|
||||
const user = new User(result.data.user)
|
||||
// Hack: Database Field is not validated properly and not nullable
|
||||
if (user.publisherId === 0) {
|
||||
user.publisherId = undefined
|
||||
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
|
||||
const loginUserPassword = BigInt(loginUser.password.toString())
|
||||
if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) {
|
||||
throw new Error('No user with this credentials')
|
||||
}
|
||||
user.hasElopage = result.data.hasElopage
|
||||
// read additional settings from settings table
|
||||
// TODO: If user has no pubKey Create it again and update user.
|
||||
|
||||
const userRepository = getCustomRepository(UserRepository)
|
||||
let userEntity: void | DbUser
|
||||
userEntity = await userRepository.findByPubkeyHex(user.pubkey).catch(() => {
|
||||
const loginUserPubKey = loginUser.pubKey
|
||||
const loginUserPubKeyString = loginUserPubKey.toString('hex')
|
||||
userEntity = await userRepository.findByPubkeyHex(loginUserPubKeyString).catch(() => {
|
||||
// User not stored in state_users
|
||||
// TODO: Check with production data - email is unique which can cause problems
|
||||
userEntity = new DbUser()
|
||||
userEntity.firstName = user.firstName
|
||||
userEntity.lastName = user.lastName
|
||||
userEntity.username = user.username
|
||||
userEntity.email = user.email
|
||||
userEntity.pubkey = Buffer.from(user.pubkey, 'hex')
|
||||
userEntity.firstName = loginUser.firstName
|
||||
userEntity.lastName = loginUser.lastName
|
||||
userEntity.username = loginUser.username
|
||||
userEntity.email = loginUser.email
|
||||
userEntity.pubkey = loginUser.pubKey
|
||||
|
||||
userRepository.save(userEntity).catch(() => {
|
||||
throw new Error('error by save userEntity')
|
||||
@ -237,16 +236,28 @@ export class UserResolver {
|
||||
throw new Error('error with cannot happen')
|
||||
}
|
||||
|
||||
// Save publisherId if Elopage is not yet registered
|
||||
const user = new User()
|
||||
user.email = email
|
||||
user.firstName = loginUser.firstName
|
||||
user.lastName = loginUser.lastName
|
||||
user.username = loginUser.username
|
||||
user.description = loginUser.description
|
||||
user.pubkey = loginUserPubKeyString
|
||||
user.language = loginUser.language
|
||||
|
||||
// Elopage Status & Stored PublisherId
|
||||
user.hasElopage = await this.hasElopage({ pubKey: loginUserPubKeyString })
|
||||
if (!user.hasElopage && publisherId) {
|
||||
user.publisherId = publisherId
|
||||
|
||||
// TODO: Check if we can use updateUserInfos
|
||||
// await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey })
|
||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
|
||||
loginUser.publisherId = publisherId
|
||||
loginUserRepository.save(loginUser)
|
||||
}
|
||||
|
||||
// coinAnimation
|
||||
const userSettingRepository = getCustomRepository(UserSettingRepository)
|
||||
const coinanimation = await userSettingRepository
|
||||
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
|
||||
@ -254,6 +265,12 @@ export class UserResolver {
|
||||
throw new Error(error)
|
||||
})
|
||||
user.coinanimation = coinanimation
|
||||
|
||||
context.setHeaders.push({
|
||||
key: 'token',
|
||||
value: encode(loginUser.pubKey),
|
||||
})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@ -609,7 +626,8 @@ export class UserResolver {
|
||||
return false
|
||||
}
|
||||
|
||||
const elopageBuyCount = await LoginElopageBuys.count({ payerEmail: userEntity.email })
|
||||
const loginElopageBuysRepository = getCustomRepository(LoginElopageBuysRepository)
|
||||
const elopageBuyCount = await loginElopageBuysRepository.count({ payerEmail: userEntity.email })
|
||||
return elopageBuyCount > 0
|
||||
}
|
||||
}
|
||||
|
||||
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> {}
|
||||
@ -2,4 +2,10 @@ import { EntityRepository, Repository } from 'typeorm'
|
||||
import { LoginUser } from '@entity/LoginUser'
|
||||
|
||||
@EntityRepository(LoginUser)
|
||||
export class LoginUserRepository extends Repository<LoginUser> {}
|
||||
export class LoginUserRepository extends Repository<LoginUser> {
|
||||
async findByEmail(email: string): Promise<LoginUser> {
|
||||
return this.createQueryBuilder('loginUser')
|
||||
.where('loginUser.email = :email', { email })
|
||||
.getOneOrFail()
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,15 @@ export class UserRepository extends Repository<User> {
|
||||
.getOneOrFail()
|
||||
}
|
||||
|
||||
async findByPubkeyHexBuffer(pubkeyHexBuffer: Buffer): Promise<User> {
|
||||
const pubKeyString = pubkeyHexBuffer.toString('hex')
|
||||
return await this.findByPubkeyHex(pubKeyString)
|
||||
}
|
||||
|
||||
async findByEmail(email: string): Promise<User> {
|
||||
return this.createQueryBuilder('user').where('user.email = :email', { email }).getOneOrFail()
|
||||
}
|
||||
|
||||
async getUsersIndiced(userIds: number[]): Promise<User[]> {
|
||||
if (!userIds.length) return []
|
||||
const users = await this.createQueryBuilder('user')
|
||||
|
||||
@ -8,8 +8,6 @@ services:
|
||||
image: gradido/frontend:development
|
||||
build:
|
||||
target: development
|
||||
networks:
|
||||
- external-net
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
# - DEBUG=true
|
||||
@ -20,6 +18,23 @@ services:
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./frontend:/app
|
||||
|
||||
########################################################
|
||||
# ADMIN INTERFACE ######################################
|
||||
########################################################
|
||||
admin:
|
||||
image: gradido/admin:development
|
||||
build:
|
||||
target: development
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
# - DEBUG=true
|
||||
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
|
||||
- admin_node_modules:/app/node_modules
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./admin:/app
|
||||
|
||||
########################################################
|
||||
# BACKEND ##############################################
|
||||
########################################################
|
||||
@ -142,6 +157,7 @@ services:
|
||||
|
||||
volumes:
|
||||
frontend_node_modules:
|
||||
admin_node_modules:
|
||||
backend_node_modules:
|
||||
backend_database_node_modules:
|
||||
backend_database_build:
|
||||
|
||||
@ -15,6 +15,7 @@ services:
|
||||
context: ./frontend
|
||||
target: production
|
||||
networks:
|
||||
- external-net
|
||||
- internal-net
|
||||
ports:
|
||||
- 3000:3000
|
||||
@ -30,6 +31,31 @@ services:
|
||||
# - ./.env
|
||||
# - ./frontend/.env
|
||||
|
||||
########################################################
|
||||
# ADMIN INTERFACE ######################################
|
||||
########################################################
|
||||
admin:
|
||||
image: gradido/admin:latest
|
||||
build:
|
||||
context: ./admin
|
||||
target: production
|
||||
networks:
|
||||
- external-net
|
||||
- internal-net
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
# Envs used in Dockerfile
|
||||
# - DOCKER_WORKDIR="/app"
|
||||
# - PORT=8090
|
||||
# - BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||
# - BUILD_VERSION="0.0.0.0"
|
||||
# - BUILD_COMMIT="0000000"
|
||||
- NODE_ENV="production"
|
||||
# env_file:
|
||||
# - ./.env
|
||||
# - ./admin/.env
|
||||
|
||||
#########################################################
|
||||
## MARIADB ##############################################
|
||||
#########################################################
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
"change-password": "Fehler beim Ändern des Passworts",
|
||||
"error": "Fehler",
|
||||
"no-account": "Leider konnten wir keinen Account finden mit diesen Daten!",
|
||||
"no-email-verify": "Die Email wurde noch nicht bestätigt, bitte überprüfe deine Emails und klicke auf den Aktivierungslink!",
|
||||
"session-expired": "Sitzung abgelaufen!"
|
||||
},
|
||||
"form": {
|
||||
@ -180,9 +181,12 @@
|
||||
"uppercase": "Ein Großbuchstabe erforderlich."
|
||||
},
|
||||
"thx": {
|
||||
"activateEmail": "Deine Email wurde noch nicht aktiviert, bitte überprüfe deine Email und Klicke den Aktivierungslink!",
|
||||
"checkEmail": "Deine Email würde erfolgreich verifiziert.",
|
||||
"email": "Wir haben dir eine eMail gesendet.",
|
||||
"register": "Du bist jetzt registriert.",
|
||||
"emailActivated": "Danke dass Du deine Email bestätigt hast.",
|
||||
"errorTitle": "Achtung!",
|
||||
"register": "Du bist jetzt registriert, bitte überprüfe deine Emails und klicke auf den Aktivierungslink.",
|
||||
"reset": "Dein Passwort wurde geändert.",
|
||||
"title": "Danke!"
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
"change-password": "Error while changing password",
|
||||
"error": "Error",
|
||||
"no-account": "Unfortunately we could not find an account to the given data!",
|
||||
"no-email-verify": "Your email is not activated yet, please check your emails and click the activation link!",
|
||||
"session-expired": "The session expired"
|
||||
},
|
||||
"form": {
|
||||
@ -180,9 +181,12 @@
|
||||
"uppercase": "One uppercase letter required."
|
||||
},
|
||||
"thx": {
|
||||
"activateEmail": "Your email has not been activated yet, please check your emails and click the activation link!",
|
||||
"checkEmail": "Your email has been successfully verified.",
|
||||
"email": "We have sent you an email.",
|
||||
"register": "You are registred now.",
|
||||
"emailActivated": "Thank you your email has been activated.",
|
||||
"errorTitle": "Attention!",
|
||||
"register": "You are registered now, please check your emails and click the activation link.",
|
||||
"reset": "Your password has been changed.",
|
||||
"title": "Thank you!"
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ const routes = [
|
||||
path: '/thx/:comingFrom',
|
||||
component: () => import('../views/Pages/thx.vue'),
|
||||
beforeEnter: (to, from, next) => {
|
||||
const validFrom = ['password', 'reset', 'register']
|
||||
const validFrom = ['password', 'reset', 'register', 'login']
|
||||
if (!validFrom.includes(from.path.split('/')[1])) {
|
||||
next({ path: '/login' })
|
||||
} else {
|
||||
|
||||
@ -104,9 +104,14 @@ export default {
|
||||
this.$router.push('/overview')
|
||||
loader.hide()
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((error) => {
|
||||
if (!error.message.includes('user email not validated')) {
|
||||
this.$toasted.error(this.$t('error.no-account'))
|
||||
} else {
|
||||
// : this.$t('error.no-email-verify')
|
||||
this.$router.push('/thx/login')
|
||||
}
|
||||
loader.hide()
|
||||
this.$toasted.error(this.$t('error.no-account'))
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@ -4,10 +4,12 @@
|
||||
<div class="header py-7 py-lg-8 pt-lg-9">
|
||||
<b-container>
|
||||
<div class="header-body text-center mb-7">
|
||||
<p class="h1">{{ $t('site.thx.title') }}</p>
|
||||
<p class="h1">{{ $t(displaySetup.headline) }}</p>
|
||||
<p class="h4">{{ $t(displaySetup.subtitle) }}</p>
|
||||
<hr />
|
||||
<b-button :to="displaySetup.linkTo">{{ $t(displaySetup.button) }}</b-button>
|
||||
<b-button v-if="displaySetup.linkTo" :to="displaySetup.linkTo">
|
||||
{{ $t(displaySetup.button) }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-container>
|
||||
</div>
|
||||
@ -17,25 +19,33 @@
|
||||
<script>
|
||||
const textFields = {
|
||||
password: {
|
||||
headline: 'site.thx.title',
|
||||
subtitle: 'site.thx.email',
|
||||
button: 'login',
|
||||
linkTo: '/login',
|
||||
},
|
||||
reset: {
|
||||
headline: 'site.thx.title',
|
||||
subtitle: 'site.thx.reset',
|
||||
button: 'login',
|
||||
linkTo: '/login',
|
||||
},
|
||||
register: {
|
||||
headline: 'site.thx.title',
|
||||
subtitle: 'site.thx.register',
|
||||
button: 'site.login.signin',
|
||||
linkTo: '/overview',
|
||||
},
|
||||
checkEmail: {
|
||||
headline: 'site.thx.title',
|
||||
subtitle: 'site.thx.checkEmail',
|
||||
button: 'login',
|
||||
linkTo: '/login',
|
||||
},
|
||||
login: {
|
||||
headline: 'site.thx.errorTitle',
|
||||
subtitle: 'site.thx.activateEmail',
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@ -147,7 +147,6 @@ Poco::JSON::Object* JsonUnsecureLogin::handle(Poco::Dynamic::Var params)
|
||||
infos.add("set user.group_id to default group_id = 1");
|
||||
case USER_NO_PRIVATE_KEY:
|
||||
case USER_COMPLETE:
|
||||
case USER_EMAIL_NOT_ACTIVATED:
|
||||
result->set("state", "success");
|
||||
result->set("user", session->getNewUser()->getJson());
|
||||
result->set("session_id", session->getHandle());
|
||||
@ -158,6 +157,10 @@ Poco::JSON::Object* JsonUnsecureLogin::handle(Poco::Dynamic::Var params)
|
||||
AWAIT(hasElopageTask)
|
||||
result->set("hasElopage", hasElopageTask->hasElopage());
|
||||
return result;
|
||||
case USER_EMAIL_NOT_ACTIVATED:
|
||||
result->set("state", "processing");
|
||||
result->set("msg", "user email not validated");
|
||||
break;
|
||||
default:
|
||||
result->set("state", "error");
|
||||
result->set("msg", "unknown user state");
|
||||
|
||||
@ -69,7 +69,19 @@ server {
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /sockjs-node {
|
||||
location /admin {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
proxy_pass http://admin:8080;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /sockjs-node {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user