From e49892d69c5af288ebd3c1531e26806a1a43012d Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 6 Apr 2022 10:56:27 +0200 Subject: [PATCH 01/17] Changed the value of code so that it get's the configured COMMUNITY_URL and redeem/ from the backend. --- backend/src/graphql/model/TransactionLink.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index 5081ffd7d..7d91a22ac 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -2,7 +2,7 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { User } from './User' - +import CONFIG from '@/config' @ObjectType() export class TransactionLink { constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) { @@ -11,7 +11,7 @@ export class TransactionLink { this.amount = transactionLink.amount this.holdAvailableAmount = transactionLink.holdAvailableAmount this.memo = transactionLink.memo - this.code = transactionLink.code + this.code = CONFIG.COMMUNITY_URL + 'redeem/' + transactionLink.code this.createdAt = transactionLink.createdAt this.validUntil = transactionLink.validUntil this.deletedAt = transactionLink.deletedAt From 54f5a0123f66500384ba4d983535fa0c7c603d33 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 6 Apr 2022 11:03:22 +0200 Subject: [PATCH 02/17] Remove the base url from the frontend. --- frontend/src/components/ClipboardCopy.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue index b952dbd8c..67461cb0f 100644 --- a/frontend/src/components/ClipboardCopy.vue +++ b/frontend/src/components/ClipboardCopy.vue @@ -18,7 +18,7 @@ export default { }, data() { return { - url: `${window.location.origin}/redeem/${this.code}`, + url: `${this.code}`, } }, methods: { From ff24798767d87a7adbbd75a027dbcdb1ed364796 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 6 Apr 2022 11:10:09 +0200 Subject: [PATCH 03/17] Add free line after import. --- backend/src/graphql/model/TransactionLink.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index 7d91a22ac..f0134470a 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -3,6 +3,7 @@ import Decimal from 'decimal.js-light' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { User } from './User' import CONFIG from '@/config' + @ObjectType() export class TransactionLink { constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) { From d984ff25827aa84881dc7c74bd0b91fa2fb95e0a Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 6 Apr 2022 18:44:19 +0200 Subject: [PATCH 04/17] Add COMMUNITY_REDEEM_LINK to the config. --- backend/.env.dist | 1 + backend/.env.template | 1 + backend/src/config/index.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/backend/.env.dist b/backend/.env.dist index 0a95eda6c..2dc6f2b5f 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -27,6 +27,7 @@ KLICKTIPP_APIKEY_EN=SomeFakeKeyEN COMMUNITY_NAME=Gradido Entwicklung COMMUNITY_URL=http://localhost/ COMMUNITY_REGISTER_URL=http://localhost/register +COMMUNITY_REDEEM_URL=http://localhost/redeem/ COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido. # Login Server diff --git a/backend/.env.template b/backend/.env.template index 66cac7a7c..f9a5f3e32 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -26,6 +26,7 @@ KLICKTIPP_APIKEY_EN=$KLICKTIPP_APIKEY_EN COMMUNITY_NAME=$COMMUNITY_NAME COMMUNITY_URL=$COMMUNITY_URL COMMUNITY_REGISTER_URL=$COMMUNITY_REGISTER_URL +COMMUNITY_REDEEM_URL=$COMMUNITY_REDEEM_URL COMMUNITY_DESCRIPTION=$COMMUNITY_DESCRIPTION # Login Server diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 50388d29a..9c6e082c7 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -50,6 +50,7 @@ const community = { COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung', COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/', COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register', + COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/', COMMUNITY_DESCRIPTION: process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.', } From 41b35f2c5e6674e1c5922d0ac25efa287cfa7693 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 6 Apr 2022 18:45:27 +0200 Subject: [PATCH 05/17] Add link to the TransactionLink and change code by link in frontend. --- backend/src/graphql/model/TransactionLink.ts | 6 +++++- frontend/src/components/ClipboardCopy.vue | 4 ++-- frontend/src/components/GddSend/TransactionResultLink.vue | 4 ++-- .../src/components/TransactionLinks/TransactionLink.vue | 3 ++- frontend/src/graphql/mutations.js | 1 + frontend/src/graphql/queries.js | 1 + frontend/src/pages/Send.vue | 4 +++- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index f0134470a..652545bbd 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -12,12 +12,13 @@ export class TransactionLink { this.amount = transactionLink.amount this.holdAvailableAmount = transactionLink.holdAvailableAmount this.memo = transactionLink.memo - this.code = CONFIG.COMMUNITY_URL + 'redeem/' + transactionLink.code + this.code = transactionLink.code this.createdAt = transactionLink.createdAt this.validUntil = transactionLink.validUntil this.deletedAt = transactionLink.deletedAt this.redeemedAt = transactionLink.redeemedAt this.redeemedBy = redeemedBy + this.link = CONFIG.COMMUNITY_REDEEM_URL + this.code } @Field(() => Number) @@ -52,6 +53,9 @@ export class TransactionLink { @Field(() => User, { nullable: true }) redeemedBy: User | null + + @Field(() => String) + link: string } @ObjectType() diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue index 67461cb0f..ddc2cde8e 100644 --- a/frontend/src/components/ClipboardCopy.vue +++ b/frontend/src/components/ClipboardCopy.vue @@ -14,11 +14,11 @@ export default { name: 'ClipboardCopy', props: { - code: { type: String, required: true }, + link: { type: String, required: true }, }, data() { return { - url: `${this.code}`, + url: `${this.link}`, } }, methods: { diff --git a/frontend/src/components/GddSend/TransactionResultLink.vue b/frontend/src/components/GddSend/TransactionResultLink.vue index 66b5529d6..79369fc99 100644 --- a/frontend/src/components/GddSend/TransactionResultLink.vue +++ b/frontend/src/components/GddSend/TransactionResultLink.vue @@ -5,7 +5,7 @@
{{ $t('gdd_per_link.created') }}
- +

{{ $t('form.close') }} @@ -24,7 +24,7 @@ export default { ClipboardCopy, }, props: { - code: { + link: { type: String, required: true, }, diff --git a/frontend/src/components/TransactionLinks/TransactionLink.vue b/frontend/src/components/TransactionLinks/TransactionLink.vue index 9cbfdfb93..1c5ad690f 100644 --- a/frontend/src/components/TransactionLinks/TransactionLink.vue +++ b/frontend/src/components/TransactionLinks/TransactionLink.vue @@ -55,6 +55,7 @@ export default { props: { amount: { type: String, required: true }, code: { type: String, required: true }, + link: { type: String, required: true }, holdAvailableAmount: { type: String, required: true }, id: { type: Number, required: true }, memo: { type: String, required: true }, @@ -62,7 +63,7 @@ export default { }, methods: { copy() { - const link = `${window.location.origin}/redeem/${this.code}` + const link = `${this.link}` navigator.clipboard .writeText(link) .then(() => { diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 3dbd8888e..754d8930a 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -76,6 +76,7 @@ export const createTransactionLink = gql` mutation($amount: Decimal!, $memo: String!) { createTransactionLink(amount: $amount, memo: $memo) { code + link } } ` diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index b8622ef2d..3452694d4 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -155,6 +155,7 @@ export const listTransactionLinks = gql` holdAvailableAmount memo code + link createdAt validUntil redeemedAt diff --git a/frontend/src/pages/Send.vue b/frontend/src/pages/Send.vue index 613342dba..533678a6e 100644 --- a/frontend/src/pages/Send.vue +++ b/frontend/src/pages/Send.vue @@ -41,7 +41,7 @@ >


@@ -88,6 +88,7 @@ export default { currentTransactionStep: TRANSACTION_STEPS.transactionForm, loading: false, code: null, + link: null, } }, props: { @@ -145,6 +146,7 @@ export default { .then((result) => { this.$emit('set-tunneled-email', null) this.code = result.data.createTransactionLink.code + this.link = result.data.createTransactionLink.link this.transactionData = { ...EMPTY_TRANSACTION_DATA } this.currentTransactionStep = TRANSACTION_STEPS.transactionResultLink this.updateTransactions({}) From 2cc7a6dee67e568e88a832b74ab647b08d6f0b34 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 6 Apr 2022 19:16:30 +0200 Subject: [PATCH 06/17] Fixing tests since we now use link and code. --- .../components/DecayInformations/CollapseLinksList.spec.js | 2 ++ .../src/components/TransactionLinks/TransactionLink.spec.js | 1 + .../components/Transactions/TransactionLinkSummary.spec.js | 4 ++++ frontend/src/pages/Send.spec.js | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/DecayInformations/CollapseLinksList.spec.js b/frontend/src/components/DecayInformations/CollapseLinksList.spec.js index b533c6aa1..b288f78d0 100644 --- a/frontend/src/components/DecayInformations/CollapseLinksList.spec.js +++ b/frontend/src/components/DecayInformations/CollapseLinksList.spec.js @@ -16,6 +16,7 @@ const propsData = { { amount: '5', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 87, @@ -26,6 +27,7 @@ const propsData = { { amount: '6', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 86, diff --git a/frontend/src/components/TransactionLinks/TransactionLink.spec.js b/frontend/src/components/TransactionLinks/TransactionLink.spec.js index f019a0ee1..8e455360b 100644 --- a/frontend/src/components/TransactionLinks/TransactionLink.spec.js +++ b/frontend/src/components/TransactionLinks/TransactionLink.spec.js @@ -22,6 +22,7 @@ const mocks = { const propsData = { amount: '75', code: 'c00000000c000000c0000', + link: 'http://localhost/redeem/c00000000c000000c0000', holdAvailableAmount: '5.13109484759482747111', id: 12, memo: 'Wie schön hier etwas Quatsch zu lesen!', diff --git a/frontend/src/components/Transactions/TransactionLinkSummary.spec.js b/frontend/src/components/Transactions/TransactionLinkSummary.spec.js index 1f2a4388c..7dfd0d41b 100644 --- a/frontend/src/components/Transactions/TransactionLinkSummary.spec.js +++ b/frontend/src/components/Transactions/TransactionLinkSummary.spec.js @@ -44,6 +44,7 @@ describe('TransactionLinkSummary', () => { { amount: '75', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 86, @@ -55,6 +56,7 @@ describe('TransactionLinkSummary', () => { { amount: '85', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 107, @@ -65,6 +67,7 @@ describe('TransactionLinkSummary', () => { { amount: '95', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 92, @@ -76,6 +79,7 @@ describe('TransactionLinkSummary', () => { { amount: '150', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 16, diff --git a/frontend/src/pages/Send.spec.js b/frontend/src/pages/Send.spec.js index 447fdde33..148d62168 100644 --- a/frontend/src/pages/Send.spec.js +++ b/frontend/src/pages/Send.spec.js @@ -162,7 +162,7 @@ describe('Send', () => { describe('transaction form link', () => { beforeEach(async () => { apolloMutationMock.mockResolvedValue({ - data: { createTransactionLink: { code: '0123456789' } }, + data: { createTransactionLink: { code: '0123456789', link: 'http://localhost/redeem/0123456789' } }, }) const transactionForm = wrapper.findComponent({ name: 'TransactionForm' }) await transactionForm.findAll('input[type="radio"]').at(1).setChecked() From f6b9b7664cb8071d02525340ea6b2e6bcecd1c3e Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 7 Apr 2022 11:00:29 +0200 Subject: [PATCH 07/17] Add link to mocked return value. --- .../components/Transactions/TransactionLinkSummary.spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/components/Transactions/TransactionLinkSummary.spec.js b/frontend/src/components/Transactions/TransactionLinkSummary.spec.js index 7dfd0d41b..1d3d70527 100644 --- a/frontend/src/components/Transactions/TransactionLinkSummary.spec.js +++ b/frontend/src/components/Transactions/TransactionLinkSummary.spec.js @@ -155,6 +155,7 @@ describe('TransactionLinkSummary', () => { { amount: '76', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 87, @@ -166,6 +167,7 @@ describe('TransactionLinkSummary', () => { { amount: '86', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 108, @@ -177,6 +179,7 @@ describe('TransactionLinkSummary', () => { { amount: '96', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 93, @@ -188,6 +191,7 @@ describe('TransactionLinkSummary', () => { { amount: '150', code: 'ce28664b5308c17f931c0367', + link: 'http://localhost/redeem/ce28664b5308c17f931c0367', createdAt: '2022-03-16T14:22:40.000Z', holdAvailableAmount: '5.13109484759482747111', id: 17, From 2f2c00605f50e6bd192ef79208004fb6075f1275 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 8 Apr 2022 08:14:12 +0200 Subject: [PATCH 08/17] Change the config parameter community_redeem_url to a regex. --- backend/.env.dist | 2 +- backend/src/config/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index 2dc6f2b5f..929263107 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -27,7 +27,7 @@ KLICKTIPP_APIKEY_EN=SomeFakeKeyEN COMMUNITY_NAME=Gradido Entwicklung COMMUNITY_URL=http://localhost/ COMMUNITY_REGISTER_URL=http://localhost/register -COMMUNITY_REDEEM_URL=http://localhost/redeem/ +COMMUNITY_REDEEM_URL=http://localhost/redeem/{code} COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido. # Login Server diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 9c6e082c7..d0b693ad7 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -50,7 +50,7 @@ const community = { COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung', COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/', COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register', - COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/', + COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/{code}', COMMUNITY_DESCRIPTION: process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.', } From 6588a636dd39be354d78106698ece09334b45a4d Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 8 Apr 2022 08:14:50 +0200 Subject: [PATCH 09/17] Change the composition of the link from addition to replace. --- backend/src/graphql/model/TransactionLink.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index 652545bbd..18a601948 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -18,7 +18,7 @@ export class TransactionLink { this.deletedAt = transactionLink.deletedAt this.redeemedAt = transactionLink.redeemedAt this.redeemedBy = redeemedBy - this.link = CONFIG.COMMUNITY_REDEEM_URL + this.code + this.link = CONFIG.COMMUNITY_REDEEM_URL.replace(/{code}/g, this.code) } @Field(() => Number) From de7891f492abda1d7ca77bd451533315d7d3f242 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 8 Apr 2022 08:16:20 +0200 Subject: [PATCH 10/17] Fix linting. --- frontend/src/pages/Send.spec.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/Send.spec.js b/frontend/src/pages/Send.spec.js index 148d62168..76757a247 100644 --- a/frontend/src/pages/Send.spec.js +++ b/frontend/src/pages/Send.spec.js @@ -162,7 +162,12 @@ describe('Send', () => { describe('transaction form link', () => { beforeEach(async () => { apolloMutationMock.mockResolvedValue({ - data: { createTransactionLink: { code: '0123456789', link: 'http://localhost/redeem/0123456789' } }, + data: { + createTransactionLink: { + code: '0123456789', + link: 'http://localhost/redeem/0123456789', + }, + }, }) const transactionForm = wrapper.findComponent({ name: 'TransactionForm' }) await transactionForm.findAll('input[type="radio"]').at(1).setChecked() From 00e03af7f31090f339903047d1c1efcac6a57490 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 21 Apr 2022 13:19:01 +0200 Subject: [PATCH 11/17] increase backend config version v6 --- backend/.env.dist | 2 +- backend/src/config/index.ts | 2 +- deployment/bare_metal/.env.dist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index f4a17e81a..de33a7272 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -1,4 +1,4 @@ -CONFIG_VERSION=v5.2022-04-12 +CONFIG_VERSION=v6.2022-04-21 # Server PORT=4000 diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 0cd0f408d..86cefe9e6 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -14,7 +14,7 @@ const constants = { DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v5.2022-04-12', + EXPECTED: 'v6.2022-04-21', CURRENT: '', }, } diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index efac0fc68..316fb60c2 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -24,7 +24,7 @@ COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community" # backend -BACKEND_CONFIG_VERSION=v5.2022-04-12 +BACKEND_CONFIG_VERSION=v6.2022-04-21 JWT_EXPIRES_IN=30m GDT_API_URL=https://gdt.gradido.net From f9efc3fa06fdae058880d80dcc89d17ef661bbef Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 21 Apr 2022 13:22:52 +0200 Subject: [PATCH 12/17] use link instead of url, do not use v-model, use value to not allow mutate prop --- frontend/src/components/ClipboardCopy.vue | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue index ddc2cde8e..ff8a3ae87 100644 --- a/frontend/src/components/ClipboardCopy.vue +++ b/frontend/src/components/ClipboardCopy.vue @@ -1,7 +1,7 @@