Merge branch 'master' into federation-introduce-private-key-in-community-table

This commit is contained in:
clauspeterhuebner 2023-06-16 00:02:25 +02:00 committed by GitHub
commit d060acaba2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 2672 additions and 797 deletions

View File

@ -1,12 +1,19 @@
import gql from 'graphql-tag' import gql from 'graphql-tag'
export const searchUsers = gql` export const searchUsers = gql`
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $filters: SearchUsersFilters) { query (
$query: String!
$filters: SearchUsersFilters
$currentPage: Int = 0
$pageSize: Int = 25
$order: Order = ASC
) {
searchUsers( searchUsers(
searchText: $searchText query: $query
filters: $filters
currentPage: $currentPage currentPage: $currentPage
pageSize: $pageSize pageSize: $pageSize
filters: $filters order: $order
) { ) {
userCount userCount
userList { userList {

View File

@ -10,11 +10,20 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
userCount: 4, userCount: 4,
userList: [ userList: [
{ {
userId: 1, userId: 4,
firstName: 'Bibi', firstName: 'New',
lastName: 'Bloxberg', lastName: 'User',
email: 'bibi@bloxberg.de', email: 'new@user.ch',
creation: [200, 400, 600], creation: [1000, 1000, 1000],
emailChecked: false,
deletedAt: null,
},
{
userId: 3,
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
creation: [0, 0, 0],
emailChecked: true, emailChecked: true,
deletedAt: null, deletedAt: null,
}, },
@ -28,23 +37,14 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
deletedAt: new Date(), deletedAt: new Date(),
}, },
{ {
userId: 3, userId: 1,
firstName: 'Peter', firstName: 'Bibi',
lastName: 'Lustig', lastName: 'Bloxberg',
email: 'peter@lustig.de', email: 'bibi@bloxberg.de',
creation: [0, 0, 0], creation: [200, 400, 600],
emailChecked: true, emailChecked: true,
deletedAt: null, deletedAt: null,
}, },
{
userId: 4,
firstName: 'New',
lastName: 'User',
email: 'new@user.ch',
creation: [1000, 1000, 1000],
emailChecked: false,
deletedAt: null,
},
], ],
}, },
}, },
@ -79,9 +79,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
searchText: '', query: '',
currentPage: 1, currentPage: 1,
pageSize: 25, pageSize: 25,
order: 'DESC',
filters: { filters: {
byActivated: null, byActivated: null,
byDeleted: null, byDeleted: null,
@ -100,9 +101,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
searchText: '', query: '',
currentPage: 1, currentPage: 1,
pageSize: 25, pageSize: 25,
order: 'DESC',
filters: { filters: {
byActivated: false, byActivated: false,
byDeleted: null, byDeleted: null,
@ -122,9 +124,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
searchText: '', query: '',
currentPage: 1, currentPage: 1,
pageSize: 25, pageSize: 25,
order: 'DESC',
filters: { filters: {
byActivated: null, byActivated: null,
byDeleted: true, byDeleted: true,
@ -144,9 +147,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
searchText: '', query: '',
currentPage: 2, currentPage: 2,
pageSize: 25, pageSize: 25,
order: 'DESC',
filters: { filters: {
byActivated: null, byActivated: null,
byDeleted: null, byDeleted: null,
@ -166,9 +170,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
searchText: 'search string', query: 'search string',
currentPage: 1, currentPage: 1,
pageSize: 25, pageSize: 25,
order: 'DESC',
filters: { filters: {
byActivated: null, byActivated: null,
byDeleted: null, byDeleted: null,
@ -185,9 +190,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
searchText: '', query: '',
currentPage: 1, currentPage: 1,
pageSize: 25, pageSize: 25,
order: 'DESC',
filters: { filters: {
byActivated: null, byActivated: null,
byDeleted: null, byDeleted: null,

View File

@ -49,7 +49,7 @@
pills pills
size="lg" size="lg"
v-model="currentPage" v-model="currentPage"
per-page="perPage" :per-page="perPage"
:total-rows="rows" :total-rows="rows"
align="center" align="center"
:hide-ellipsis="true" :hide-ellipsis="true"
@ -97,10 +97,11 @@ export default {
.query({ .query({
query: searchUsers, query: searchUsers,
variables: { variables: {
searchText: this.criteria, query: this.criteria,
filters: this.filters,
currentPage: this.currentPage, currentPage: this.currentPage,
pageSize: this.perPage, pageSize: this.perPage,
filters: this.filters, order: 'DESC',
}, },
fetchPolicy: 'no-cache', fetchPolicy: 'no-cache',
}) })

View File

@ -25,10 +25,12 @@ module.exports = {
}, },
node: true, node: true,
}, },
// the parser cannot handle the split sodium import
'import/ignore': ['sodium-native'],
}, },
rules: { rules: {
'no-console': 'error', 'no-console': 'error',
camelcase: ['error', { allow: ['FederationClient_*'] }], camelcase: ['error', { allow: ['FederationClient_*', 'crypto_*', 'randombytes_random'] }],
'no-debugger': 'error', 'no-debugger': 'error',
'prettier/prettier': [ 'prettier/prettier': [
'error', 'error',
@ -58,7 +60,10 @@ module.exports = {
'import/no-dynamic-require': 'error', 'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off', 'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error', 'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': ['error', { ignore: ['@/*', 'random-bigint'] }], 'import/no-relative-parent-imports': [
'error',
{ ignore: ['@/*', 'random-bigint', 'sodium-native'] },
],
'import/no-self-import': 'error', 'import/no-self-import': 'error',
'import/no-unresolved': 'error', 'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error', 'import/no-useless-path-segments': 'error',

View File

@ -0,0 +1,8 @@
// eslint-disable-next-line import/no-unresolved
export * from '@/node_modules/@types/sodium-native'
declare module 'sodium-native' {
export function crypto_hash_sha512_init(state: Buffer, key?: Buffer, outlen?: Buffer): void
export function crypto_hash_sha512_update(state: Buffer, input: Buffer): void
export function crypto_hash_sha512_final(state: Buffer, out: Buffer): void
}

View File

@ -55,6 +55,7 @@
"@types/lodash.clonedeep": "^4.5.6", "@types/lodash.clonedeep": "^4.5.6",
"@types/node": "^16.10.3", "@types/node": "^16.10.3",
"@types/nodemailer": "^6.4.4", "@types/nodemailer": "^6.4.4",
"@types/sodium-native": "^2.3.5",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1", "@typescript-eslint/parser": "^5.57.1",

File diff suppressed because it is too large Load Diff

View File

@ -94,11 +94,11 @@ describe('sendEmailTranslated', () => {
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'receiver@mail.org', to: 'receiver@mail.org',
cc: 'support@gradido.net', cc: 'support@gradido.net',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Try To Register Again With Your Email', subject: 'Try To Register Again With Your Email',
html: expect.stringContaining('Gradido: Try To Register Again With Your Email'), html: expect.stringContaining('Try To Register Again With Your Email'),
text: expect.stringContaining('GRADIDO: TRY TO REGISTER AGAIN WITH YOUR EMAIL'), text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
}), }),
}) })
}) })
@ -142,11 +142,11 @@ describe('sendEmailTranslated', () => {
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: CONFIG.EMAIL_TEST_RECEIVER, to: CONFIG.EMAIL_TEST_RECEIVER,
cc: 'support@gradido.net', cc: 'support@gradido.net',
from: `Gradido (do not answer) <${CONFIG.EMAIL_SENDER}>`, from: `Gradido (emails.general.doNotAnswer) <${CONFIG.EMAIL_SENDER}>`,
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Try To Register Again With Your Email', subject: 'Try To Register Again With Your Email',
html: expect.stringContaining('Gradido: Try To Register Again With Your Email'), html: expect.stringContaining('Try To Register Again With Your Email'),
text: expect.stringContaining('GRADIDO: TRY TO REGISTER AGAIN WITH YOUR EMAIL'), text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
}), }),
}) })
}) })

View File

@ -70,7 +70,36 @@ export const sendEmailTranslated = async ({
const resultSend = await email const resultSend = await email
.send({ .send({
template: path.join(__dirname, 'templates', template), template: path.join(__dirname, 'templates', template),
message: receiver, message: {
...receiver,
attachments: [
{
filename: 'gradido-header.jpeg',
path: path.join(__dirname, 'templates/includes/gradido-header.jpeg'),
cid: 'gradidoheader',
},
{
filename: 'facebook-icon.png',
path: path.join(__dirname, 'templates/includes/facebook-icon.png'),
cid: 'facebookicon',
},
{
filename: 'telegram-icon.png',
path: path.join(__dirname, 'templates/includes/telegram-icon.png'),
cid: 'telegramicon',
},
{
filename: 'twitter-icon.png',
path: path.join(__dirname, 'templates/includes/twitter-icon.png'),
cid: 'twittericon',
},
{
filename: 'youtube-icon.png',
path: path.join(__dirname, 'templates/includes/youtube-icon.png'),
cid: 'youtubeicon',
},
],
},
locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale' locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale'
}) })
.catch((error: unknown) => { .catch((error: unknown) => {

View File

@ -34,11 +34,9 @@ let testEnv: {
beforeAll(async () => { beforeAll(async () => {
testEnv = await testEnvironment(logger, localization) testEnv = await testEnvironment(logger, localization)
con = testEnv.con con = testEnv.con
// await cleanDB()
}) })
afterAll(async () => { afterAll(async () => {
// await cleanDB()
await con.close() await con.close()
}) })
@ -87,8 +85,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -97,37 +97,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Message about your common good contribution', subject: 'Message about your common good contribution',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: MESSAGE ABOUT YOUR COMMON GOOD CONTRIBUTION'), text: expect.stringContaining('MESSAGE ABOUT YOUR COMMON GOOD CONTRIBUTION'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Message about your common good contribution</title>', expect(result.originalMessage.html).toMatchSnapshot()
)
expect(result.originalMessage.html).toContain(
'>Gradido: Message about your common good contribution</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'you have received a message from Bibi Bloxberg regarding your common good contribution “My contribution.”.',
)
expect(result.originalMessage.html).toContain(
'To view and reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!',
)
expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
)
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -163,8 +143,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -173,41 +155,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Email Verification', subject: 'Email Verification',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: EMAIL VERIFICATION'), text: expect.stringContaining('EMAIL VERIFICATION'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain('<title>Gradido: Email Verification</title>') it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toContain('>Gradido: Email Verification</h1>') expect(result.originalMessage.html).toMatchSnapshot()
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'Your email address has just been registered with Gradido.',
)
expect(result.originalMessage.html).toContain(
'Please click on this link to complete the registration and activate your Gradido account:',
)
expect(result.originalMessage.html).toContain(
'<a href="http://localhost/checkEmail/6627633878930542284">http://localhost/checkEmail/6627633878930542284</a>',
)
expect(result.originalMessage.html).toContain(
'or copy the link above into your browser window.',
)
expect(result.originalMessage.html).toContain(
'The link has a validity of 23 hours and 30 minutes. If the validity of the link has already expired, you can have a new link sent to you here:',
)
expect(result.originalMessage.html).toContain(
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
)
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -240,7 +198,8 @@ describe('sendEmailVariants', () => {
}) })
}) })
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -249,45 +208,18 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Try To Register Again With Your Email', subject: 'Try To Register Again With Your Email',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: TRY TO REGISTER AGAIN WITH YOUR EMAIL'), text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Try To Register Again With Your Email</title>', expect(result.originalMessage.html).toMatchSnapshot()
) })
expect(result.originalMessage.html).toContain(
'>Gradido: Try To Register Again With Your Email</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'Your email address has just been used again to register an account with Gradido.',
)
expect(result.originalMessage.html).toContain(
'However, an account already exists for your email address.',
)
expect(result.originalMessage.html).toContain(
'Please click on the following link if you have forgotten your password:',
)
expect(result.originalMessage.html).toContain(
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
)
expect(result.originalMessage.html).toContain(
'or copy the link above into your browser window.',
)
expect(result.originalMessage.html).toContain(
'If you are not the one who tried to register again, please contact our support:<br><a href="mailto:support@supportmail.com">support@supportmail.com</a>',
)
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -327,8 +259,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -337,37 +271,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Your contribution to the common good was confirmed', subject: 'Your contribution to the common good was confirmed',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining( text: expect.stringContaining('YOUR CONTRIBUTION TO THE COMMON GOOD WAS CONFIRMED'),
'GRADIDO: YOUR CONTRIBUTION TO THE COMMON GOOD WAS CONFIRMED',
),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Your contribution to the common good was confirmed</title>', expect(result.originalMessage.html).toMatchSnapshot()
)
expect(result.originalMessage.html).toContain(
'>Gradido: Your contribution to the common good was confirmed</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'Your public good contribution “My contribution.” has just been confirmed by Bibi Bloxberg and credited to your Gradido account.',
)
expect(result.originalMessage.html).toContain('Amount: 23.54 GDD')
expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
)
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -405,7 +319,9 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
describe('result', () => {
it('has expected result', () => { it('has expected result', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
@ -415,37 +331,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Your common good contribution was rejected', subject: 'Your common good contribution was rejected',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: YOUR COMMON GOOD CONTRIBUTION WAS REJECTED'), text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS REJECTED'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Your common good contribution was rejected</title>', expect(result.originalMessage.html).toMatchSnapshot()
)
expect(result.originalMessage.html).toContain(
'>Gradido: Your common good contribution was rejected</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'Your public good contribution “My contribution.” was rejected by Bibi Bloxberg.',
)
expect(result.originalMessage.html).toContain(
'To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!',
)
expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
)
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -483,8 +379,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -493,37 +391,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Your common good contribution was deleted', subject: 'Your common good contribution was deleted',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: YOUR COMMON GOOD CONTRIBUTION WAS DELETED'), text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS DELETED'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Your common good contribution was deleted</title>', expect(result.originalMessage.html).toMatchSnapshot()
)
expect(result.originalMessage.html).toContain(
'>Gradido: Your common good contribution was deleted</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'Your public good contribution “My contribution.” was deleted by Bibi Bloxberg.',
)
expect(result.originalMessage.html).toContain(
'To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!',
)
expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
)
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -559,8 +437,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -569,39 +449,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Reset password', subject: 'Reset password',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: RESET PASSWORD'), text: expect.stringContaining('RESET PASSWORD'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain('<title>Gradido: Reset password</title>') it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toContain('>Gradido: Reset password</h1>') expect(result.originalMessage.html).toMatchSnapshot()
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'You, or someone else, requested a password reset for this account.',
)
expect(result.originalMessage.html).toContain('If it was you, please click on the link:')
expect(result.originalMessage.html).toContain(
'<a href="http://localhost/reset-password/3762660021544901417">http://localhost/reset-password/3762660021544901417</a>',
)
expect(result.originalMessage.html).toContain(
'or copy the link above into your browser window.',
)
expect(result.originalMessage.html).toContain(
'The link has a validity of 23 hours and 30 minutes. If the validity of the link has already expired, you can have a new link sent to you here:',
)
expect(result.originalMessage.html).toContain(
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
)
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -643,8 +501,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -653,36 +513,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Bibi Bloxberg has redeemed your Gradido link', subject: 'Bibi Bloxberg has redeemed your Gradido link',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('BIBI BLOXBERG HAS REDEEMED YOUR GRADIDO LINK'), text: expect.stringContaining('BIBI BLOXBERG HAS REDEEMED YOUR GRADIDO LINK'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Bibi Bloxberg has redeemed your Gradido link</title>', expect(result.originalMessage.html).toMatchSnapshot()
)
expect(result.originalMessage.html).toContain(
'>Gradido: Bibi Bloxberg has redeemed your Gradido link</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'Bibi Bloxberg (bibi@bloxberg.de) has just redeemed your link.',
)
expect(result.originalMessage.html).toContain('Amount: 17.65 GDD')
expect(result.originalMessage.html).toContain('Message: You deserve it! 🙏🏼')
expect(result.originalMessage.html).toContain(
`You can find transaction details in your Gradido account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
)
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })
@ -722,8 +563,10 @@ describe('sendEmailVariants', () => {
}, },
}) })
}) })
})
it('has expected result', () => { describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({ expect(result).toMatchObject({
envelope: { envelope: {
from: 'info@gradido.net', from: 'info@gradido.net',
@ -732,34 +575,17 @@ describe('sendEmailVariants', () => {
message: expect.any(String), message: expect.any(String),
originalMessage: expect.objectContaining({ originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>', to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (do not answer) <info@gradido.net>', from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: [], attachments: expect.any(Array),
subject: 'Gradido: Bibi Bloxberg has sent you 37.40 Gradido', subject: 'Bibi Bloxberg has sent you 37.40 Gradido',
html: expect.any(String), html: expect.any(String),
text: expect.stringContaining('GRADIDO: BIBI BLOXBERG HAS SENT YOU 37.40 GRADIDO'), text: expect.stringContaining('BIBI BLOXBERG HAS SENT YOU 37.40 GRADIDO'),
}), }),
}) })
expect(result.originalMessage.html).toContain('<!DOCTYPE html>') })
expect(result.originalMessage.html).toContain('<html lang="en">')
expect(result.originalMessage.html).toContain( it('has the correct html as snapshot', () => {
'<title>Gradido: Bibi Bloxberg has sent you 37.40 Gradido</title>', expect(result.originalMessage.html).toMatchSnapshot()
)
expect(result.originalMessage.html).toContain(
'>Gradido: Bibi Bloxberg has sent you 37.40 Gradido</h1>',
)
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
expect(result.originalMessage.html).toContain(
'You have just received 37.40 GDD from Bibi Bloxberg (bibi@bloxberg.de).',
)
expect(result.originalMessage.html).toContain(
`You can find transaction details in your Gradido account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
)
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
expect(result.originalMessage.html).toContain('—————')
expect(result.originalMessage.html).toContain(
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
)
}) })
}) })
}) })

View File

@ -1,20 +1,16 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.accountActivation.subject') h2= t('emails.accountActivation.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.accountActivation.subject') include ../includes/salutation.pug
#container.col
include ../hello.pug
p= t('emails.accountActivation.emailRegistered') p= t('emails.accountActivation.emailRegistered')
p .content
= t('emails.accountActivation.pleaseClickLink') h2= t('emails.general.completeRegistration')
br div(class="p_content")= t('emails.accountActivation.pleaseClickLink')
a(href=activationLink) #{activationLink} a.button-3(href=activationLink) #{t('emails.accountActivation.activateAccount')}
br div(class="p_content")= t('emails.general.orCopyLink')
= t('emails.general.orCopyLink')
p a.clink(href=activationLink) #{activationLink}
= t('emails.accountActivation.duration', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes })
br include ../includes/requestNewLink.pug
a(href=resendLink) #{resendLink}
include ../greatingFormularImprint.pug

View File

@ -1,23 +1,22 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.accountMultiRegistration.subject') h2= t('emails.accountMultiRegistration.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.accountMultiRegistration.subject') include ../includes/salutation.pug
#container.col
include ../hello.pug
p p
= t('emails.accountMultiRegistration.emailReused') = t('emails.accountMultiRegistration.emailReused')
br br
= t('emails.accountMultiRegistration.emailExists') = t('emails.accountMultiRegistration.emailExists')
p .content
= t('emails.accountMultiRegistration.onForgottenPasswordClickLink') h2= t('emails.resetPassword.title')
br div(class="p_content")= t('emails.accountMultiRegistration.onForgottenPasswordClickLink')
a(href=resendLink) #{resendLink} a.button-3(href=resendLink) #{t('emails.general.reset')}
br div(class="p_content")= t('emails.general.orCopyLink')
= t('emails.accountMultiRegistration.onForgottenPasswordCopyLink')
p a.clink(href=resendLink) #{resendLink}
= t('emails.accountMultiRegistration.ifYouAreNotTheOne')
br h2(style="color: red")= t('emails.accountMultiRegistration.contactSupport')
a(href='mailto:' + supportEmail)= supportEmail div(class="p_content")= t('emails.accountMultiRegistration.ifYouAreNotTheOne')
include ../greatingFormularImprint.pug
a.clink(href='mailto:' + supportEmail)= supportEmail

View File

@ -1,16 +1,14 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.addedContributionMessage.subject') h2= t('emails.addedContributionMessage.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.addedContributionMessage.subject') include ../includes/salutation.pug
#container.col
include ../hello.pug
p= t('emails.addedContributionMessage.commonGoodContributionMessage', { senderFirstName, senderLastName, contributionMemo }) p= t('emails.addedContributionMessage.commonGoodContributionMessage', { senderFirstName, senderLastName, contributionMemo })
p= t('emails.addedContributionMessage.toSeeAndAnswerMessage') .content
p h2= t('emails.addedContributionMessage.readMessage')
= t('emails.general.linkToYourAccount') div(class="p_content")= t('emails.addedContributionMessage.toSeeAndAnswerMessage')
= " "
a(href=overviewURL) #{overviewURL} a.button-3(href="https://gdd.gradido.net/community/contribution") #{t('emails.general.toAccount')}
p= t('emails.general.pleaseDoNotReply')
include ../greatingFormularImprint.pug include ../includes/doNotReply.pug

View File

@ -1,16 +1,10 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.contributionConfirmed.subject') h2= t('emails.contributionConfirmed.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.contributionConfirmed.subject') include ../includes/salutation.pug
#container.col p= t('emails.contributionConfirmed.commonGoodContributionConfirmed', { contributionMemo, senderFirstName, senderLastName, amountGDD: contributionAmount })
include ../hello.pug .content
p= t('emails.contributionConfirmed.commonGoodContributionConfirmed', { senderFirstName, senderLastName, contributionMemo }) include ../includes/contributionDetailsCTA.pug
p= t('emails.general.amountGDD', { amountGDD: contributionAmount }) include ../includes/doNotReply.pug
p
= t('emails.general.linkToYourAccount')
= " "
a(href=overviewURL) #{overviewURL}
p= t('emails.general.pleaseDoNotReply')
include ../greatingFormularImprint.pug

View File

@ -1,16 +1,10 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.contributionDeleted.subject') h2= t('emails.contributionDeleted.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.contributionDeleted.subject') include ../includes/salutation.pug
#container.col p= t('emails.contributionDeleted.commonGoodContributionDeleted', { contributionMemo, senderFirstName, senderLastName })
include ../hello.pug .content
p= t('emails.contributionDeleted.commonGoodContributionDeleted', { senderFirstName, senderLastName, contributionMemo }) include ../includes/contributionDetailsCTA.pug
p= t('emails.contributionDeleted.toSeeContributionsAndMessages') include ../includes/doNotReply.pug
p
= t('emails.general.linkToYourAccount')
= " "
a(href=overviewURL) #{overviewURL}
p= t('emails.general.pleaseDoNotReply')
include ../greatingFormularImprint.pug

View File

@ -1,16 +1,10 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.contributionDenied.subject') h2= t('emails.contributionDenied.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.contributionDenied.subject') include ../includes/salutation.pug
#container.col p= t('emails.contributionDenied.commonGoodContributionDenied', { contributionMemo, senderFirstName, senderLastName })
include ../hello.pug .content
p= t('emails.contributionDenied.commonGoodContributionDenied', { senderFirstName, senderLastName, contributionMemo }) include ../includes/contributionDetailsCTA.pug
p= t('emails.contributionDenied.toSeeContributionsAndMessages') include ../includes/doNotReply.pug
p
= t('emails.general.linkToYourAccount')
= " "
a(href=overviewURL) #{overviewURL}
p= t('emails.general.pleaseDoNotReply')
include ../greatingFormularImprint.pug

View File

@ -1,16 +0,0 @@
p(style='margin-top: 24px;')
= t('emails.general.sincerelyYours')
br
= t('emails.general.yourGradidoTeam')
p(style='margin-top: 24px;')= '—————'
p(style='margin-top: 24px;')
if t('general.imprintImageURL').length > 0
div(style='position: relative; left: -22px;')
img(src=t('general.imprintImageURL'), width='200', alt=t('general.imprintImageAlt'))
br
each line in t('general.imprint').split(/\n/)
= line
br
a(href='mailto:' + supportEmail)= supportEmail
br
a(href=communityURL)= communityURL

View File

@ -0,0 +1,7 @@
//-
h2= t('emails.general.contributionDetails')
div(class="p_content")= t('emails.contribution.toSeeContributionsAndMessages')
a.button-3(href="https://gdd.gradido.net/community/contributions") #{t('emails.general.toAccount')}
div(class="p_content")= t('emails.general.orCopyLink')
a.clink(href="https://gdd.gradido.net/community/contributions") https://gdd.gradido.net/community/contributions

View File

@ -0,0 +1 @@
div(class="p_content")= t('emails.general.pleaseDoNotReply')

View File

@ -0,0 +1,216 @@
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 100;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 100;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 100;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 200;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 200;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 200;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 800;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 800;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 800;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 900;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 900;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 900;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,73 @@
footer
.w-container(class="footer_01")
.socialmedia
a.slink(
target="_blank"
href="https://www.facebook.com/groups/Gradido/"
)
img.bi-facebook(
alt="facebook"
loading="lazy"
src="cid:facebookicon"
)
a.slink(
target="_blank"
href="https://t.me/GradidoGruppe"
)
img.bi-telegram(
alt="Telegram"
loading="lazy"
src="cid:telegramicon"
)
a.slink(
target="_blank"
href="https://twitter.com/gradido"
)
img.bi-twitter(
alt="Twitter"
loading="lazy"
src="cid:twittericon"
)
a.slink(
target="_blank"
href="https://www.youtube.com/c/GradidoNet"
)
img.bi-youtube(
alt="youtube"
loading="lazy"
src="cid:youtubeicon"
)
.line
.footer
div(class="footer_p1")= t("emails.footer.contactOurSupport")
div(class="footer_p2")= t("emails.footer.supportEmail")
img.image(
alt="Gradido Logo"
src="https://gdd.gradido.net/img/brand/green.png"
)
div
a(
class="terms_of_use"
href="https://gradido.net/de/impressum/"
target="_blank"
)= t("emails.footer.imprint")
br
a(
class="terms_of_use"
href="https://gradido.net/de/datenschutz/"
target="_blank"
)= t("emails.footer.privacyPolicy")
div(class="footer_p1")
| Gradido-Akademie
br
| Institut für Wirtschaftsbionik
br
| Pfarrweg 2
br
| 74653 Künzelsau
br
| Deutschland
br
br
br

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,6 @@
//- This sets the greeting at the end of every e-mail
.text-block
p
= t('emails.general.sincerelyYours')
br
= t('emails.general.yourGradidoTeam')

View File

@ -0,0 +1,13 @@
header
.head
//- TODO
//- when https://gdd.gradido.net/img/gradido-email-header.jpg is on production,
//- replace this URL by https://gdd.gradido.net/img/brand/gradido-email-header.png
img.head-logo(
alt="Gradido Logo"
loading="lazy"
src="cid:gradidoheader"
)

View File

@ -0,0 +1,10 @@
//-
requestNewLink
h2= t('emails.general.requestNewLink')
if timeDurationObject.minutes == 0
div(class="p_content")= t('emails.general.linkValidity', { hours: timeDurationObject.hours })
else
div(class="p_content")= t('emails.general.linkValidityWithMinutes', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes })
a.button-4(href=resendLink) #{t('emails.general.newLink')}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,136 @@
body{
display: block;
font-family: "Work Sans", sans-serif;
font-size: 17px;
text-align: center;
text-align: -webkit-center;
justify-content: center;
padding: 0px;
margin: 0px;
}
h2 {
margin-top: 15px;
color: #383838;
}
.container {
max-width: 680px;
margin: 0 auto;
display: block;
}
.head-logo {
width: 100%;
height: auto;
}
.text-block {
margin-top: 20px;
color: #9ca0a8;
}
.content {
display: block;
width: 78%;
margin: 40px 1% 40px 1%;
padding: 20px 10% 40px 10%;
border-radius: 24px;
background-image: linear-gradient(180deg, #f5f5f5, #f5f5f5);
}
.p_content{
margin: 15px 0 15px 0;
line-height: 26px;
color: #9ca0a8;
}
.clink {
line-break: anywhere;
margin-bottom: 40px;
}
.button-3,
.button-4 {
display: inline-block;
padding: 9px 15px;
color: white;
border: 0;
line-height: inherit;
text-decoration: none;
cursor: pointer;
border-radius: 20px;
background-image: radial-gradient(circle farthest-corner at 0% 0%, #f9cd69, #c58d38);
box-shadow: 16px 13px 35px 0 rgba(56, 56, 56, 0.3);
margin: 25px 0 25px 0;
width: 50%;
}
.button-4 {
background-image: radial-gradient(circle farthest-corner at 0% 0%, #616161, #c2c2c2);
}
.socialmedia {
display: flex;
margin-top: 40px;
max-width: 600px;
}
.slink{
width: 150px;
}
.footer {
padding-bottom: 20px;
}
.footer_p1 {
margin-top: 30px;
color: #9ca0a8;
margin-bottom: 30px;
}
.footer_p2 {
color: #383838;
font-weight: bold;
}
.image {
width: 200px;
margin-top: 30px;
margin-bottom: 30px;
}
.div-block {
display: table;
margin-top: 20px;
margin-bottom: 40px;
flex-direction: row;
justify-content: center;
align-items: center;
}
.terms_of_use {
color: #9ca0a8;
}
.text-block-3 {
color: #9ca0a8;
margin-bottom: 30px;
}
.line_image,
.line {
width: 100%;
height: 13px;
margin-top: 40px;
}
.line_image {
background-image: linear-gradient(90deg, #c58d38, #c58d38 0%, #f3cd7c 35%, #dbb056 54%, #eec05f 63%, #cc9d3d);
}
.line {
background-image: linear-gradient(90deg, #c58d38, #f3cd7c 40%, #dbb056 55%, #eec05f 71%, #cc9d3d);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,26 @@
doctype html
html(lang=locale)
head
meta(
content="multipart/html; charset=UTF-8"
http-equiv="content-type"
)
meta(
name="viewport"
content="width=device-width, initial-scale=1"
)
style.
.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}
style
include includes/email.css
include includes/webflow.css
body
div.container
include includes/header.pug
.wrapper
block content
include includes/greeting.pug
include includes/footer.pug

View File

@ -1,20 +1,16 @@
doctype html extends ../layout.pug
html(lang=locale)
head block content
title= t('emails.resetPassword.subject') h2= t('emails.resetPassword.title')
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.resetPassword.subject') include ../includes/salutation.pug
#container.col
include ../hello.pug
p= t('emails.resetPassword.youOrSomeoneResetPassword') p= t('emails.resetPassword.youOrSomeoneResetPassword')
p .content
= t('emails.resetPassword.pleaseClickLink') h2= t('emails.resetPassword.title')
br div(class="p_content")= t('emails.resetPassword.pleaseClickLink')
a(href=resetLink) #{resetLink} a.button-3(href=resetLink) #{t('emails.general.reset')}
br div(class="p_content")= t('emails.general.orCopyLink')
= t('emails.general.orCopyLink')
p a.clink(href=resetLink) #{resetLink}
= t('emails.resetPassword.duration', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes })
br include ../includes/requestNewLink.pug
a(href=resendLink) #{resendLink}
include ../greatingFormularImprint.pug

View File

@ -1,19 +1,18 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.transactionLinkRedeemed.subject', { senderFirstName, senderLastName }) h2= t('emails.transactionLinkRedeemed.title', { senderFirstName, senderLastName })
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.transactionLinkRedeemed.subject', { senderFirstName, senderLastName }) include ../includes/salutation.pug
#container.col
include ../hello.pug
p= t('emails.transactionLinkRedeemed.hasRedeemedYourLink', { senderFirstName, senderLastName, senderEmail }) p= t('emails.transactionLinkRedeemed.hasRedeemedYourLink', { senderFirstName, senderLastName, senderEmail })
p .content
= t('emails.general.amountGDD', { amountGDD: transactionAmount }) h2= t('emails.general.transactionDetails')
div(class="p_content")= t('emails.general.amountGDD', { amountGDD: transactionAmount })
br br
= t('emails.transactionLinkRedeemed.memo', { transactionMemo }) = t('emails.transactionLinkRedeemed.memo', { transactionMemo })
p br
= t('emails.general.detailsYouFindOnLinkToYourAccount') = t('emails.general.detailsYouFindOnLinkToYourAccount')
= " "
a(href=overviewURL) #{overviewURL} a.button-3(href="https://gdd.gradido.net/transactions") #{t('emails.general.toAccount')}
p= t('emails.general.pleaseDoNotReply')
include ../greatingFormularImprint.pug include ../includes/doNotReply.pug

View File

@ -1,15 +1,15 @@
doctype html extend ../layout.pug
html(lang=locale)
head block content
title= t('emails.transactionReceived.subject', { senderFirstName, senderLastName, transactionAmount }) h2= t('emails.transactionReceived.title', { senderFirstName, senderLastName, transactionAmount })
body .text-block
h1(style='margin-bottom: 24px;')= t('emails.transactionReceived.subject', { senderFirstName, senderLastName, transactionAmount }) include ../includes/salutation.pug
#container.col
include ../hello.pug
p= t('emails.transactionReceived.haveReceivedAmountGDDFrom', { transactionAmount, senderFirstName, senderLastName, senderEmail }) p= t('emails.transactionReceived.haveReceivedAmountGDDFrom', { transactionAmount, senderFirstName, senderLastName, senderEmail })
p .content
= t('emails.general.detailsYouFindOnLinkToYourAccount') h2= t('emails.general.transactionDetails')
= " " div(class="p_content")= t('emails.general.detailsYouFindOnLinkToYourAccount')
a(href=overviewURL) #{overviewURL}
p= t('emails.general.pleaseDoNotReply') a.button-3(href="https://gdd.gradido.net/transactions") #{t('emails.general.toAccount')}
include ../greatingFormularImprint.pug
include ../includes/doNotReply.pug

View File

@ -1,21 +0,0 @@
import { ArgsType, Field, Int } from 'type-graphql'
import { SearchUsersFilters } from '@arg/SearchUsersFilters'
@ArgsType()
export class SearchUsersArgs {
@Field(() => String)
searchText: string
@Field(() => Int, { nullable: true })
// eslint-disable-next-line type-graphql/invalid-nullable-input-type
currentPage?: number
@Field(() => Int, { nullable: true })
// eslint-disable-next-line type-graphql/invalid-nullable-input-type
pageSize?: number
// eslint-disable-next-line type-graphql/wrong-decorator-signature
@Field(() => SearchUsersFilters, { nullable: true, defaultValue: null })
filters?: SearchUsersFilters | null
}

View File

@ -1,12 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field } from 'type-graphql'
@ObjectType() @ObjectType()
export class KlickTipp { export class KlickTipp {
constructor(json: any) { constructor(newsletterState: boolean) {
this.newsletterState = json.status === 'Subscribed' this.newsletterState = newsletterState
} }
@Field(() => Boolean) @Field(() => Boolean)

View File

@ -2110,7 +2110,7 @@ describe('UserResolver', () => {
describe('search users', () => { describe('search users', () => {
const variablesWithoutTextAndFilters = { const variablesWithoutTextAndFilters = {
searchText: '', query: '',
currentPage: 1, currentPage: 1,
pageSize: 25, pageSize: 25,
filters: null, filters: null,

View File

@ -8,22 +8,12 @@ import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User' import { User as DbUser } from '@entity/User'
import { UserContact as DbUserContact } from '@entity/UserContact' import { UserContact as DbUserContact } from '@entity/UserContact'
import i18n from 'i18n' import i18n from 'i18n'
import { import { Resolver, Query, Args, Arg, Authorized, Ctx, Mutation, Int } from 'type-graphql'
Resolver,
Query,
Args,
Arg,
Authorized,
Ctx,
UseMiddleware,
Mutation,
Int,
} from 'type-graphql'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { CreateUserArgs } from '@arg/CreateUserArgs' import { CreateUserArgs } from '@arg/CreateUserArgs'
import { Paginated } from '@arg/Paginated' import { Paginated } from '@arg/Paginated'
import { SearchUsersArgs } from '@arg/SearchUsersArgs' import { SearchUsersFilters } from '@arg/SearchUsersFilters'
import { UnsecureLoginArgs } from '@arg/UnsecureLoginArgs' import { UnsecureLoginArgs } from '@arg/UnsecureLoginArgs'
import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs' import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs'
import { OptInType } from '@enum/OptInType' import { OptInType } from '@enum/OptInType'
@ -60,7 +50,6 @@ import {
EVENT_ADMIN_USER_DELETE, EVENT_ADMIN_USER_DELETE,
EVENT_ADMIN_USER_UNDELETE, EVENT_ADMIN_USER_UNDELETE,
} from '@/event/Events' } from '@/event/Events'
import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddleware'
import { isValidPassword } from '@/password/EncryptorUtils' import { isValidPassword } from '@/password/EncryptorUtils'
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor' import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
import { Context, getUser, getClientTimezoneOffset } from '@/server/context' import { Context, getUser, getClientTimezoneOffset } from '@/server/context'
@ -71,15 +60,14 @@ import { hasElopageBuys } from '@/util/hasElopageBuys'
import { getTimeDurationObject, printTimeDuration } from '@/util/time' import { getTimeDurationObject, printTimeDuration } from '@/util/time'
import random from 'random-bigint' import random from 'random-bigint'
import { randombytes_random } from 'sodium-native'
import { FULL_CREATION_AVAILABLE } from './const/const' import { FULL_CREATION_AVAILABLE } from './const/const'
import { getUserCreations } from './util/creations' import { getUserCreations } from './util/creations'
import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUserByIdentifier } from './util/findUserByIdentifier'
import { getKlicktippState } from './util/getKlicktippState'
import { validateAlias } from './util/validateAlias' import { validateAlias } from './util/validateAlias'
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-commonjs
const sodium = require('sodium-native')
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
const DEFAULT_LANGUAGE = 'de' const DEFAULT_LANGUAGE = 'de'
const isLanguage = (language: string): boolean => { const isLanguage = (language: string): boolean => {
@ -122,7 +110,6 @@ const newGradidoID = async (): Promise<string> => {
export class UserResolver { export class UserResolver {
@Authorized([RIGHTS.VERIFY_LOGIN]) @Authorized([RIGHTS.VERIFY_LOGIN])
@Query(() => User) @Query(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async verifyLogin(@Ctx() context: Context): Promise<User> { async verifyLogin(@Ctx() context: Context): Promise<User> {
logger.info('verifyLogin...') logger.info('verifyLogin...')
// TODO refactor and do not have duplicate code with login(see below) // TODO refactor and do not have duplicate code with login(see below)
@ -132,12 +119,12 @@ export class UserResolver {
user.hasElopage = await this.hasElopage(context) user.hasElopage = await this.hasElopage(context)
logger.debug(`verifyLogin... successful: ${user.firstName}.${user.lastName}`) logger.debug(`verifyLogin... successful: ${user.firstName}.${user.lastName}`)
user.klickTipp = await getKlicktippState(userEntity.emailContact.email)
return user return user
} }
@Authorized([RIGHTS.LOGIN]) @Authorized([RIGHTS.LOGIN])
@Mutation(() => User) @Mutation(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async login( async login(
@Args() { email, password, publisherId }: UnsecureLoginArgs, @Args() { email, password, publisherId }: UnsecureLoginArgs,
@Ctx() context: Context, @Ctx() context: Context,
@ -183,6 +170,7 @@ export class UserResolver {
dbUser.publisherId = publisherId dbUser.publisherId = publisherId
await DbUser.save(dbUser) await DbUser.save(dbUser)
} }
user.klickTipp = await getKlicktippState(dbUser.emailContact.email)
context.setHeaders.push({ context.setHeaders.push({
key: 'token', key: 'token',
@ -237,7 +225,7 @@ export class UserResolver {
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent. // TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
const user = new User(communityDbUser) const user = new User(communityDbUser)
user.id = sodium.randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in? user.id = randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in?
user.gradidoID = uuidv4() user.gradidoID = uuidv4()
user.firstName = firstName user.firstName = firstName
user.lastName = lastName user.lastName = lastName
@ -640,8 +628,11 @@ export class UserResolver {
@Authorized([RIGHTS.SEARCH_USERS]) @Authorized([RIGHTS.SEARCH_USERS])
@Query(() => SearchUsersResult) @Query(() => SearchUsersResult)
async searchUsers( async searchUsers(
@Arg('query', () => String) query: string,
@Arg('filters', () => SearchUsersFilters, { nullable: true })
filters: SearchUsersFilters | null | undefined,
@Args() @Args()
{ searchText, currentPage = 1, pageSize = 25, filters }: SearchUsersArgs, { currentPage = 1, pageSize = 25, order = Order.ASC }: Paginated,
@Ctx() context: Context, @Ctx() context: Context,
): Promise<SearchUsersResult> { ): Promise<SearchUsersResult> {
const clientTimezoneOffset = getClientTimezoneOffset(context) const clientTimezoneOffset = getClientTimezoneOffset(context)
@ -659,15 +650,16 @@ export class UserResolver {
userFields.map((fieldName) => { userFields.map((fieldName) => {
return 'user.' + fieldName return 'user.' + fieldName
}), }),
searchText, query,
filters ?? null, filters ?? null,
currentPage, currentPage,
pageSize, pageSize,
order,
) )
if (users.length === 0) { if (users.length === 0) {
return { return {
userCount: 0, userCount: count,
userList: [], userList: [],
} }
} }

View File

@ -0,0 +1,19 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { KlickTipp } from '@model/KlickTipp'
import { getKlickTippUser } from '@/apis/KlicktippController'
import { klickTippLogger as logger } from '@/server/logger'
export const getKlicktippState = async (email: string): Promise<KlickTipp> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const klickTippUser = await getKlickTippUser(email)
if (klickTippUser) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return new KlickTipp(klickTippUser.status === 'Subscribed')
}
} catch (err) {
logger.error('There is no klicktipp user for email', email, err)
}
return new KlickTipp(false)
}

View File

@ -1,69 +1,92 @@
{ {
"emails": { "emails": {
"accountActivation": { "accountActivation": {
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:", "activateAccount": "Konto aktivieren",
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.", "emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
"pleaseClickLink": "Klicke bitte auf diesen Link, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren:", "pleaseClickLink": "Klicke bitte hier, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren.",
"subject": "Gradido: E-Mail Überprüfung" "subject": "E-Mail Überprüfung",
"title": "E-Mail Überprüfung"
}, },
"accountMultiRegistration": { "accountMultiRegistration": {
"contactSupport": "Support kontaktieren",
"emailExists": "Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.", "emailExists": "Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.",
"emailReused": "deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.", "emailReused": "deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.",
"ifYouAreNotTheOne": "Wenn du nicht derjenige bist, der versucht hat sich erneut zu registrieren, wende dich bitte an unseren Support:", "ifYouAreNotTheOne": "Wenn du nicht versucht hast dich erneut zu registrieren, wende dich bitte an unseren Support:",
"onForgottenPasswordClickLink": "Klicke bitte auf den folgenden Link, falls du dein Passwort vergessen haben solltest:", "onForgottenPasswordClickLink": "Solltest du dein Passwort vergessen haben, klicke bitte hier.",
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.", "subject": "Erneuter Registrierungsversuch mit deiner E-Mail",
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail" "title": "Erneuter Registrierungsversuch mit deiner E-Mail"
}, },
"addedContributionMessage": { "addedContributionMessage": {
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.", "commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag", "readMessage": "Nachricht lesen und beantworten",
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“!" "subject": "Nachricht zu deinem Gemeinwohl-Beitrag",
"title": "Nachricht zu deinem Gemeinwohl-Beitrag",
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“."
},
"contribution": {
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“."
}, },
"contributionConfirmed": { "contributionConfirmed": {
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.", "commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt. Es wurden deinem Gradido-Konto {amountGDD} GDD gutgeschrieben.",
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt" "subject": "Dein Gemeinwohl-Beitrag wurde bestätigt",
"title": "Dein Gemeinwohl-Beitrag wurde bestätigt"
}, },
"contributionDeleted": { "contributionDeleted": {
"commonGoodContributionDeleted": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} gelöscht.", "commonGoodContributionDeleted": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} gelöscht.",
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde gelöscht", "subject": "Dein Gemeinwohl-Beitrag wurde gelöscht",
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“!" "title": "Dein Gemeinwohl-Beitrag wurde gelöscht"
}, },
"contributionDenied": { "contributionDenied": {
"commonGoodContributionDenied": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.", "commonGoodContributionDenied": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.",
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde abgelehnt", "subject": "Dein Gemeinwohl-Beitrag wurde abgelehnt",
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“!" "title": "Dein Gemeinwohl-Beitrag wurde abgelehnt"
},
"footer": {
"contactOurSupport": "Bei weiteren Fragen kontaktiere bitte unseren Support.",
"imprint": "Impressum",
"imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland",
"imprintImageAlt": "Gradido-Akademie Logo",
"privacyPolicy": "Datenschutzerklärung",
"supportEmail": "support@gradido.net"
}, },
"general": { "general": {
"amountGDD": "Betrag: {amountGDD} GDD", "amountGDD": "Betrag: {amountGDD} GDD",
"detailsYouFindOnLinkToYourAccount": "Details zur Transaktion findest du in deinem Gradido-Konto:", "completeRegistration": "Registrierung abschließen",
"doNotAnswer": "nicht antworten", "contribution": "Gemeinwohl-Beitrag: {contributionMemo}",
"contributionDetails": "Beitragsdetails",
"detailsYouFindOnLinkToYourAccount": "Details zur Transaktion findest du in deinem Gradido-Konto.",
"helloName": "Hallo {firstName} {lastName},", "helloName": "Hallo {firstName} {lastName},",
"linkToYourAccount": "Link zu deinem Konto:", "linkValidity": "Der Link hat eine Gültigkeit von {hours} Stunden.\nSollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen.",
"orCopyLink": "oder kopiere den obigen Link in dein Browserfenster.", "linkValidityWithMinutes": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten.\nSollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen.",
"pleaseDoNotReply": "Bitte antworte nicht auf diese E-Mail!", "newLink": "Neuer Link",
"orCopyLink": "Oder kopiere den Link in dein Browserfenster.",
"pleaseDoNotReply": "Bitte antworte nicht auf diese E-Mail.",
"requestNewLink": "Neuen gültigen Link anfordern",
"reset": "zurücksetzen",
"sincerelyYours": "Liebe Grüße", "sincerelyYours": "Liebe Grüße",
"toAccount": "Zum Konto",
"transactionDetails": "Transaktionsdetails",
"yourGradidoTeam": "dein Gradido-Team" "yourGradidoTeam": "dein Gradido-Team"
}, },
"resetPassword": { "resetPassword": {
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:", "pleaseClickLink": "Wenn du es warst, klicke bitte hier.",
"pleaseClickLink": "Wenn du es warst, klicke bitte auf den Link:", "subject": "Passwort zurücksetzen",
"subject": "Gradido: Passwort zurücksetzen", "title": "Passwort zurücksetzen",
"youOrSomeoneResetPassword": "du, oder jemand anderes, hast für dieses Konto ein Zurücksetzen des Passworts angefordert." "youOrSomeoneResetPassword": "du oder jemand anderes, hast für dieses Konto ein Zurücksetzen des Passworts angefordert."
}, },
"transactionLinkRedeemed": { "transactionLinkRedeemed": {
"hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) hat soeben deinen Link eingelöst.", "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) hat soeben deinen Link eingelöst.",
"memo": "Nachricht: {transactionMemo}", "memo": "Nachricht: {transactionMemo}",
"subject": "Gradido: {senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst" "subject": "{senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst",
"title": "{senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst"
}, },
"transactionReceived": { "transactionReceived": {
"haveReceivedAmountGDDFrom": "du hast soeben {transactionAmount} GDD von {senderFirstName} {senderLastName} ({senderEmail}) erhalten.", "haveReceivedAmountGDDFrom": "du hast soeben {transactionAmount} GDD von {senderFirstName} {senderLastName} ({senderEmail}) erhalten.",
"subject": "Gradido: {senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet" "subject": "{senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet",
"title": "{senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet"
} }
}, },
"general": { "general": {
"decimalSeparator": ",", "decimalSeparator": ","
"imprint": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland",
"imprintImageAlt": "Gradido-Akademie Logo",
"imprintImageURL": "https://gdd.gradido.net/img/brand/green.png"
} }
} }

View File

@ -1,69 +1,92 @@
{ {
"emails": { "emails": {
"accountActivation": { "accountActivation": {
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:", "activateAccount": "Activate account",
"emailRegistered": "Your email address has just been registered with Gradido.", "emailRegistered": "Your email address has just been registered with Gradido.",
"pleaseClickLink": "Please click on this link to complete the registration and activate your Gradido account:", "pleaseClickLink": "Please click here to complete the registration and activate your Gradido account.",
"subject": "Gradido: Email Verification" "subject": "Email Verification",
"title": "Email Verification"
}, },
"accountMultiRegistration": { "accountMultiRegistration": {
"contactSupport": "Contact support",
"emailExists": "However, an account already exists for your email address.", "emailExists": "However, an account already exists for your email address.",
"emailReused": "Your email address has just been used again to register an account with Gradido.", "emailReused": "Your email address has just been used again to register an account with Gradido.",
"ifYouAreNotTheOne": "If you are not the one who tried to register again, please contact our support:", "ifYouAreNotTheOne": "If you did not try to register again, please contact our support:",
"onForgottenPasswordClickLink": "Please click on the following link if you have forgotten your password:", "onForgottenPasswordClickLink": "If you have forgotten your password, please click here.",
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.", "subject": "Try To Register Again With Your Email",
"subject": "Gradido: Try To Register Again With Your Email" "title": "Try To Register Again With Your Email"
}, },
"addedContributionMessage": { "addedContributionMessage": {
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", "commonGoodContributionMessage": "You have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
"subject": "Gradido: Message about your common good contribution", "readMessage": "Read and reply to message",
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!" "subject": "Message about your common good contribution",
"title": "Message about your common good contribution",
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab."
},
"contribution": {
"toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab."
}, },
"contributionConfirmed": { "contributionConfirmed": {
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.", "commonGoodContributionConfirmed": "Your common good contribution “{contributionMemo}” has just been approved by {senderFirstName} {senderLastName}. Your Gradido account has been credited with {amountGDD} GDD.",
"subject": "Gradido: Your contribution to the common good was confirmed" "subject": "Your contribution to the common good was confirmed",
"title": "Your contribution to the common good was confirmed"
}, },
"contributionDeleted": { "contributionDeleted": {
"commonGoodContributionDeleted": "Your public good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.", "commonGoodContributionDeleted": "Your common good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.",
"subject": "Gradido: Your common good contribution was deleted", "subject": "Your common good contribution was deleted",
"toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!" "title": "Your common good contribution was deleted"
}, },
"contributionDenied": { "contributionDenied": {
"commonGoodContributionDenied": "Your public good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.", "commonGoodContributionDenied": "Your common good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.",
"subject": "Gradido: Your common good contribution was rejected", "subject": "Your common good contribution was rejected",
"toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!" "title": "Your common good contribution was rejected"
},
"footer": {
"contactOurSupport": "If you have any further questions, please contact our support.",
"imprint": "Impressum",
"imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland",
"imprintImageAlt": "Gradido-Akademie Logo",
"privacyPolicy": "Privacy Policy",
"supportEmail": "support@gradido.net"
}, },
"general": { "general": {
"amountGDD": "Amount: {amountGDD} GDD", "amountGDD": "Amount: {amountGDD} GDD",
"detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account:", "completeRegistration": "Complete registration",
"doNotAnswer": "do not answer", "contribution": "Contribution: : {contributionMemo}",
"helloName": "Hello {firstName} {lastName}", "contributionDetails": "Contribution details",
"linkToYourAccount": "Link to your account:", "detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account.",
"orCopyLink": "or copy the link above into your browser window.", "helloName": "Hello {firstName} {lastName},",
"pleaseDoNotReply": "Please do not reply to this email!", "linkValidity": "The link has a validity of {hours} hours.\nIf the validity of the link has already expired, you can have a new link sent to you here.",
"linkValidityWithMinutes": "The link has a validity of {hours} hours and {minutes} minutes.\nIf the validity of the link has already expired, you can have a new link sent to you here.",
"newLink": "New link",
"orCopyLink": "Or copy the link into your browser window.",
"pleaseDoNotReply": "Please do not reply to this email.",
"requestNewLink": "Request new valid link",
"reset": "reset",
"sincerelyYours": "Kind regards,", "sincerelyYours": "Kind regards,",
"toAccount": "To account",
"transactionDetails": "Transaction details",
"yourGradidoTeam": "your Gradido team" "yourGradidoTeam": "your Gradido team"
}, },
"resetPassword": { "resetPassword": {
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:", "pleaseClickLink": "If it was you, please click here.",
"pleaseClickLink": "If it was you, please click on the link:", "subject": "Reset password",
"subject": "Gradido: Reset password", "title": "Reset password",
"youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account." "youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account."
}, },
"transactionLinkRedeemed": { "transactionLinkRedeemed": {
"hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.", "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.",
"memo": "Message: {transactionMemo}", "memo": "Message: {transactionMemo}",
"subject": "Gradido: {senderFirstName} {senderLastName} has redeemed your Gradido link" "subject": "{senderFirstName} {senderLastName} has redeemed your Gradido link",
"title": "{senderFirstName} {senderLastName} has redeemed your Gradido link"
}, },
"transactionReceived": { "transactionReceived": {
"haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName} ({senderEmail}).", "haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName} ({senderEmail}).",
"subject": "Gradido: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" "subject": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido",
"title": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido"
} }
}, },
"general": { "general": {
"decimalSeparator": ".", "decimalSeparator": "."
"imprint": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland",
"imprintImageAlt": "Gradido-Akademie Logo",
"imprintImageURL": "https://gdd.gradido.net/img/brand/green.png"
} }
} }

View File

@ -1,31 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { MiddlewareFn } from 'type-graphql'
import { KlickTipp } from '@model/KlickTipp'
import { getKlickTippUser } from '@/apis/KlicktippController'
import { klickTippLogger as logger } from '@/server/logger'
export const klicktippNewsletterStateMiddleware: MiddlewareFn = async (
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
{ root, args, context, info },
next,
) => {
// eslint-disable-next-line n/callback-return
const result = await next()
let klickTipp = new KlickTipp({ status: 'Unsubscribed' })
try {
const klickTippUser = await getKlickTippUser(result.email)
if (klickTippUser) {
klickTipp = new KlickTipp(klickTippUser)
}
} catch (err) {
logger.error(`There is no user for (email='${result.email}') ${err}`)
}
result.klickTipp = klickTipp
return result
}

View File

@ -10,8 +10,19 @@ import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError' import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-commonjs import {
const sodium = require('sodium-native') crypto_shorthash_KEYBYTES,
crypto_box_SEEDBYTES,
crypto_hash_sha512_init,
crypto_hash_sha512_update,
crypto_hash_sha512_final,
crypto_hash_sha512_BYTES,
crypto_hash_sha512_STATEBYTES,
crypto_shorthash_BYTES,
crypto_pwhash_SALTBYTES,
crypto_pwhash,
crypto_shorthash,
} from 'sodium-native'
// We will reuse this for changePassword // We will reuse this for changePassword
export const isValidPassword = (password: string): boolean => { export const isValidPassword = (password: string): boolean => {
@ -22,36 +33,36 @@ export const SecretKeyCryptographyCreateKey = (salt: string, password: string):
logger.trace('SecretKeyCryptographyCreateKey...') logger.trace('SecretKeyCryptographyCreateKey...')
const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex')
const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex')
if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) { if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) {
throw new LogError( throw new LogError(
'ServerKey has an invalid size', 'ServerKey has an invalid size',
configLoginServerKey.length, configLoginServerKey.length,
sodium.crypto_shorthash_KEYBYTES, crypto_shorthash_KEYBYTES,
) )
} }
const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES) const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES)
sodium.crypto_hash_sha512_init(state) crypto_hash_sha512_init(state)
sodium.crypto_hash_sha512_update(state, Buffer.from(salt)) crypto_hash_sha512_update(state, Buffer.from(salt))
sodium.crypto_hash_sha512_update(state, configLoginAppSecret) crypto_hash_sha512_update(state, configLoginAppSecret)
const hash = Buffer.alloc(sodium.crypto_hash_sha512_BYTES) const hash = Buffer.alloc(crypto_hash_sha512_BYTES)
sodium.crypto_hash_sha512_final(state, hash) crypto_hash_sha512_final(state, hash)
const encryptionKey = Buffer.alloc(sodium.crypto_box_SEEDBYTES) const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES)
const opsLimit = 10 const opsLimit = 10
const memLimit = 33554432 const memLimit = 33554432
const algo = 2 const algo = 2
sodium.crypto_pwhash( crypto_pwhash(
encryptionKey, encryptionKey,
Buffer.from(password), Buffer.from(password),
hash.slice(0, sodium.crypto_pwhash_SALTBYTES), hash.slice(0, crypto_pwhash_SALTBYTES),
opsLimit, opsLimit,
memLimit, memLimit,
algo, algo,
) )
const encryptionKeyHash = Buffer.alloc(sodium.crypto_shorthash_BYTES) const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES)
sodium.crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey)
return [encryptionKeyHash, encryptionKey] return [encryptionKeyHash, encryptionKey]
} }

View File

@ -69,12 +69,19 @@ export const sendResetPasswordEmail = gql`
` `
export const searchUsers = gql` export const searchUsers = gql`
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $filters: SearchUsersFilters) { query (
$query: String!
$filters: SearchUsersFilters
$currentPage: Int = 1
$pageSize: Int = 25
$order: Order = ASC
) {
searchUsers( searchUsers(
searchText: $searchText query: $query
filters: $filters
currentPage: $currentPage currentPage: $currentPage
pageSize: $pageSize pageSize: $pageSize
filters: $filters order: $order
) { ) {
userCount userCount
userList { userList {

View File

@ -1,7 +1,8 @@
import { Brackets, EntityRepository, IsNull, Not, Repository } from '@dbTools/typeorm' import { Brackets, EntityRepository, IsNull, Not, Repository } from '@dbTools/typeorm'
import { User as DbUser } from '@entity/User' import { User as DbUser } from '@entity/User'
import { SearchUsersFilters } from '@/graphql/arg/SearchUsersFilters' import { SearchUsersFilters } from '@arg/SearchUsersFilters'
import { Order } from '@enum/Order'
@EntityRepository(DbUser) @EntityRepository(DbUser)
export class UserRepository extends Repository<DbUser> { export class UserRepository extends Repository<DbUser> {
@ -11,6 +12,7 @@ export class UserRepository extends Repository<DbUser> {
filters: SearchUsersFilters | null, filters: SearchUsersFilters | null,
currentPage: number, currentPage: number,
pageSize: number, pageSize: number,
order = Order.ASC,
): Promise<[DbUser[], number]> { ): Promise<[DbUser[], number]> {
const query = this.createQueryBuilder('user') const query = this.createQueryBuilder('user')
.select(select) .select(select)
@ -46,6 +48,7 @@ export class UserRepository extends Repository<DbUser> {
} }
return query return query
.orderBy({ 'user.id': order })
.take(pageSize) .take(pageSize)
.skip((currentPage - 1) * pageSize) .skip((currentPage - 1) * pageSize)
.getManyAndCount() .getManyAndCount()

View File

@ -1181,6 +1181,13 @@
"@types/mime" "^1" "@types/mime" "^1"
"@types/node" "*" "@types/node" "*"
"@types/sodium-native@^2.3.5":
version "2.3.5"
resolved "https://registry.yarnpkg.com/@types/sodium-native/-/sodium-native-2.3.5.tgz#5d2681e7b6b67bcbdc63cfb133e303ec9e942e43"
integrity sha512-a3DAIpW8+36XAY8aIR36JBQQsfOabxHuJwx11DL/PTvnbwEWPAXW66b8QbMi0r2vUnkOfREsketxdvjBmQxqDQ==
dependencies:
"@types/node" "*"
"@types/stack-utils@^2.0.0": "@types/stack-utils@^2.0.0":
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
@ -3645,7 +3652,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
"gradido-database@file:../database": "gradido-database@file:../database":
version "1.20.0" version "1.21.0"
dependencies: dependencies:
"@types/uuid" "^8.3.4" "@types/uuid" "^8.3.4"
cross-env "^7.0.3" cross-env "^7.0.3"

View File

@ -10,10 +10,10 @@ module.exports = {
'standard', 'standard',
'eslint:recommended', 'eslint:recommended',
'plugin:prettier/recommended', 'plugin:prettier/recommended',
// 'plugin:import/recommended', 'plugin:import/recommended',
// 'plugin:import/typescript', 'plugin:import/typescript',
// 'plugin:security/recommended', // 'plugin:security/recommended',
// 'plugin:@eslint-community/eslint-comments/recommended', 'plugin:@eslint-community/eslint-comments/recommended',
], ],
settings: { settings: {
'import/parsers': { 'import/parsers': {
@ -37,44 +37,44 @@ module.exports = {
}, },
], ],
// import // import
// 'import/export': 'error', 'import/export': 'error',
// 'import/no-deprecated': 'error', 'import/no-deprecated': 'error',
// 'import/no-empty-named-blocks': 'error', 'import/no-empty-named-blocks': 'error',
// 'import/no-extraneous-dependencies': 'error', // 'import/no-extraneous-dependencies': 'error',
// 'import/no-mutable-exports': 'error', 'import/no-mutable-exports': 'error',
// 'import/no-unused-modules': 'error', 'import/no-unused-modules': 'error',
// 'import/no-named-as-default': 'error', 'import/no-named-as-default': 'error',
// 'import/no-named-as-default-member': 'error', 'import/no-named-as-default-member': 'error',
// 'import/no-amd': 'error', 'import/no-amd': 'error',
// 'import/no-commonjs': 'error', 'import/no-commonjs': 'error',
// 'import/no-import-module-exports': 'error', 'import/no-import-module-exports': 'error',
// 'import/no-nodejs-modules': 'off', 'import/no-nodejs-modules': 'off',
// 'import/unambiguous': 'error', 'import/unambiguous': 'error',
// 'import/default': 'error', 'import/default': 'error',
// 'import/named': 'error', 'import/named': 'error',
// 'import/namespace': 'error', 'import/namespace': 'error',
// 'import/no-absolute-path': 'error', 'import/no-absolute-path': 'error',
// 'import/no-cycle': 'error', // 'import/no-cycle': 'error',
// 'import/no-dynamic-require': 'error', 'import/no-dynamic-require': 'error',
// 'import/no-internal-modules': 'off', 'import/no-internal-modules': 'off',
// 'import/no-relative-packages': 'error', 'import/no-relative-packages': 'error',
// 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], // 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }],
// 'import/no-self-import': 'error', 'import/no-self-import': 'error',
// 'import/no-unresolved': 'error', 'import/no-unresolved': 'error',
// 'import/no-useless-path-segments': 'error', 'import/no-useless-path-segments': 'error',
// 'import/no-webpack-loader-syntax': 'error', 'import/no-webpack-loader-syntax': 'error',
// 'import/consistent-type-specifier-style': 'error', 'import/consistent-type-specifier-style': 'error',
// 'import/exports-last': 'off', 'import/exports-last': 'off',
// 'import/extensions': 'error', 'import/extensions': 'error',
// 'import/first': 'error', 'import/first': 'error',
// 'import/group-exports': 'off', 'import/group-exports': 'off',
// 'import/newline-after-import': 'error', 'import/newline-after-import': 'error',
// 'import/no-anonymous-default-export': 'error', 'import/no-anonymous-default-export': 'error',
// 'import/no-default-export': 'error', 'import/no-default-export': 'error',
// 'import/no-duplicates': 'error', 'import/no-duplicates': 'error',
// 'import/no-named-default': 'error', 'import/no-named-default': 'error',
// 'import/no-namespace': 'error', 'import/no-namespace': 'error',
// 'import/no-unassigned-import': 'error', 'import/no-unassigned-import': 'error',
// 'import/order': [ // 'import/order': [
// 'error', // 'error',
// { // {
@ -99,45 +99,45 @@ module.exports = {
// distinctGroup: true, // distinctGroup: true,
// }, // },
// ], // ],
// 'import/prefer-default-export': 'off', 'import/prefer-default-export': 'off',
// n // n
// 'n/handle-callback-err': 'error', 'n/handle-callback-err': 'error',
// 'n/no-callback-literal': 'error', 'n/no-callback-literal': 'error',
// 'n/no-exports-assign': 'error', 'n/no-exports-assign': 'error',
// 'n/no-extraneous-import': 'error', // 'n/no-extraneous-import': 'error',
// 'n/no-extraneous-require': 'error', 'n/no-extraneous-require': 'error',
// 'n/no-hide-core-modules': 'error', 'n/no-hide-core-modules': 'error',
// 'n/no-missing-import': 'off', // not compatible with typescript 'n/no-missing-import': 'off', // not compatible with typescript
// 'n/no-missing-require': 'error', 'n/no-missing-require': 'error',
// 'n/no-new-require': 'error', 'n/no-new-require': 'error',
// 'n/no-path-concat': 'error', 'n/no-path-concat': 'error',
// 'n/no-process-exit': 'error', // 'n/no-process-exit': 'error',
// 'n/no-unpublished-bin': 'error', 'n/no-unpublished-bin': 'error',
// 'n/no-unpublished-import': 'off', // TODO need to exclude seeds 'n/no-unpublished-import': 'off', // TODO need to exclude seeds
// 'n/no-unpublished-require': 'error', 'n/no-unpublished-require': 'error',
// 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], 'n/no-unsupported-features': ['error', { ignores: ['modules'] }],
// 'n/no-unsupported-features/es-builtins': 'error', 'n/no-unsupported-features/es-builtins': 'error',
// 'n/no-unsupported-features/es-syntax': 'error', 'n/no-unsupported-features/es-syntax': 'error',
// 'n/no-unsupported-features/node-builtins': 'error', 'n/no-unsupported-features/node-builtins': 'error',
// 'n/process-exit-as-throw': 'error', 'n/process-exit-as-throw': 'error',
// 'n/shebang': 'error', 'n/shebang': 'error',
// 'n/callback-return': 'error', 'n/callback-return': 'error',
// 'n/exports-style': 'error', 'n/exports-style': 'error',
// 'n/file-extension-in-import': 'off', 'n/file-extension-in-import': 'off',
// 'n/global-require': 'error', 'n/global-require': 'error',
// 'n/no-mixed-requires': 'error', 'n/no-mixed-requires': 'error',
// 'n/no-process-env': 'error', 'n/no-process-env': 'error',
// 'n/no-restricted-import': 'error', 'n/no-restricted-import': 'error',
// 'n/no-restricted-require': 'error', 'n/no-restricted-require': 'error',
// 'n/no-sync': 'error', // 'n/no-sync': 'error',
// 'n/prefer-global/buffer': 'error', 'n/prefer-global/buffer': 'error',
// 'n/prefer-global/console': 'error', 'n/prefer-global/console': 'error',
// 'n/prefer-global/process': 'error', 'n/prefer-global/process': 'error',
// 'n/prefer-global/text-decoder': 'error', 'n/prefer-global/text-decoder': 'error',
// 'n/prefer-global/text-encoder': 'error', 'n/prefer-global/text-encoder': 'error',
// 'n/prefer-global/url': 'error', 'n/prefer-global/url': 'error',
// 'n/prefer-global/url-search-params': 'error', 'n/prefer-global/url-search-params': 'error',
// 'n/prefer-promises/dns': 'error', 'n/prefer-promises/dns': 'error',
// 'n/prefer-promises/fs': 'error', // 'n/prefer-promises/fs': 'error',
// promise // promise
// 'promise/catch-or-return': 'error', // 'promise/catch-or-return': 'error',
@ -155,10 +155,10 @@ module.exports = {
// 'promise/prefer-await-to-callbacks': 'error', // 'promise/prefer-await-to-callbacks': 'error',
// 'promise/no-multiple-resolved': 'error', // 'promise/no-multiple-resolved': 'error',
// eslint comments // eslint comments
// '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
// '@eslint-community/eslint-comments/no-restricted-disable': 'error', '@eslint-community/eslint-comments/no-restricted-disable': 'error',
// '@eslint-community/eslint-comments/no-use': 'off', '@eslint-community/eslint-comments/no-use': 'off',
// '@eslint-community/eslint-comments/require-description': 'off', '@eslint-community/eslint-comments/require-description': 'off',
}, },
overrides: [ overrides: [
// only for ts files // only for ts files

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
import { UserSetting } from '../0002-add_settings/UserSetting' import { UserSetting } from './UserSetting'
// Moriz: I do not like the idea of having two user tables // Moriz: I do not like the idea of having two user tables
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) @Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm' import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,5 +1,5 @@
/* eslint-disable no-use-before-define */ /* eslint-disable no-use-before-define */
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { Contribution } from '../Contribution' import { Contribution } from '../Contribution'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Column, Column,

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Column, Column,

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Column, Column,

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Column, Column,

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Column, Column,

View File

@ -2,7 +2,7 @@ import { Contribution } from '../Contribution'
import { ContributionMessage } from '../ContributionMessage' import { ContributionMessage } from '../ContributionMessage'
import { User } from '../User' import { User } from '../User'
import { Transaction } from '../Transaction' import { Transaction } from '../Transaction'
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Entity, Entity,

View File

@ -2,7 +2,7 @@ import { Contribution } from '../Contribution'
import { ContributionMessage } from '../ContributionMessage' import { ContributionMessage } from '../ContributionMessage'
import { User } from '../User' import { User } from '../User'
import { Transaction } from '../Transaction' import { Transaction } from '../Transaction'
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { import {
BaseEntity, BaseEntity,
Entity, Entity,

View File

@ -1,5 +1,5 @@
/* eslint-disable no-use-before-define */ /* eslint-disable no-use-before-define */
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { Contribution } from '../Contribution' import { Contribution } from '../Contribution'

View File

@ -20,9 +20,8 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-unused-vars */
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
// Set precision value // Set precision value
Decimal.set({ Decimal.set({
@ -41,11 +40,13 @@ interface Decay {
duration: number | null duration: number | null
} }
/* eslint-disable no-unused-vars */
export enum TransactionTypeId { export enum TransactionTypeId {
CREATION = 1, CREATION = 1,
SEND = 2, SEND = 2,
RECEIVE = 3, RECEIVE = 3,
} }
/* eslint-enable no-unused-vars */
function decayFormula(value: Decimal, seconds: number): Decimal { function decayFormula(value: Decimal, seconds: number): Decimal {
return value.mul(new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds)) return value.mul(new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds))

View File

@ -15,7 +15,7 @@
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
import fs from 'fs' import fs from 'fs'
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
// Set precision value // Set precision value
Decimal.set({ Decimal.set({
@ -156,6 +156,5 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
} }
} }
/* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {} export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {}

View File

@ -11,6 +11,5 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
AND users.deleted_at IS NOT NULL;`) AND users.deleted_at IS NOT NULL;`)
} }
/* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {} export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {}

View File

@ -34,6 +34,5 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
} }
} }
/* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {} export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {}

View File

@ -48,5 +48,8 @@
"ts-mysql-migrate": "^1.0.2", "ts-mysql-migrate": "^1.0.2",
"typeorm": "^0.2.38", "typeorm": "^0.2.38",
"uuid": "^8.3.2" "uuid": "^8.3.2"
},
"engines": {
"node": ">=14"
} }
} }

View File

@ -1,6 +1,7 @@
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) /* eslint-disable n/no-process-env */
import dotenv from 'dotenv' import dotenv from 'dotenv'
dotenv.config() dotenv.config()
const constants = { const constants = {
@ -35,6 +36,4 @@ if (
) )
} }
const CONFIG = { ...constants, ...database, ...migrations } export const CONFIG = { ...constants, ...database, ...migrations }
export default CONFIG

View File

@ -1,6 +1,5 @@
import 'reflect-metadata'
import { createDatabase } from './prepare' import { createDatabase } from './prepare'
import CONFIG from './config' import { CONFIG } from './config'
import { createPool } from 'mysql' import { createPool } from 'mysql'
import { Migration } from 'ts-mysql-migrate' import { Migration } from 'ts-mysql-migrate'

View File

@ -1,6 +1,6 @@
import { createConnection } from 'mysql2/promise' import { createConnection } from 'mysql2/promise'
import CONFIG from './config' import { CONFIG } from './config'
export const createDatabase = async (): Promise<void> => { export const createDatabase = async (): Promise<void> => {
const con = await createConnection({ const con = await createConnection({

View File

@ -1,4 +1,4 @@
import Decimal from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ValueTransformer } from 'typeorm' import { ValueTransformer } from 'typeorm'
Decimal.set({ Decimal.set({

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB