diff --git a/.github/workflows/deploy-eos-staging.yml b/.github/workflows/deploy-eos-staging.yml index 9c48c0d19..2ebcf3242 100644 --- a/.github/workflows/deploy-eos-staging.yml +++ b/.github/workflows/deploy-eos-staging.yml @@ -2,7 +2,9 @@ name: EOS - Build and Deploy to Staging - MainNet on: push: - branches: [ master ] + branches: + - master + - feat/add-support-for-dgraph-cloud-with-namespaces jobs: build: @@ -21,7 +23,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v1 with: - node-version: '16.x' + node-version: '18.x' - run: yarn global add @vue/cli@latest - run: yarn install - run: mv public/chain-manifests-prod.json public/chain-manifests.json @@ -36,6 +38,8 @@ jobs: MULTISIG_CONTRACT: 'msig.hypha' KV_CONTRACT: 'kv.hypha' PPP_ENV: ${{ vars.PPP_ENV }} + ENV: 'prod' + HYPHA_AUTH_URL: ${{ vars.HYPHA_AUTH_URL }} PPP_APP_ID: ${{ vars.PPP_APP_ID }} ACCOUNT_API_URL: ${{ vars.ACCOUNT_API_URL }} ACCOUNT_API_KEY: ${{ secrets.ACCOUNT_API_KEY }} @@ -44,14 +48,14 @@ jobs: BLOCKCHAIN_EXPLORER_BTC: 'https://www.blockchain.com/btc/tx/' BLOCKCHAIN_EXPLORER_ETH: 'https://etherscan.io/tx/' BLOCKCHAIN_EXPLORER_EOS: 'https://bloks.io/transaction/' - + CHAIN_NAME: 'eos' SENTRY_DSN: ${{ secrets.SENTRY_DSN }} DOCUMENTATION: ${{ vars.DOCUMENTATION }} - DGRAPH_URL: ${{ vars.DGRAPH_URL }} + DGRAPH_URL: https://hypha.us-east-1.aws.cloud.dgraph.io DGRAPH_ROOT_HASH: ${{ vars.DGRAPH_ROOT_HASH }} DGRAPH_AUTH_KEY: ${{ secrets.DGRAPH_AUTH_KEY }} - GRAPHQL_URI: ${{vars.GRAPHQL_URI}} + GRAPHQL_URI: https://hypha.us-east-1.aws.cloud.dgraph.io/graphql ROOT_DAO_ID: ${{vars.ROOT_DAO_ID}} ROOT_DAO_SLUG: ${{vars.ROOT_DAO_SLUG}} HEALTH_ENDPOINT: ${{ vars.HEALTH_ENDPOINT }} diff --git a/package.json b/package.json index fa6d0dac7..611262214 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "author": "Max ", "license": "Apache-2.0", "scripts": { - "dev": "export NODE_OPTIONS=--openssl-legacy-provider && quasar dev -m spa", + "dev": "quasar dev -m spa", "dev:eos": "env-cmd -f ./config/eos-testnet.env quasar dev -m spa", "prod:telos": "env-cmd -f ./config/telos-mainnet.env quasar dev -m spa", "prod:eos": "env-cmd -f ./config/eos-mainnet.env quasar dev -m spa", - "build": "quasar build", + "build": "NODE_OPTIONS=--openssl-legacy-provider quasar build", "lint": "eslint --ext .js,.vue .", "test": "exit 0", "storybook": "start-storybook -s ./public -p 6006", @@ -32,6 +32,7 @@ "@sentry/integrations": "6.3.1", "@sentry/tracing": "^7.14.2", "@sentry/vue": "^7.14.2", + "apollo-link-token-refresh": "v0.2", "apollo-link-ws": "^1.0.20", "apollo-utilities": "^1.3.4", "axios": "0.21.1", @@ -44,6 +45,7 @@ "eosjs-ecc": "^4.0.7", "ipfs-http-client": "55.0.0", "it-all": "^1.0.6", + "jwt-decode": "^4.0.0", "libphonenumber-js": "^1.10.6", "qrcode.vue": "1.7.0", "quasar": "1.15.10", diff --git a/quasar.conf.js b/quasar.conf.js index 1e43f6eea..e68116b3c 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -103,16 +103,25 @@ module.exports = function (ctx) { HYPHA_TOKEN_SALES_RPC_URL: process.env.HYPHA_TOKEN_SALES_RPC_URL, PACKAGE_VERSION: JSON.parse(packageJson).version || 0, HEALTH_ENDPOINT: process.env.HEALTH_ENDPOINT, - CHAIN_NAME: process.env.CHAIN_NAME, + CHAIN_NAME: process.env.CHAIN_NAME || 'telos', + IS_TESTNET: process.env.IS_TESTNET, + HYPHA_AUTH_URL: + process.env.HYPHA_AUTH_URL || + 'https://y3b2ihsdv7.execute-api.us-east-1.amazonaws.com', CAPTCHA_PUBLIC_KEY: process.env.CAPTCHA_PUBLIC_KEY, CAPTCHA_HOST: process.env.CAPTCHA_HOST, CAPTCHA_NETWORK: process.env.CAPTCHA_NETWORK || 'telosTestnet', LOGIN_CONTRACT: process.env.LOGIN_CONTRACT, JOIN_CONTRACT: process.env.JOIN_CONTRACT, JOIN_URI: process.env.JOIN_URI, - DEFFERED_HYPHA_CONTRACT: process.env.DEFFERED_HYPHA_CONTRACT || 'costak.hypha', - DOWNLOAD_WALLET_LINK_IOS: process.env.DOWNLOAD_WALLET_LINK_IOS || "http://itunes.apple.com/lb/app/1659926348", - DOWNLOAD_WALLET_LINK_ANDROID: process.env.DOWNLOAD_WALLET_LINK_ANDROID || "http://play.google.com/store/apps/details?id=earth.hypha.wallet.hypha_wallet" + DEFFERED_HYPHA_CONTRACT: + process.env.DEFFERED_HYPHA_CONTRACT || 'costak.hypha', + DOWNLOAD_WALLET_LINK_IOS: + process.env.DOWNLOAD_WALLET_LINK_IOS || + 'http://itunes.apple.com/lb/app/1659926348', + DOWNLOAD_WALLET_LINK_ANDROID: + process.env.DOWNLOAD_WALLET_LINK_ANDROID || + 'http://play.google.com/store/apps/details?id=earth.hypha.wallet.hypha_wallet' }, scopeHoisting: true, diff --git a/src/apollo/apollo-client-config.js b/src/apollo/apollo-client-config.js index 4e879e0de..700e66d03 100644 --- a/src/apollo/apollo-client-config.js +++ b/src/apollo/apollo-client-config.js @@ -1,4 +1,7 @@ -import { IntrospectionFragmentMatcher, defaultDataIdFromObject } from 'apollo-cache-inmemory' +import { + IntrospectionFragmentMatcher, + defaultDataIdFromObject +} from 'apollo-cache-inmemory' import schema from './schema.json' export default async function (/* { app, router, store, ssrContext, urlPath, redirect } */) { @@ -20,7 +23,7 @@ export default async function (/* { app, router, store, ssrContext, urlPath, red fragmentMatcher: new IntrospectionFragmentMatcher({ introspectionQueryResultData: schema }), - dataIdFromObject: r => { + dataIdFromObject: (r) => { if (r.docId && r.__typename) { return `${r.__typename}:${r.docId}` } diff --git a/src/apollo/apollo-client-hooks.js b/src/apollo/apollo-client-hooks.js index 0750dfdd7..89a6335c1 100644 --- a/src/apollo/apollo-client-hooks.js +++ b/src/apollo/apollo-client-hooks.js @@ -2,15 +2,68 @@ import { HttpLink } from 'apollo-link-http' import { split } from 'apollo-link' import { WebSocketLink } from 'apollo-link-ws' import { getMainDefinition } from 'apollo-utilities' +import { TokenRefreshLink } from 'apollo-link-token-refresh' // import TokenRefreshLink +import { jwtDecode } from 'jwt-decode' + +const hyphaAccessTokenKey = 'hyphaAccessToken' +const hyphaRefreshTokenKey = 'hyphaRefreshToken' + +const tokenRefreshLink = new TokenRefreshLink({ + isTokenValidOrUndefined: () => { + const token = localStorage.getItem(hyphaAccessTokenKey) + if (!token) return false // if no token, assume it's valid + + try { + const { exp } = jwtDecode(token) + + return Date.now() < exp * 1000 + } catch { + return false + } + }, + fetchAccessToken: () => { + const HYPHA_AUTH_URL = process.env.HYPHA_AUTH_URL + const CHAIN_NAME = process.env.CHAIN_NAME + const ENV = process.env.ENV || process.env.PPP_ENV || 'prod' + const network = process.env.IS_TESTNET ? 'testnet' : 'mainnet' + const url = `${HYPHA_AUTH_URL}?chain=${CHAIN_NAME}&env=${ENV}&network=${network}` + + return fetch(url, { + method: 'GET' + }) + }, + handleFetch: (accessToken) => { + localStorage.setItem(hyphaAccessTokenKey, accessToken) + }, + handleResponse: (operation, accessTokenField) => async (response) => { + const data = await response.json() + + localStorage.setItem(hyphaAccessTokenKey, data.accessJWT) + localStorage.setItem(hyphaRefreshTokenKey, data.refreshJWT) + + return response + }, + handleError: (err) => { + // Handle token refresh errors (e.g., logout user) + console.warn('Your refresh token is invalid. Try to relogin') + console.error(err) + } +}) const httpLink = new HttpLink({ - // You should use an absolute URL here - uri: process.env.GRAPHQL_URI || 'https://alpha-stt.tekit.io/graphql' + uri: process.env.GRAPHQL_URI || 'https://alpha-stt.tekit.io/graphql', + fetch: (uri, options) => { + const accessToken = localStorage.getItem('hyphaAccessToken') // get the access token from storage + options.headers['X-Dgraph-AccessToken'] = accessToken + return fetch(uri, options) + } }) // Create the subscription websocket link const wsLink = new WebSocketLink({ - uri: process.env.GRAPHQL_URI.replace('https', 'wss') || 'wss://alpha-stts.tekit.io/graphql', + uri: + process.env.GRAPHQL_URI.replace('https', 'wss') || + 'wss://alpha-stts.tekit.io/graphql', options: { reconnect: true } @@ -22,20 +75,35 @@ const link = split( // split based on operation type ({ query }) => { const definition = getMainDefinition(query) - return definition.kind === 'OperationDefinition' && + return ( + definition.kind === 'OperationDefinition' && definition.operation === 'subscription' + ) }, wsLink, - httpLink + tokenRefreshLink.concat(httpLink) ) -export async function apolloClientBeforeCreate ({ apolloClientConfigObj, app, router, store, ssrContext, urlPath, redirect }) { +export async function apolloClientBeforeCreate({ + apolloClientConfigObj, + app, + router, + store, + ssrContext, + urlPath, + redirect +}) { // if needed you can modify here the config object used for apollo client // instantiation apolloClientConfigObj.link = link } -export async function apolloClientAfterCreate ({ apolloClient, store }/* { apolloClient, app, router, store, ssrContext, urlPath, redirect } */) { +export async function apolloClientAfterCreate( + { + apolloClient, + store + } /* { apolloClient, app, router, store, ssrContext, urlPath, redirect } */ +) { // if needed you can modify here the created apollo client store.$apollo = apolloClient } diff --git a/src/components/navigation/left-navigation.vue b/src/components/navigation/left-navigation.vue index 470956fba..cb3e4510e 100644 --- a/src/components/navigation/left-navigation.vue +++ b/src/components/navigation/left-navigation.vue @@ -35,7 +35,7 @@ export default { } }, fetchPolicy: 'no-cache', - pollInterval: 1000, + // pollInterval: 1000, skip () { return !this.selectedDao || !this.selectedDao.docId } } }, diff --git a/src/components/profiles/wallet-base.vue b/src/components/profiles/wallet-base.vue index 1c7ae9521..f116e0a05 100644 --- a/src/components/profiles/wallet-base.vue +++ b/src/components/profiles/wallet-base.vue @@ -101,8 +101,8 @@ export default { username: this.account } }, - skip () { return !this.selectedDao?.docId }, - pollInterval: 1000 + // pollInterval: 1000, + skip () { return !this.selectedDao?.docId } } }, diff --git a/src/components/proposals/voter-list.vue b/src/components/proposals/voter-list.vue index e4c78d332..932ebe41e 100644 --- a/src/components/proposals/voter-list.vue +++ b/src/components/proposals/voter-list.vue @@ -50,16 +50,16 @@ export default { query: gql`query proposalVotes($docId: String!, $first: Int!, $offset: Int!) { ${PROPOSAL_VOTES_QUERY} }`, update: data => data.queryVote, skip () { return !this.proposalId }, + // pollInterval: 1000, variables () { return { docId: this.proposalId, first: this.size, offset: this.page * this.size } - }, + } // fetchPolicy: 'no-cache', - pollInterval: 1000 // subscribeToMore: { // document: gql`subscription proposalVotes($docId: String!, $first: Int, $offset: Int) { ${PROPOSAL_VOTES_QUERY} }`, // skip () { return !this.proposalId }, diff --git a/src/pages/dho/Ecosystem.vue b/src/pages/dho/Ecosystem.vue index db719ebf3..56abcac59 100644 --- a/src/pages/dho/Ecosystem.vue +++ b/src/pages/dho/Ecosystem.vue @@ -130,8 +130,8 @@ export default { }) }, variables () { return { daoId: this.selectedDao.docId } }, - skip () { return !this.selectedDao?.docId }, - pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + // pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + skip () { return !this.selectedDao?.docId } } }, diff --git a/src/pages/dho/Election.vue b/src/pages/dho/Election.vue index e89fc314d..f5528c25c 100644 --- a/src/pages/dho/Election.vue +++ b/src/pages/dho/Election.vue @@ -82,7 +82,7 @@ export default { } }, fetchPolicy: 'no-cache', - pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + // pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY skip () { return !this.selectedDao || !this.selectedDao.docId } }, ongoingElection: { @@ -94,7 +94,7 @@ export default { } }, fetchPolicy: 'no-cache', - pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + // pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY skip () { return !this.selectedDao || !this.selectedDao.docId } }, upcomingElection: { @@ -106,7 +106,7 @@ export default { } }, fetchPolicy: 'no-cache', - pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + // pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY skip () { return !this.selectedDao || !this.selectedDao.docId } } }, diff --git a/src/pages/dho/Explore.vue b/src/pages/dho/Explore.vue index 8e9b0fd09..12840279c 100644 --- a/src/pages/dho/Explore.vue +++ b/src/pages/dho/Explore.vue @@ -27,7 +27,7 @@ export default { daoName: '', first: 6, offset: 0, - more: true, + more: false, restart: false, view: 'card', @@ -200,7 +200,7 @@ export default { async resetPagination () { this.restart = true this.offset = 0 - this.more = true + this.more = false await this.$nextTick() this.$refs.scroll.stop() diff --git a/src/pages/dho/Treasury.vue b/src/pages/dho/Treasury.vue index 0b426f6b3..fe77a6c29 100644 --- a/src/pages/dho/Treasury.vue +++ b/src/pages/dho/Treasury.vue @@ -171,6 +171,7 @@ export default { skip () { return !this.selectedDao || !this.selectedDao.docId }, + // pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY variables () { const rowsPerPage = this.pagination.rowsPerPage || 10 return { @@ -181,8 +182,7 @@ export default { }, filter: this.tab === MULTISIG_TABS.PAYOUT ? { not: { has: 'paidby' } } : {} } - }, - pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + } }, daoMultisigSignRequestsQuery: { query: require('~/query/treasury/dao-multisig-sign-requests.gql'), @@ -208,8 +208,8 @@ export default { daoId: this.selectedDao.docId } }, - skip () { return !this.selectedDao?.docId }, - pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + // pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + skip () { return !this.selectedDao?.docId } }, daoMultisigReadyExecRequestsQuery: { query: require('~/query/treasury/dao-multisig-sign-requests.gql'), @@ -237,10 +237,10 @@ export default { } }, skip () { return !this.selectedDao?.docId }, + // pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY result: async function () { await this.formatExecReuqests() - }, - pollInterval: 1000 // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + } } }, async beforeMount () { diff --git a/src/pages/onboarding/create.vue b/src/pages/onboarding/create.vue index 70114ea61..620a3f098 100644 --- a/src/pages/onboarding/create.vue +++ b/src/pages/onboarding/create.vue @@ -308,8 +308,8 @@ export default { const query = await this.$apollo.watchQuery({ query: isDraft ? require('~/query/dao-draft-created.gql') : require('~/query/dao-created.gql'), + // pollInterval: 100, variables: isDraft ? { daoName: this.form.name } : { regexp: '/^' + this.form.name + '$/i' }, - pollInterval: 100 }) query.subscribe(({ data, loading }) => { diff --git a/src/pages/upvote-election/UpvoteElection.vue b/src/pages/upvote-election/UpvoteElection.vue index f79868173..2ec50aebd 100644 --- a/src/pages/upvote-election/UpvoteElection.vue +++ b/src/pages/upvote-election/UpvoteElection.vue @@ -150,7 +150,7 @@ export default { skip () { return !this.account || !this.selectedDao || !this.selectedDao.docId }, - pollInterval: 1000, + // pollInterval: 1000, fetchPolicy: 'no-cache', result (res) { if (res.data.getDao?.badge?.find(badge => badge.details_title_s === ELECTION_BADGES.DELEGATE)) { @@ -211,7 +211,7 @@ export default { } }, fetchPolicy: 'no-cache', - pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY + // pollInterval: 1000, // THIS IS JUST TEMPORARY UNTIL GRAPHQL SUBSCRIPTION IS READY skip () { return !this.selectedDao || !this.selectedDao.docId }, result (res) { this.statusSetUp() diff --git a/src/store/payments/actions.js b/src/store/payments/actions.js index cad56bfa2..4b7edcc2c 100644 --- a/src/store/payments/actions.js +++ b/src/store/payments/actions.js @@ -40,6 +40,7 @@ export const countPayments = async function ({ rootState }) { return result.data.payments[0].total } +// TODO: Remove this, used by legacy dgraph queries export const loadPayments = async function ({ rootState }, { page, rowsPerPage }) { // TODO: Get rid of 'has(payment)' call, in lieu of typed get? const query = ` diff --git a/src/store/payouts/actions.js b/src/store/payouts/actions.js index d6fd82ff3..995afce25 100644 --- a/src/store/payouts/actions.js +++ b/src/store/payouts/actions.js @@ -30,6 +30,7 @@ export const savePayoutProposal = async function ({ rootState }, draft) { return this.$api.signTransaction(actions) } +// TODO: Remove this, used by legacy dgraph queries export const loadPayouts = async function ({ commit }, { first, offset }) { const query = ` query payouts($first:int, $offset: int) { @@ -79,6 +80,7 @@ export const loadPayouts = async function ({ commit }, { first, offset }) { return result.data.payouts.length === 0 } +// TOdO: Remove this, used by legacy dgraph queries export const loadUserPayouts = async function ({ commit }, { first, offset, user }) { const query = ` query payouts($first:int, $offset: int, $user: string) { diff --git a/yarn.lock b/yarn.lock index 62b10f864..598c524ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6178,6 +6178,13 @@ apollo-link-http@^1.5.16: apollo-link-http-common "^0.2.16" tslib "^1.9.3" +apollo-link-token-refresh@v0.2: + version "0.2.7" + resolved "https://registry.yarnpkg.com/apollo-link-token-refresh/-/apollo-link-token-refresh-0.2.7.tgz#0ccfffce3510e5c8d399eefdedf9cc438d1c3d76" + integrity sha512-RMLrHObXnjM7L+8bf5HFK6uhGjSX5lOSyyWUHWcsb7xF3c1yAkZxFIopNiYkgRh/2G44kloGu/xt2uniYYM9DA== + dependencies: + apollo-link "^1.2.3" + apollo-link-ws@^1.0.20: version "1.0.20" resolved "https://registry.yarnpkg.com/apollo-link-ws/-/apollo-link-ws-1.0.20.tgz#dfad44121f8445c6d7b7f8101a1b24813ba008ed" @@ -6186,7 +6193,7 @@ apollo-link-ws@^1.0.20: apollo-link "^1.2.14" tslib "^1.9.3" -apollo-link@^1.0.0, apollo-link@^1.2.13, apollo-link@^1.2.14: +apollo-link@^1.0.0, apollo-link@^1.2.13, apollo-link@^1.2.14, apollo-link@^1.2.3: version "1.2.14" resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9" integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg== @@ -13377,6 +13384,11 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +jwt-decode@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b" + integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"