diff --git a/.github/workflows/deploy-to-cloudrun-production.yml b/.github/workflows/deploy-to-cloudrun-production.yml new file mode 100644 index 000000000..1ef6e42fc --- /dev/null +++ b/.github/workflows/deploy-to-cloudrun-production.yml @@ -0,0 +1,96 @@ +name: "Deploy to Production Cloud Run" + +on: + push: + branches: + - prod-gcp + - jp-testing + +env: + + # DB VARS + db_username: ${{ secrets.DB_USERNAME }} + db_password: ${{ secrets.DB_PASSWORD }} + db_hostname: ${{ secrets.DB_HOSTNAME }} + db_port: ${{ secrets.DB_PORT }} + db_name: ${{ secrets.DB_NAME }} + backend_api: ${{ secrets.BACKEND_API }} + tx_options: ${{ secrets.TX_OPTIONS }} + hotjar_site_id: ${{ secrets.HOTJAR_STAGE_ID }} + sentry_url: ${{ secrets.SENTRY_STAGE_URL }} + + # GCP VARS + BACKEND_DOCKER_IMAGE_URL: ${{ vars.BACKEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + FRONTEND_DOCKER_IMAGE_URL: ${{ vars.FRONTNED_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + GAR_LOCATION: ${{ vars.GCP_GAR_LOCATION }} + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }} + WORKLOAD_IDENTITY_PROVIDER: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ env.SERVICE_ACCOUNT }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 + with: + project_id: ${{ env.PROJECT_ID }} + + - name: Build CAST Backend + run: |- + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + docker build -t ${{ env.BACKEND_DOCKER_IMAGE_URL }} --file backend/Dockerfile ./backend + docker push ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Build CAST Frontend + env: + BACKEND: ${{ env.backend_api }} + run: | + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + cd frontend && sed -i -e 's/REPLACE_HOTJAR_SITE_ID/${{ env.hotjar_site_id }}/g' Dockerfile.stage && sed -i -e 's|REPLACE_SENTRY_URL|${{ env.sentry_url }}|g' Dockerfile.stage && docker build -f Dockerfile.stage . -t ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + + deploy-production-backend: + needs: [build] + environment: production + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - name: Deploy Backend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.BACKEND_GCP_SERVICE }} + image: ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Deploy Frontend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.FRONTEND_GCP_SERVICE }} + image: ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + diff --git a/.github/workflows/deploy-to-cloudrun-staging.yml b/.github/workflows/deploy-to-cloudrun-staging.yml new file mode 100644 index 000000000..6a64be02f --- /dev/null +++ b/.github/workflows/deploy-to-cloudrun-staging.yml @@ -0,0 +1,97 @@ +name: "Deploy to Staging Cloud Run" + +on: + push: + branches: + - staging + - jp/staging-cloud-run-prep + +env: + + # DB VARS + db_username: ${{ secrets.DB_USERNAME }} + db_password: ${{ secrets.DB_PASSWORD }} + db_hostname: ${{ secrets.DB_HOSTNAME }} + db_port: ${{ secrets.DB_PORT }} + db_name: ${{ secrets.DB_NAME }} + backend_api: ${{ secrets.BACKEND_API }} + tx_options: ${{ secrets.TX_OPTIONS }} + hotjar_site_id: ${{ secrets.HOTJAR_STAGE_ID }} + sentry_url: ${{ secrets.SENTRY_STAGE_URL }} + + # GCP VARS + BACKEND_DOCKER_IMAGE_URL: ${{ vars.BACKEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + FRONTEND_DOCKER_IMAGE_URL: ${{ vars.FRONTEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + GAR_LOCATION: ${{ vars.GCP_GAR_LOCATION }} + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }} + WORKLOAD_IDENTITY_PROVIDER: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ env.SERVICE_ACCOUNT }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 + with: + project_id: ${{ env.PROJECT_ID }} + + - name: Build & Push Backend + run: |- + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + docker build -t ${{ env.BACKEND_DOCKER_IMAGE_URL }} --file backend/Dockerfile ./backend + docker push ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Build & Push Frontend + env: + BACKEND: ${{ env.backend_api }} + run: | + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + cd frontend && docker build -f Dockerfile.stage . -t ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + docker push ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + + deploy-staging-backend: + needs: [build] + environment: staging + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - name: Deploy Backend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.BACKEND_GCP_SERVICE }} + image: ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Deploy Frontend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.FRONTEND_GCP_SERVICE }} + image: ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + diff --git a/backend/main/main.go b/backend/main/main.go index 82ef8a3cb..96ae11886 100644 --- a/backend/main/main.go +++ b/backend/main/main.go @@ -4,8 +4,9 @@ import ( "log" "os" - app "github.com/DapperCollectives/CAST/backend/main/server" "github.com/joho/godotenv" + + app "github.com/DapperCollectives/CAST/backend/main/server" ) func main() { diff --git a/backend/main/server/app.go b/backend/main/server/app.go index 43f377180..f9c55fb21 100644 --- a/backend/main/server/app.go +++ b/backend/main/server/app.go @@ -1,42 +1,43 @@ package server import ( - // "errors" - "context" "encoding/json" "flag" "fmt" "io/ioutil" "net/http" + "net/url" "os" "strings" - "github.com/DapperCollectives/CAST/backend/main/middleware" - "github.com/DapperCollectives/CAST/backend/main/models" - "github.com/DapperCollectives/CAST/backend/main/shared" - "github.com/DapperCollectives/CAST/backend/main/strategies" "github.com/axiomzen/envconfig" "github.com/gorilla/mux" "github.com/jackc/pgx/v4/pgxpool" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" + + "github.com/DapperCollectives/CAST/backend/main/middleware" + "github.com/DapperCollectives/CAST/backend/main/models" + "github.com/DapperCollectives/CAST/backend/main/shared" + "github.com/DapperCollectives/CAST/backend/main/strategies" ) -type Database shared.Database -type IpfsClient shared.IpfsClient -type Allowlist shared.Allowlist -type Vote models.Vote -type Proposal models.Proposal -type Community models.Community -type Balance models.Balance -type List models.List -type ListRequest models.ListPayload -type ListUpdatePayload models.ListUpdatePayload -type CommunityUser models.CommunityUser -type CommunityUserPayload models.CommunityUserPayload -type UserCommunity models.UserCommunity +type ( + Database shared.Database + IpfsClient shared.IpfsClient + Allowlist shared.Allowlist + Vote models.Vote + Proposal models.Proposal + Community models.Community + Balance models.Balance + List models.List + ListRequest models.ListPayload + ListUpdatePayload models.ListUpdatePayload + CommunityUser models.CommunityUser + CommunityUserPayload models.CommunityUserPayload + UserCommunity models.UserCommunity +) type TxOptionsAddresses []string @@ -58,6 +59,20 @@ type Strategy interface { GetVotes(votes []*models.VoteWithBalance, proposal *models.Proposal) ([]*models.VoteWithBalance, error) GetVoteWeightForBalance(vote *models.VoteWithBalance, proposal *models.Proposal) (float64, error) InitStrategy(f *shared.FlowAdapter, db *shared.Database) + TallyVotes( + votes []*models.VoteWithBalance, + proposal *models.Proposal, + ) (models.ProposalResults, error) + GetVotes( + votes []*models.VoteWithBalance, + proposal *models.Proposal, + ) ([]*models.VoteWithBalance, error) + GetVoteWeightForBalance( + vote *models.VoteWithBalance, + proposal *models.Proposal, + ) (float64, error) + InitStrategy(f *shared.FlowAdapter, db *shared.Database, sc *shared.DpsAdapter) +>>>>>>> Stashed changes FetchBalance(b *models.Balance, p *models.Proposal) (*models.Balance, error) RequiresSnapshot() bool } @@ -164,7 +179,7 @@ func (a *App) Initialize() { } // Create Map for Flow Adaptor to look up when voting - var customScriptsMap = make(map[string]shared.CustomScript) + customScriptsMap := make(map[string]shared.CustomScript) for _, script := range customScripts { customScriptsMap[script.Key] = script } @@ -202,8 +217,7 @@ func (a *App) ConnectDB(username, password, host, port, dbname string) { database.Context = context.Background() database.Name = dbname - connectionString := - fmt.Sprintf("postgres://%s:%s@%s:%s/%s", username, password, host, port, dbname) + connectionString := ToUnixURL(false, username, password, dbname, host) pconf, confErr := pgxpool.ParseConfig(connectionString) if confErr != nil { @@ -226,3 +240,30 @@ func (a *App) ConnectDB(username, password, host, port, dbname string) { log.Info().Msgf("Successfully created Postgres conn pool") } } + +func ToUnixURL(ssl bool, username, password, db, host string) string { + urlStr := "postgresql://" + + if len(username) == 0 { + username = "postgres" + } + urlStr += username + + if len(password) > 0 { + urlStr = urlStr + ":" + url.PathEscape(password) + } + urlStr += "@" + + urlStr += "/" + url.PathEscape(db) + + // Append query parameters + urlStr += "?" + "host=" + host + + mode := "" + if !ssl { + mode = "&sslmode=disable" + } + + urlStr += mode + return urlStr +} diff --git a/frontend/Dockerfile b/frontend/Dockerfile index ca9f28d14..6677b9c1d 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -19,8 +19,6 @@ ENV REACT_APP_BACK_END_SERVER_API "https://api.cast.fyi" ENV REACT_APP_IPFS_GATEWAY "https://dappercollectives.mypinata.cloud/ipfs" ENV REACT_APP_FLOW_ENV "mainnet" ENV REACT_APP_TX_OPTIONS_ADDRS "0xe0de919ed4ebeee4,0x7f81b82fa0e59b17" -ENV REACT_APP_HOTJAR_SITE_ID=REPLACE_HOTJAR_SITE_ID -ENV REACT_APP_SENTRY_URL=REPLACE_SENTRY_URL RUN yarn build diff --git a/frontend/Dockerfile.stage b/frontend/Dockerfile.stage index 6cbdb910a..9764ca7bc 100644 --- a/frontend/Dockerfile.stage +++ b/frontend/Dockerfile.stage @@ -13,14 +13,11 @@ RUN yarn install #ENVS FOR STAGING FRONTEND ENV NODE_ENV "production" ENV REACT_APP_APP_ENV "production" -ENV REACT_APP_FRONTEND_URL "fe.staging.cast.dapperlabs.com" -ENV REACT_APP_BACK_END_SERVER_API "https://app.staging.cast.dapperlabs.com" +ENV REACT_APP_FRONTEND_URL "cast.staging.fyi.com" +ENV REACT_APP_BACK_END_SERVER_API "https://cast-fronted-staging-oa3wu6tmxq-uc.a.run.app" ENV REACT_APP_IPFS_GATEWAY "https://dappercollectives.mypinata.cloud/ipfs" ENV REACT_APP_FLOW_ENV "mainnet" ENV REACT_APP_TX_OPTIONS_ADDRS "0xe0de919ed4ebeee4,0x7f81b82fa0e59b17" -ENV REACT_APP_HOTJAR_SITE_ID=REPLACE_HOTJAR_SITE_ID -ENV REACT_APP_SENTRY_URL=REPLACE_SENTRY_URL - RUN yarn build diff --git a/frontend/packages/client/.env.example b/frontend/packages/client/.env.example index 3335d86a1..511070c00 100644 --- a/frontend/packages/client/.env.example +++ b/frontend/packages/client/.env.example @@ -4,5 +4,3 @@ REACT_APP_FLOW_ENV=emulator REACT_APP_BACK_END_SERVER_API=http://localhost:5001 REACT_APP_IPFS_GATEWAY=https://dappercollectives.mypinata.cloud/ipfs REACT_APP_TX_OPTIONS_ADDRS="0xc590d541b72f0ac1,0x72d401812f579e3e" -REACT_APP_HOTJAR_SITE_ID=0 -REACT_APP_SENTRY_URL=REPLACE_SENTRY_URL \ No newline at end of file diff --git a/frontend/packages/client/package.json b/frontend/packages/client/package.json index c637649d2..ca5272651 100644 --- a/frontend/packages/client/package.json +++ b/frontend/packages/client/package.json @@ -6,8 +6,7 @@ "@craco/craco": "^6.4.5", "@creativebulma/bulma-tooltip": "^1.2.0", "@hookform/resolvers": "^2.9.7", - "@hotjar/browser": "^1.0.6", - "@onflow/fcl": "^1.2.0", + "@onflow/fcl": "^1.3.1", "@onflow/sdk": "^1.1.1", "@onflow/six-transfer-tokens": "0.0.8", "@sentry/react": "^7.14.0", diff --git a/frontend/packages/client/src/App.js b/frontend/packages/client/src/App.js index 7cd3680fe..c98bc8fc4 100644 --- a/frontend/packages/client/src/App.js +++ b/frontend/packages/client/src/App.js @@ -6,16 +6,13 @@ import NotificationModalProvider from 'contexts/NotificationModal'; import { Web3Provider } from 'contexts/Web3'; import { ErrorHandler } from 'components'; import { IS_PRODUCTION } from 'const'; -import Hotjar from '@hotjar/browser'; +import { ChakraProvider } from '@chakra-ui/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import AppPages from 'pages'; import Error from 'pages/Error'; import './App.sass'; -const hotjarVersion = 6; -Hotjar.init(process.env.REACT_APP_HOTJAR_SITE_ID, hotjarVersion); - // create react-query client const queryClient = new QueryClient(); diff --git a/frontend/packages/client/src/index.js b/frontend/packages/client/src/index.js index e9a2e36a2..d53362aef 100644 --- a/frontend/packages/client/src/index.js +++ b/frontend/packages/client/src/index.js @@ -1,13 +1,9 @@ import ReactDOM from 'react-dom'; -import * as Sentry from '@sentry/react'; -import { BrowserTracing } from '@sentry/tracing'; import App from './App'; import './index.css'; -Sentry.init({ - dsn: process.env.REACT_APP_SENTRY_URL, - integrations: [new BrowserTracing()], - tracesSampleRate: 0.3, -}); +// hack for buffer error on react-scripts version > 5 +// eslint-disable-next-line +window.Buffer = window.Buffer || require('buffer').Buffer; ReactDOM.render(, document.getElementById('root')); diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 000000000..80744eda7 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "darwin-arm64-120", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..fb57ccd13 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +