Merge branch 'master' into add-sender-email-to-transaction-mail

This commit is contained in:
Moriz Wahl 2022-03-23 20:59:31 +01:00 committed by GitHub
commit bd79d63783
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 359 additions and 173 deletions

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=v1.2022-03-18
GRAPHQL_URI=http://localhost:4000/graphql GRAPHQL_URI=http://localhost:4000/graphql
WALLET_AUTH_URL=http://localhost/authenticate?token={token} WALLET_AUTH_URL=http://localhost/authenticate?token={token}
WALLET_URL=http://localhost/login WALLET_URL=http://localhost/login

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=$ADMIN_CONFIG_VERSION
GRAPHQL_URI=$GRAPHQL_URI GRAPHQL_URI=$GRAPHQL_URI
WALLET_AUTH_URL=$WALLET_AUTH_URL WALLET_AUTH_URL=$WALLET_AUTH_URL
WALLET_URL=$WALLET_URL WALLET_URL=$WALLET_URL

View File

@ -4,11 +4,20 @@
// Load Package Details for some default values // Load Package Details for some default values
const pkg = require('../../package') const pkg = require('../../package')
const constants = {
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v1.2022-03-18',
CURRENT: '',
},
}
const version = { const version = {
APP_VERSION: pkg.version, APP_VERSION: pkg.version,
BUILD_COMMIT: process.env.BUILD_COMMIT || null, BUILD_COMMIT: process.env.BUILD_COMMIT || null,
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code // 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), BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').slice(0, 7),
PORT: process.env.PORT || 8080,
} }
const environment = { const environment = {
@ -27,14 +36,24 @@ const debug = {
DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false, DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false,
} }
const options = {} // Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
if (
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
constants.CONFIG_VERSION.CURRENT,
)
) {
throw new Error(
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
)
}
const CONFIG = { const CONFIG = {
...constants,
...version, ...version,
...environment, ...environment,
...endpoints, ...endpoints,
...options,
...debug, ...debug,
} }
export default CONFIG module.exports = CONFIG

View File

@ -2,11 +2,12 @@ const path = require('path')
const webpack = require('webpack') const webpack = require('webpack')
const Dotenv = require('dotenv-webpack') const Dotenv = require('dotenv-webpack')
const StatsPlugin = require('stats-webpack-plugin') const StatsPlugin = require('stats-webpack-plugin')
const CONFIG = require('./src/config')
// vue.config.js // vue.config.js
module.exports = { module.exports = {
devServer: { devServer: {
port: process.env.PORT || 8080, port: CONFIG.PORT,
}, },
pluginOptions: { pluginOptions: {
i18n: { i18n: {
@ -34,7 +35,7 @@ module.exports = {
// 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR), // 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR),
// 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE), // 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE),
// 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION), // 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION),
'process.env.BUILD_COMMIT': JSON.stringify(process.env.BUILD_COMMIT), 'process.env.BUILD_COMMIT': JSON.stringify(CONFIG.BUILD_COMMIT),
// 'process.env.PORT': JSON.stringify(process.env.PORT), // 'process.env.PORT': JSON.stringify(process.env.PORT),
}), }),
// generate webpack stats to allow analysis of the bundlesize // generate webpack stats to allow analysis of the bundlesize
@ -46,7 +47,7 @@ module.exports = {
}, },
css: { css: {
// Enable CSS source maps. // Enable CSS source maps.
sourceMap: process.env.NODE_ENV !== 'production', sourceMap: CONFIG.NODE_ENV !== 'production',
}, },
outputDir: path.resolve(__dirname, './dist'), outputDir: path.resolve(__dirname, './dist'),
} }

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=v1.2022-03-18
# Server # Server
PORT=4000 PORT=4000
JWT_SECRET=secret123 JWT_SECRET=secret123
@ -38,8 +40,8 @@ EMAIL_SENDER=info@gradido.net
EMAIL_PASSWORD=xxx EMAIL_PASSWORD=xxx
EMAIL_SMTP_URL=gmail.com EMAIL_SMTP_URL=gmail.com
EMAIL_SMTP_PORT=587 EMAIL_SMTP_PORT=587
EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{code} EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin}{code}
EMAIL_LINK_SETPASSWORD=http://localhost/reset/{code} EMAIL_LINK_SETPASSWORD=http://localhost/reset/{optin}
EMAIL_CODE_VALID_TIME=10 EMAIL_CODE_VALID_TIME=10
# Webhook # Webhook

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=$BACKEND_CONFIG_VERSION
# Server # Server
JWT_SECRET=$JWT_SECRET JWT_SECRET=$JWT_SECRET
JWT_EXPIRES_IN=10m JWT_EXPIRES_IN=10m

View File

@ -12,6 +12,11 @@ Decimal.set({
const constants = { const constants = {
DB_VERSION: '0033-add_referrer_id', DB_VERSION: '0033-add_referrer_id',
DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v1.2022-03-18',
CURRENT: '',
},
} }
const server = { const server = {
@ -62,9 +67,9 @@ const email = {
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com', EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com',
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587',
EMAIL_LINK_VERIFICATION: EMAIL_LINK_VERIFICATION:
process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{code}', process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}',
EMAIL_LINK_SETPASSWORD: EMAIL_LINK_SETPASSWORD:
process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{code}', process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{optin}',
EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME
? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 10 ? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 10
: 10, : 10,
@ -78,6 +83,18 @@ const webhook = {
// This is needed by graphql-directive-auth // This is needed by graphql-directive-auth
process.env.APP_SECRET = server.JWT_SECRET process.env.APP_SECRET = server.JWT_SECRET
// Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
if (
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
constants.CONFIG_VERSION.CURRENT,
)
) {
throw new Error(
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
)
}
const CONFIG = { const CONFIG = {
...constants, ...constants,
...server, ...server,

View File

@ -16,4 +16,7 @@ export default class CreateUserArgs {
@Field(() => Int, { nullable: true }) @Field(() => Int, { nullable: true })
publisherId: number publisherId: number
@Field(() => String, { nullable: true })
redeemCode?: string | null
} }

View File

@ -124,7 +124,10 @@ describe('UserResolver', () => {
describe('account activation email', () => { describe('account activation email', () => {
it('sends an account activation email', () => { it('sends an account activation email', () => {
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{code}/g, emailOptIn) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
/{optin}/g,
emailOptIn,
).replace(/{code}/g, '')
expect(sendAccountActivationEmail).toBeCalledWith({ expect(sendAccountActivationEmail).toBeCalledWith({
link: activationLink, link: activationLink,
firstName: 'Peter', firstName: 'Peter',

View File

@ -7,6 +7,7 @@ import { getConnection, getCustomRepository, QueryRunner } from '@dbTools/typeor
import CONFIG from '@/config' import CONFIG from '@/config'
import { User } from '@model/User' import { User } from '@model/User'
import { User as DbUser } from '@entity/User' import { User as DbUser } from '@entity/User'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { encode } from '@/auth/JWT' import { encode } from '@/auth/JWT'
import CreateUserArgs from '@arg/CreateUserArgs' import CreateUserArgs from '@arg/CreateUserArgs'
import UnsecureLoginArgs from '@arg/UnsecureLoginArgs' import UnsecureLoginArgs from '@arg/UnsecureLoginArgs'
@ -305,7 +306,8 @@ export class UserResolver {
@Authorized([RIGHTS.CREATE_USER]) @Authorized([RIGHTS.CREATE_USER])
@Mutation(() => User) @Mutation(() => User)
async createUser( async createUser(
@Args() { email, firstName, lastName, language, publisherId }: CreateUserArgs, @Args()
{ email, firstName, lastName, language, publisherId, redeemCode = null }: CreateUserArgs,
): Promise<User> { ): Promise<User> {
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
// default int publisher_id = 0; // default int publisher_id = 0;
@ -338,6 +340,12 @@ export class UserResolver {
dbUser.language = language dbUser.language = language
dbUser.publisherId = publisherId dbUser.publisherId = publisherId
dbUser.passphrase = passphrase.join(' ') dbUser.passphrase = passphrase.join(' ')
if (redeemCode) {
const transactionLink = await dbTransactionLink.findOne({ code: redeemCode })
if (transactionLink) {
dbUser.referrerId = transactionLink.userId
}
}
// TODO this field has no null allowed unlike the loginServer table // TODO this field has no null allowed unlike the loginServer table
// dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... // dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000...
// dbUser.pubkey = keyPair[0] // dbUser.pubkey = keyPair[0]
@ -360,9 +368,9 @@ export class UserResolver {
const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner) const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner)
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
/{code}/g, /{optin}/g,
emailOptIn.verificationCode.toString(), emailOptIn.verificationCode.toString(),
) ).replace(/{code}/g, redeemCode ? '/' + redeemCode : '')
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendAccountActivationEmail({ const emailSent = await sendAccountActivationEmail({
@ -379,6 +387,7 @@ export class UserResolver {
console.log(`Account confirmation link: ${activationLink}`) console.log(`Account confirmation link: ${activationLink}`)
} }
*/ */
await queryRunner.commitTransaction() await queryRunner.commitTransaction()
} catch (e) { } catch (e) {
await queryRunner.rollbackTransaction() await queryRunner.rollbackTransaction()
@ -404,7 +413,7 @@ export class UserResolver {
const emailOptIn = await createEmailOptIn(user.id, queryRunner) const emailOptIn = await createEmailOptIn(user.id, queryRunner)
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
/{code}/g, /{optin}/g,
emailOptIn.verificationCode.toString(), emailOptIn.verificationCode.toString(),
) )
@ -443,7 +452,7 @@ export class UserResolver {
const optInCode = await getOptInCode(user.id) const optInCode = await getOptInCode(user.id)
const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace( const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace(
/{code}/g, /{optin}/g,
optInCode.verificationCode.toString(), optInCode.verificationCode.toString(),
) )

View File

@ -45,6 +45,7 @@ export const createUser = gql`
$email: String! $email: String!
$language: String! $language: String!
$publisherId: Int $publisherId: Int
$redeemCode: String
) { ) {
createUser( createUser(
email: $email email: $email
@ -52,6 +53,7 @@ export const createUser = gql`
lastName: $lastName lastName: $lastName
language: $language language: $language
publisherId: $publisherId publisherId: $publisherId
redeemCode: $redeemCode
) { ) {
id id
} }

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=v1.2022-03-18
DB_HOST=localhost DB_HOST=localhost
DB_PORT=3306 DB_PORT=3306
DB_USER=root DB_USER=root

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=$DATABASE_CONFIG_VERSION
DB_HOST=localhost DB_HOST=localhost
DB_PORT=3306 DB_PORT=3306
DB_USER=$DB_USER DB_USER=$DB_USER

View File

@ -3,6 +3,14 @@
import dotenv from 'dotenv' import dotenv from 'dotenv'
dotenv.config() dotenv.config()
const constants = {
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v1.2022-03-18',
CURRENT: '',
},
}
const database = { const database = {
DB_HOST: process.env.DB_HOST || 'localhost', DB_HOST: process.env.DB_HOST || 'localhost',
DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306, DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
@ -15,6 +23,18 @@ const migrations = {
MIGRATIONS_TABLE: process.env.MIGRATIONS_TABLE || 'migrations', MIGRATIONS_TABLE: process.env.MIGRATIONS_TABLE || 'migrations',
} }
const CONFIG = { ...database, ...migrations } // Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
if (
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
constants.CONFIG_VERSION.CURRENT,
)
) {
throw new Error(
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
)
}
const CONFIG = { ...constants, ...database, ...migrations }
export default CONFIG export default CONFIG

View File

@ -18,6 +18,8 @@ WEBHOOK_GITHUB_SECRET=secret
WEBHOOK_GITHUB_BRANCH=master WEBHOOK_GITHUB_BRANCH=master
# backend # backend
BACKEND_CONFIG_VERSION=v1.2022-03-18
EMAIL=true EMAIL=true
EMAIL_USERNAME=peter@lustig.de EMAIL_USERNAME=peter@lustig.de
EMAIL_SENDER=peter@lustig.de EMAIL_SENDER=peter@lustig.de
@ -43,6 +45,9 @@ KLICKTIPP_PASSWORD=
KLICKTIPP_APIKEY_DE= KLICKTIPP_APIKEY_DE=
KLICKTIPP_APIKEY_EN= KLICKTIPP_APIKEY_EN=
# database
DATABASE_CONFIG_VERSION=v1.2022-03-18
# frontend # frontend
GRAPHQL_URI=https://stage1.gradido.net/graphql GRAPHQL_URI=https://stage1.gradido.net/graphql
ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token}

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=v1.2022-03-18
META_URL=http://localhost META_URL=http://localhost
META_TITLE_DE="Gradido Dein Dankbarkeitskonto" META_TITLE_DE="Gradido Dein Dankbarkeitskonto"
META_TITLE_EN="Gradido - Your gratitude account" META_TITLE_EN="Gradido - Your gratitude account"

View File

@ -1,3 +1,5 @@
CONFIG_VERSION=$FRONTEND_CONFIG_VERSION
META_URL=$META_URL META_URL=$META_URL
META_TITLE_DE=$META_TITLE_DE META_TITLE_DE=$META_TITLE_DE
META_TITLE_EN=$META_TITLE_EN META_TITLE_EN=$META_TITLE_EN

View File

@ -160,15 +160,18 @@ export default {
}, },
props: { props: {
balance: { type: Number, default: 0 }, balance: { type: Number, default: 0 },
email: { type: String, default: '' },
amount: { type: Number, default: 0 },
memo: { type: String, default: '' },
}, },
data() { data() {
return { return {
amountFocused: false, amountFocused: false,
emailFocused: false, emailFocused: false,
form: { form: {
email: '', email: this.email,
amount: '', amount: this.amount ? String(this.amount) : '',
memo: '', memo: this.memo,
amountValue: 0.0, amountValue: 0.0,
}, },
selected: SEND_TYPES.send, selected: SEND_TYPES.send,

View File

@ -31,7 +31,7 @@
</template> </template>
<script> <script>
export default { export default {
name: 'TransactionResultSend', name: 'TransactionResultSendError',
props: { props: {
error: { type: Boolean, default: false }, error: { type: Boolean, default: false },
errorResult: { type: String, default: '' }, errorResult: { type: String, default: '' },

View File

@ -4,11 +4,19 @@
// Load Package Details for some default values // Load Package Details for some default values
const pkg = require('../../package') const pkg = require('../../package')
const constants = {
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v1.2022-03-18',
CURRENT: '',
},
}
const version = { const version = {
APP_VERSION: pkg.version, APP_VERSION: pkg.version,
BUILD_COMMIT: process.env.BUILD_COMMIT || null, BUILD_COMMIT: process.env.BUILD_COMMIT || null,
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code // 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), BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').slice(0, 7),
} }
const environment = { const environment = {
@ -16,39 +24,51 @@ const environment = {
DEBUG: process.env.NODE_ENV !== 'production' || false, DEBUG: process.env.NODE_ENV !== 'production' || false,
PRODUCTION: process.env.NODE_ENV === 'production' || false, PRODUCTION: process.env.NODE_ENV === 'production' || false,
DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID || 2896, DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID || 2896,
PORT: process.env.PORT || 3000,
} }
// const meta = { const meta = {
// META_URL: process.env.META_URL || 'http://localhost', META_URL: process.env.META_URL || 'http://localhost',
// META_TITLE_DE: process.env.META_TITLE_DE || 'Gradido Dein Dankbarkeitskonto', META_TITLE_DE: process.env.META_TITLE_DE || 'Gradido Dein Dankbarkeitskonto',
// META_TITLE_EN: process.env.META_TITLE_EN || 'Gradido - Your gratitude account', META_TITLE_EN: process.env.META_TITLE_EN || 'Gradido - Your gratitude account',
// META_DESCRIPTION_DE: META_DESCRIPTION_DE:
// process.env.META_DESCRIPTION_DE || process.env.META_DESCRIPTION_DE ||
// 'Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle.', 'Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle.',
// META_DESCRIPTION_EN: META_DESCRIPTION_EN:
// process.env.META_DESCRIPTION_EN || process.env.META_DESCRIPTION_EN ||
// 'Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all.', 'Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all.',
// META_KEYWORDS_DE: META_KEYWORDS_DE:
// process.env.META_KEYWORDS_DE || process.env.META_KEYWORDS_DE ||
// 'Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem', 'Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem',
// META_KEYWORDS_EN: META_KEYWORDS_EN:
// process.env.META_KEYWORDS_EN || process.env.META_KEYWORDS_EN ||
// 'Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System', 'Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System',
// META_AUTHOR: process.env.META_AUTHOR || 'Bernd Hückstädt - Gradido-Akademie', META_AUTHOR: process.env.META_AUTHOR || 'Bernd Hückstädt - Gradido-Akademie',
// } }
const endpoints = { const endpoints = {
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost/graphql', GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost/graphql',
ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost/admin/authenticate?token={token}', ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost/admin/authenticate?token={token}',
} }
const options = {} // Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
if (
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
constants.CONFIG_VERSION.CURRENT,
)
) {
throw new Error(
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
)
}
const CONFIG = { const CONFIG = {
...constants,
...version, ...version,
...environment, ...environment,
...endpoints, ...endpoints,
...options, ...meta,
} }
export default CONFIG module.exports = CONFIG

View File

@ -45,6 +45,7 @@ export const createUser = gql`
$email: String! $email: String!
$language: String! $language: String!
$publisherId: Int $publisherId: Int
$redeemCode: String
) { ) {
createUser( createUser(
email: $email email: $email
@ -52,6 +53,7 @@ export const createUser = gql`
lastName: $lastName lastName: $lastName
language: $language language: $language
publisherId: $publisherId publisherId: $publisherId
redeemCode: $redeemCode
) { ) {
id id
} }

View File

@ -140,14 +140,6 @@ describe('DashboardLayoutGdd', () => {
}) })
}) })
describe('update balance', () => {
it('updates the amount correctelly', async () => {
await wrapper.findComponent({ ref: 'router-view' }).vm.$emit('update-balance', 5)
await flushPromises()
expect(wrapper.vm.balance).toBe(-5)
})
})
describe('update transactions', () => { describe('update transactions', () => {
beforeEach(async () => { beforeEach(async () => {
apolloMock.mockResolvedValue({ apolloMock.mockResolvedValue({

View File

@ -27,7 +27,6 @@
:transactionLinkCount="transactionLinkCount" :transactionLinkCount="transactionLinkCount"
:pending="pending" :pending="pending"
:decayStartBlock="decayStartBlock" :decayStartBlock="decayStartBlock"
@update-balance="updateBalance"
@update-transactions="updateTransactions" @update-transactions="updateTransactions"
></router-view> ></router-view>
</fade-transition> </fade-transition>
@ -112,9 +111,6 @@ export default {
// what to do when loading balance fails? // what to do when loading balance fails?
}) })
}, },
updateBalance(ammount) {
this.balance -= ammount
},
admin() { admin() {
window.location.assign(CONFIG.ADMIN_AUTH_URL.replace('{token}', this.$store.state.token)) window.location.assign(CONFIG.ADMIN_AUTH_URL.replace('{token}', this.$store.state.token))
this.$store.dispatch('logout') // logout without redirect this.$store.dispatch('logout') // logout without redirect

View File

@ -54,25 +54,27 @@ describe('ResetPassword', () => {
describe('mount', () => { describe('mount', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper() wrapper = Wrapper()
}) })
describe('No valid optin', () => { describe('no valid optin', () => {
it.skip('does not render the Reset Password form when not authenticated', () => { beforeEach(() => {
expect(wrapper.find('form').exists()).toBeFalsy() jest.clearAllMocks()
apolloQueryMock.mockRejectedValue({ message: 'Your time is up!' })
wrapper = Wrapper()
}) })
it.skip('toasts an error when no valid optin is given', () => { it('toasts an error when no valid optin is given', () => {
expect(toastErrorSpy).toHaveBeenCalledWith('error') expect(toastErrorSpy).toHaveBeenCalledWith('Your time is up!')
}) })
it.skip('has a message suggesting to contact the support', () => { it('redirects to /forgot-password/resetPassword', () => {
expect(wrapper.find('div.header').text()).toContain('settings.password.reset') expect(routerPushMock).toBeCalledWith('/forgot-password/resetPassword')
expect(wrapper.find('div.header').text()).toContain('settings.password.not-authenticated')
}) })
}) })
describe('is authenticated', () => { describe('valid optin', () => {
it('renders the Reset Password form when authenticated', () => { it('renders the Reset Password form when authenticated', () => {
expect(wrapper.find('div.resetpwd-form').exists()).toBeTruthy() expect(wrapper.find('div.resetpwd-form').exists()).toBeTruthy()
}) })
@ -148,7 +150,6 @@ describe('ResetPassword', () => {
describe('server response with error code > 10min', () => { describe('server response with error code > 10min', () => {
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks()
apolloMutationMock.mockRejectedValue({ message: '...Code is older than 10 minutes' }) apolloMutationMock.mockRejectedValue({ message: '...Code is older than 10 minutes' })
await wrapper.find('form').trigger('submit') await wrapper.find('form').trigger('submit')
await flushPromises() await flushPromises()
@ -163,7 +164,7 @@ describe('ResetPassword', () => {
}) })
}) })
describe('server response with error code > 10min', () => { describe('server response with error', () => {
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks() jest.clearAllMocks()
apolloMutationMock.mockRejectedValueOnce({ message: 'Error' }) apolloMutationMock.mockRejectedValueOnce({ message: 'Error' })
@ -178,6 +179,7 @@ describe('ResetPassword', () => {
describe('server response with success on /checkEmail', () => { describe('server response with success on /checkEmail', () => {
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks()
mocks.$route.path.mock = 'checkEmail' mocks.$route.path.mock = 'checkEmail'
apolloMutationMock.mockResolvedValue({ apolloMutationMock.mockResolvedValue({
data: { data: {
@ -204,6 +206,28 @@ describe('ResetPassword', () => {
it('redirects to "/thx/checkEmail"', () => { it('redirects to "/thx/checkEmail"', () => {
expect(routerPushMock).toHaveBeenCalledWith('/thx/checkEmail') expect(routerPushMock).toHaveBeenCalledWith('/thx/checkEmail')
}) })
describe('with param code', () => {
beforeEach(async () => {
mocks.$route.params.code = 'the-most-secret-code-ever'
apolloMutationMock.mockResolvedValue({
data: {
resetPassword: 'success',
},
})
wrapper = Wrapper()
await wrapper.findAll('input').at(0).setValue('Aa123456_')
await wrapper.findAll('input').at(1).setValue('Aa123456_')
await wrapper.find('form').trigger('submit')
await flushPromises()
})
it('redirects to "/thx/checkEmail/the-most-secret-code-ever"', () => {
expect(routerPushMock).toHaveBeenCalledWith(
'/thx/checkEmail/the-most-secret-code-ever',
)
})
})
}) })
describe('server response with success on /reset-password', () => { describe('server response with success on /reset-password', () => {

View File

@ -6,11 +6,11 @@
<b-row class="justify-content-center"> <b-row class="justify-content-center">
<b-col xl="5" lg="6" md="8" class="px-2"> <b-col xl="5" lg="6" md="8" class="px-2">
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys--> <!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
<h1>{{ $t(displaySetup.authenticated) }}</h1> <h1>{{ $t(displaySetup.title) }}</h1>
<div class="pb-4"> <div class="pb-4">
<span> <span>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys--> <!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
{{ $t(displaySetup.notAuthenticated) }} {{ $t(displaySetup.text) }}
</span> </span>
</div> </div>
</b-col> </b-col>
@ -53,14 +53,14 @@ import { queryOptIn } from '@/graphql/queries'
const textFields = { const textFields = {
reset: { reset: {
authenticated: 'settings.password.change-password', title: 'settings.password.change-password',
notAuthenticated: 'settings.password.reset-password.text', text: 'settings.password.reset-password.text',
button: 'settings.password.change-password', button: 'settings.password.change-password',
linkTo: '/login', linkTo: '/login',
}, },
checkEmail: { checkEmail: {
authenticated: 'settings.password.set', title: 'settings.password.set',
notAuthenticated: 'settings.password.set-password.text', text: 'settings.password.set-password.text',
button: 'settings.password.set', button: 'settings.password.set',
linkTo: '/login', linkTo: '/login',
}, },
@ -97,7 +97,11 @@ export default {
.then(() => { .then(() => {
this.form.password = '' this.form.password = ''
if (this.$route.path.includes('checkEmail')) { if (this.$route.path.includes('checkEmail')) {
this.$router.push('/thx/checkEmail') if (this.$route.params.code) {
this.$router.push('/thx/checkEmail/' + this.$route.params.code)
} else {
this.$router.push('/thx/checkEmail')
}
} else { } else {
this.$router.push('/thx/resetPassword') this.$router.push('/thx/resetPassword')
} }

View File

@ -17,9 +17,7 @@ describe('Send', () => {
const propsData = { const propsData = {
balance: 123.45, balance: 123.45,
GdtBalance: 1234.56, GdtBalance: 1234.56,
transactions: [{ balance: 0.1 }],
pending: true, pending: true,
currentTransactionStep: TRANSACTION_STEPS.transactionConfirmationSend,
} }
const mocks = { const mocks = {
@ -45,11 +43,10 @@ describe('Send', () => {
}) })
it('has a send field', () => { it('has a send field', () => {
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy() expect(wrapper.find('div.gdd-send').exists()).toBe(true)
}) })
/* SEND */ describe('fill transaction form for send coins', () => {
describe('transaction form send', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', { wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
email: 'user@example.org', email: 'user@example.org',
@ -58,86 +55,92 @@ describe('Send', () => {
selected: SEND_TYPES.send, selected: SEND_TYPES.send,
}) })
}) })
it('steps forward in the dialog', () => { it('steps forward in the dialog', () => {
expect(wrapper.findComponent({ name: 'TransactionConfirmationSend' }).exists()).toBe(true) expect(wrapper.findComponent({ name: 'TransactionConfirmationSend' }).exists()).toBe(true)
}) })
})
describe('confirm transaction if selected: SEND_TYPES.send', () => { describe('confirm transaction view', () => {
beforeEach(() => { describe('cancel confirmation', () => {
wrapper.setData({ beforeEach(async () => {
currentTransactionStep: TRANSACTION_STEPS.transactionConfirmationSend, await wrapper
transactionData: { .findComponent({ name: 'TransactionConfirmationSend' })
email: 'user@example.org', .vm.$emit('on-reset')
amount: 23.45, })
memo: 'Make the best of it!',
selected: SEND_TYPES.send,
},
})
})
it('resets the transaction process when on-reset is emitted', async () => { it('shows the transaction formular again', () => {
await wrapper.findComponent({ name: 'TransactionConfirmationSend' }).vm.$emit('on-reset') expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBe(true)
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy() })
expect(wrapper.vm.transactionData).toEqual({
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
selected: SEND_TYPES.send,
})
})
describe('transaction is confirmed and server response is success', () => { it('restores the previous data in the formular', () => {
beforeEach(async () => { expect(wrapper.find('#input-group-1').find('input').vm.$el.value).toBe(
jest.clearAllMocks() 'user@example.org',
await wrapper )
.findComponent({ name: 'TransactionConfirmationSend' }) expect(wrapper.find('#input-group-2').find('input').vm.$el.value).toBe('23.45')
.vm.$emit('send-transaction') expect(wrapper.find('#input-group-3').find('textarea').vm.$el.value).toBe(
'Make the best of it!',
)
})
}) })
it('calls the API when send-transaction is emitted', async () => { describe('confirm transaction with server succees', () => {
expect(apolloMutationMock).toBeCalledWith( beforeEach(async () => {
expect.objectContaining({ jest.clearAllMocks()
mutation: sendCoins, await wrapper
variables: { .findComponent({ name: 'TransactionConfirmationSend' })
email: 'user@example.org', .vm.$emit('send-transaction')
amount: 23.45, })
memo: 'Make the best of it!',
selected: SEND_TYPES.send, it('calls the API when send-transaction is emitted', async () => {
}, expect(apolloMutationMock).toBeCalledWith(
}), expect.objectContaining({
) mutation: sendCoins,
variables: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
selected: SEND_TYPES.send,
},
}),
)
})
it('emits update transactions', () => {
expect(wrapper.emitted('update-transactions')).toBeTruthy()
expect(wrapper.emitted('update-transactions')).toEqual(expect.arrayContaining([[{}]]))
})
it('shows the success page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
})
}) })
it('emits update-balance', () => { describe('confirm transaction with server error', () => {
expect(wrapper.emitted('update-balance')).toBeTruthy() beforeEach(async () => {
expect(wrapper.emitted('update-balance')).toEqual([[23.45]]) jest.clearAllMocks()
}) apolloMutationMock.mockRejectedValue({ message: 'recipient not known' })
await wrapper
.findComponent({ name: 'TransactionConfirmationSend' })
.vm.$emit('send-transaction')
})
it('shows the success page', () => { it('has a component TransactionResultSendError', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success') expect(wrapper.findComponent({ name: 'TransactionResultSendError' }).exists()).toBe(
}) true,
}) )
})
describe('transaction is confirmed and server response is error', () => { it('has an standard error text', () => {
beforeEach(async () => { expect(wrapper.find('.test-send_transaction_error').text()).toContain(
jest.clearAllMocks() 'form.send_transaction_error',
apolloMutationMock.mockRejectedValue({ message: 'recipient not known' }) )
await wrapper })
.findComponent({ name: 'TransactionConfirmationSend' })
.vm.$emit('send-transaction')
})
it('shows the error page', () => { it('shows recipient not found', () => {
expect(wrapper.find('.test-send_transaction_error').text()).toContain( expect(wrapper.find('.test-receiver-not-found').text()).toContain(
'form.send_transaction_error', 'transaction.receiverNotFound',
) )
}) })
it('shows recipient not found', () => {
expect(wrapper.find('.test-receiver-not-found').text()).toContain(
'transaction.receiverNotFound',
)
}) })
}) })
}) })
@ -151,10 +154,11 @@ describe('Send', () => {
}) })
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', { await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
amount: 56.78, amount: 56.78,
memo: 'Make the best of it link!', memo: 'Make the best of the link!',
selected: SEND_TYPES.link, selected: SEND_TYPES.link,
}) })
}) })
it('steps forward in the dialog', () => { it('steps forward in the dialog', () => {
expect(wrapper.findComponent({ name: 'TransactionConfirmationLink' }).exists()).toBe(true) expect(wrapper.findComponent({ name: 'TransactionConfirmationLink' }).exists()).toBe(true)
}) })
@ -173,18 +177,18 @@ describe('Send', () => {
mutation: createTransactionLink, mutation: createTransactionLink,
variables: { variables: {
amount: 56.78, amount: 56.78,
memo: 'Make the best of it link!', memo: 'Make the best of the link!',
}, },
}), }),
) )
}) })
it.skip('emits update-balance', () => { it('emits update-transactions', () => {
expect(wrapper.emitted('update-balance')).toBeTruthy() expect(wrapper.emitted('update-transactions')).toBeTruthy()
expect(wrapper.emitted('update-balance')).toEqual([[56.78]]) expect(wrapper.emitted('update-transactions')).toEqual(expect.arrayContaining([[{}]]))
}) })
it('find components ClipBoard', () => { it('finds the clip board component', () => {
expect(wrapper.findComponent({ name: 'ClipboardCopy' }).exists()).toBe(true) expect(wrapper.findComponent({ name: 'ClipboardCopy' }).exists()).toBe(true)
}) })
@ -196,7 +200,7 @@ describe('Send', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.close') expect(wrapper.find('div.card-body').text()).toContain('form.close')
}) })
describe('Copy link to Clipboard', () => { describe('copy link to clipboard', () => {
const navigatorClipboard = navigator.clipboard const navigatorClipboard = navigator.clipboard
beforeAll(() => { beforeAll(() => {
delete navigator.clipboard delete navigator.clipboard
@ -251,5 +255,39 @@ describe('Send', () => {
}) })
}) })
}) })
describe('no field selected on send transaction', () => {
const errorHandler = localVue.config.errorHandler
beforeAll(() => {
localVue.config.errorHandler = jest.fn()
})
afterAll(() => {
localVue.config.errorHandler = errorHandler
})
beforeEach(async () => {
await wrapper.setData({
currentTransactionStep: TRANSACTION_STEPS.transactionConfirmationSend,
transactionData: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
selected: 'not-valid',
},
})
})
it('throws an error', async () => {
try {
await wrapper
.findComponent({ name: 'TransactionConfirmationSend' })
.vm.$emit('send-transaction')
} catch (error) {
expect(error).toBe('undefined transactionData.selected : not-valid')
}
})
})
}) })
}) })

View File

@ -3,7 +3,11 @@
<b-container> <b-container>
<gdd-send :currentTransactionStep="currentTransactionStep" class="pt-3 ml-2 mr-2"> <gdd-send :currentTransactionStep="currentTransactionStep" class="pt-3 ml-2 mr-2">
<template #transactionForm> <template #transactionForm>
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form> <transaction-form
v-bind="transactionData"
:balance="balance"
@set-transaction="setTransaction"
></transaction-form>
</template> </template>
<template #transactionConfirmationSend> <template #transactionConfirmationSend>
<transaction-confirmation-send <transaction-confirmation-send
@ -95,7 +99,6 @@ export default {
transactions: { transactions: {
default: () => [], default: () => [],
}, },
pending: { pending: {
type: Boolean, type: Boolean,
default: true, default: true,
@ -125,7 +128,8 @@ export default {
}) })
.then(() => { .then(() => {
this.error = false this.error = false
this.$emit('update-balance', this.transactionData.amount) this.updateTransactions({})
this.transactionData = { ...EMPTY_TRANSACTION_DATA }
this.currentTransactionStep = TRANSACTION_STEPS.transactionResultSendSuccess this.currentTransactionStep = TRANSACTION_STEPS.transactionResultSendSuccess
}) })
.catch((err) => { .catch((err) => {
@ -143,6 +147,7 @@ export default {
.then((result) => { .then((result) => {
this.code = result.data.createTransactionLink.code this.code = result.data.createTransactionLink.code
this.currentTransactionStep = TRANSACTION_STEPS.transactionResultLink this.currentTransactionStep = TRANSACTION_STEPS.transactionResultLink
this.updateTransactions({})
}) })
.catch((error) => { .catch((error) => {
this.toastError(error) this.toastError(error)
@ -161,7 +166,7 @@ export default {
}, },
}, },
created() { created() {
this.updateTransactions(0) this.updateTransactions({})
}, },
} }
</script> </script>

View File

@ -9,7 +9,11 @@
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys--> <!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
<p class="h4">{{ $t(displaySetup.subtitle) }}</p> <p class="h4">{{ $t(displaySetup.subtitle) }}</p>
<hr /> <hr />
<b-button v-if="displaySetup.linkTo" :to="displaySetup.linkTo"> <b-button v-if="$route.params.code" :to="`/redeem/${$route.params.code}`">
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
{{ $t(displaySetup.button) }}
</b-button>
<b-button v-else :to="displaySetup.linkTo">
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys--> <!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
{{ $t(displaySetup.button) }} {{ $t(displaySetup.button) }}
</b-button> </b-button>

View File

@ -112,7 +112,7 @@ describe('router', () => {
}) })
describe('thx', () => { describe('thx', () => {
const thx = routes.find((r) => r.path === '/thx/:comingFrom') const thx = routes.find((r) => r.path === '/thx/:comingFrom/:code?')
it('loads the "Thx" page', async () => { it('loads the "Thx" page', async () => {
const component = await thx.component() const component = await thx.component()
@ -177,7 +177,9 @@ describe('router', () => {
describe('checkEmail', () => { describe('checkEmail', () => {
it('loads the "CheckEmail" page', async () => { it('loads the "CheckEmail" page', async () => {
const component = await routes.find((r) => r.path === '/checkEmail/:optin').component() const component = await routes
.find((r) => r.path === '/checkEmail/:optin/:code?')
.component()
expect(component.default.name).toBe('ResetPassword') expect(component.default.name).toBe('ResetPassword')
}) })
}) })

View File

@ -47,7 +47,7 @@ const routes = [
component: () => import('@/pages/Register.vue'), component: () => import('@/pages/Register.vue'),
}, },
{ {
path: '/thx/:comingFrom', path: '/thx/:comingFrom/:code?',
component: () => import('@/pages/thx.vue'), component: () => import('@/pages/thx.vue'),
beforeEnter: (to, from, next) => { beforeEnter: (to, from, next) => {
const validFrom = ['forgot-password', 'reset-password', 'register', 'login', 'checkEmail'] const validFrom = ['forgot-password', 'reset-password', 'register', 'login', 'checkEmail']
@ -79,7 +79,7 @@ const routes = [
component: () => import('@/pages/ResetPassword.vue'), component: () => import('@/pages/ResetPassword.vue'),
}, },
{ {
path: '/checkEmail/:optin', path: '/checkEmail/:optin/:code?',
component: () => import('@/pages/ResetPassword.vue'), component: () => import('@/pages/ResetPassword.vue'),
}, },
{ {

View File

@ -3,11 +3,12 @@ const webpack = require('webpack')
const Dotenv = require('dotenv-webpack') const Dotenv = require('dotenv-webpack')
const StatsPlugin = require('stats-webpack-plugin') const StatsPlugin = require('stats-webpack-plugin')
const HtmlWebpackPlugin = require('vue-html-webpack-plugin') const HtmlWebpackPlugin = require('vue-html-webpack-plugin')
const CONFIG = require('./src/config')
// vue.config.js // vue.config.js
module.exports = { module.exports = {
devServer: { devServer: {
port: process.env.PORT || 3000, port: CONFIG.PORT,
}, },
pluginOptions: { pluginOptions: {
i18n: { i18n: {
@ -35,7 +36,7 @@ module.exports = {
// 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR), // 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR),
// 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE), // 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE),
// 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION), // 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION),
'process.env.BUILD_COMMIT': JSON.stringify(process.env.BUILD_COMMIT), 'process.env.BUILD_COMMIT': JSON.stringify(CONFIG.BUILD_COMMIT),
// 'process.env.PORT': JSON.stringify(process.env.PORT), // 'process.env.PORT': JSON.stringify(process.env.PORT),
}), }),
// generate webpack stats to allow analysis of the bundlesize // generate webpack stats to allow analysis of the bundlesize
@ -44,14 +45,14 @@ module.exports = {
vue: true, vue: true,
template: 'public/index.html', template: 'public/index.html',
meta: { meta: {
title_de: process.env.META_TITLE_DE, title_de: CONFIG.META_TITLE_DE,
title_en: process.env.META_TITLE_EN, title_en: CONFIG.META_TITLE_EN,
description_de: process.env.META_DESCRIPTION_DE, description_de: CONFIG.META_DESCRIPTION_DE,
description_en: process.env.META_DESCRIPTION_EN, description_en: CONFIG.META_DESCRIPTION_EN,
keywords_de: process.env.META_KEYWORDS_DE, keywords_de: CONFIG.META_KEYWORDS_DE,
keywords_en: process.env.META_KEYWORDS_EN, keywords_en: CONFIG.META_KEYWORDS_EN,
author: process.env.META_AUTHOR, author: CONFIG.META_AUTHOR,
url: process.env.META_URL, url: CONFIG.META_URL,
}, },
}), }),
], ],
@ -61,7 +62,7 @@ module.exports = {
}, },
css: { css: {
// Enable CSS source maps. // Enable CSS source maps.
sourceMap: process.env.NODE_ENV !== 'production', sourceMap: CONFIG.NODE_ENV !== 'production',
}, },
outputDir: path.resolve(__dirname, './dist'), outputDir: path.resolve(__dirname, './dist'),
} }