├── .github └── workflows │ ├── cicd_api.yml │ ├── cicd_front.yml │ ├── cicd_lambda_metrics_cleaner.yml │ ├── cicd_lambda_snapshot.yml │ ├── cicd_proxy.yml │ └── infra.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── api-docs └── api │ ├── docs.go │ ├── swagger.json │ └── swagger.yaml ├── build └── package │ ├── api.Dockerfile │ └── proxy.Dockerfile ├── cmd ├── api │ └── main.go ├── metrics │ └── main.go ├── proxy │ └── main.go └── snapshot │ └── main.go ├── config ├── api_config.go └── proxy_config.go ├── data ├── api │ ├── conf │ │ ├── dev.toml │ │ └── local.toml │ └── migrations │ │ ├── 1_projects_table.down.sql │ │ ├── 2_projects_table.up.sql │ │ ├── 3_project_index.down.sql │ │ ├── 4_project_index.up.sql │ │ ├── 5_metrics_table.down.sql │ │ ├── 6_metrics_table.up.sql │ │ ├── 7_metrics_index.down.sql │ │ ├── 8_metrics_index.up.sql │ │ ├── 9_project_network.down.sql │ │ └── 9_project_network.up.sql └── proxy │ └── conf │ ├── local.toml │ └── prod.toml ├── docker-compose.yml ├── go.mod ├── go.sum ├── infra ├── common.tfvars ├── img │ └── architecture.png ├── terraform │ ├── iam │ │ ├── ec2-assume-role │ │ │ ├── config.tf │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── provider.tf │ │ ├── ecs-task-assume-role │ │ │ ├── config.tf │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── provider.tf │ │ ├── lambda-assume-role │ │ │ ├── config.tf │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── provider.tf │ │ ├── personal-access-role │ │ │ ├── config.tf │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── provider.tf │ │ └── vpc-flowlog-assume-role │ │ │ ├── config.tf │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── provider.tf │ ├── modules │ │ ├── cloudfront │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── ecs_cluster │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── lambda │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── network │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── rds_cluster │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── variables.tf │ │ ├── s3_shared_bucket │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── service_api │ │ │ ├── main.tf │ │ │ ├── templates │ │ │ │ └── proxy_task_definition.json.tpl │ │ │ └── variables.tf │ │ ├── service_proxy │ │ │ ├── main.tf │ │ │ ├── templates │ │ │ │ └── proxy_task_definition.json.tpl │ │ │ └── variables.tf │ │ └── tezos_node │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ ├── templates │ │ │ ├── user_data_archive.tpl │ │ │ └── user_data_rolling.tpl │ │ │ └── variables.tf │ └── remote_configuration │ │ ├── config.tf │ │ ├── main.tf │ │ ├── provider.tf │ │ ├── terraform.tfstate │ │ └── terraform.tfstate.backup └── terragrunt │ ├── 00_ecs_cluster │ └── terragrunt.hcl │ ├── 00_lambda_metrics_bucket │ └── terragrunt.hcl │ ├── 00_lambda_snapshot_bucket │ └── terragrunt.hcl │ ├── 00_network │ └── terragrunt.hcl │ ├── 01_frontend │ └── terragrunt.hcl │ ├── 01_rds_cluster │ └── terragrunt.hcl │ ├── 01_snapshot_bucket │ └── terragrunt.hcl │ ├── 01_tezos_carthagenet_archive_node │ └── terragrunt.hcl │ ├── 01_tezos_carthagenet_rolling_node │ └── terragrunt.hcl │ ├── 01_tezos_mainnet_archive_node │ └── terragrunt.hcl │ ├── 01_tezos_mainnet_rolling_node │ └── terragrunt.hcl │ ├── 02_lambda_metrics │ └── terragrunt.hcl │ ├── 02_lambda_snapshot_carthagenet │ └── terragrunt.hcl │ ├── 02_lambda_snapshot_mainnet │ └── terragrunt.hcl │ ├── 02_service_api │ └── terragrunt.hcl │ ├── 02_service_proxy_carthagenet │ └── terragrunt.hcl │ ├── 02_service_proxy_mainnet │ └── terragrunt.hcl │ └── terragrunt.hcl ├── internal ├── api │ ├── domain │ │ └── model │ │ │ └── health.go │ ├── infrastructure │ │ ├── database │ │ │ └── connection.go │ │ └── rest │ │ │ ├── helpers_test.go │ │ │ ├── inputs │ │ │ └── new_project.go │ │ │ ├── outputs │ │ │ ├── metrics_output.go │ │ │ ├── metrics_output_test.go │ │ │ └── project_output.go │ │ │ ├── rest_controller.go │ │ │ └── rest_controller_test.go │ └── usecases │ │ ├── health_usecase.go │ │ ├── helpers_test.go │ │ ├── project_usecase.go │ │ └── project_usecase_test.go └── proxy │ ├── domain │ ├── model │ │ └── node_type.go │ └── repository │ │ ├── blockchain_repository.go │ │ └── metrics_repository.go │ ├── infrastructure │ ├── cache │ │ ├── cache_blockchain_repository.go │ │ ├── cache_metrics_repository.go │ │ └── cache_metrics_repository_test.go │ ├── database │ │ └── connection.go │ ├── http │ │ ├── helpers_test.go │ │ ├── http_controller.go │ │ └── http_controller_test.go │ └── proxy │ │ └── proxy_blockchain_repository.go │ └── usecases │ ├── helpers_test.go │ ├── proxy_usecase.go │ └── proxy_usecase_test.go ├── perf ├── README.md ├── img │ ├── locust-graph.png │ └── locust-server.png ├── locust_load_on_head.py └── requirements.txt ├── pkg ├── domain │ ├── errors │ │ └── errors.go │ ├── model │ │ ├── metrics.go │ │ ├── project.go │ │ ├── request.go │ │ ├── requests_by_day_metrics.go │ │ └── rpc_usage_metrics.go │ └── repository │ │ ├── metric_repository.go │ │ └── project_repository.go └── infrastructure │ ├── cache │ ├── lru_project_repository.go │ └── lru_project_repository_test.go │ └── database │ ├── helpers_test.go │ ├── inputs │ └── metrics_input.go │ ├── postgres_metrics_repository.go │ ├── postgres_metrics_repository_test.go │ ├── postgres_project_repository.go │ └── postgres_project_repository_test.go ├── test └── proxy │ └── conf │ └── test.toml ├── vendor └── golint └── web ├── .env ├── .eslintignore ├── .eslintrc.json ├── .prettierignore ├── .prettierrc ├── package.json ├── public ├── Chapter.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── background.svg ├── bg.svg ├── browserconfig.xml ├── docs │ ├── README.md │ ├── content.md │ └── menu.md ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── fonts │ ├── ProximaNova-Bold.woff │ ├── ProximaNova-Bold.woff2 │ ├── ProximaNova-Light.woff │ ├── ProximaNova-Light.woff2 │ ├── ProximaNova-Regular.woff │ ├── ProximaNova-Regular.woff2 │ ├── ProximaNova-Semibold.woff │ ├── ProximaNova-Semibold.woff2 │ ├── ProximaNova-Thin.woff │ └── ProximaNova-Thin.woff2 ├── icons │ ├── annimated-check.svg │ ├── arrow-black.svg │ ├── arrow-down.svg │ ├── arrow-up.svg │ ├── arrow-white.svg │ ├── arrow.svg │ ├── books.svg │ ├── clock.svg │ ├── close.svg │ ├── copy.svg │ ├── cupport.svg │ ├── dialog.svg │ ├── email.svg │ ├── error.svg │ ├── eye.svg │ ├── info.svg │ ├── input-error.svg │ ├── input-success.svg │ ├── island.svg │ ├── login.svg │ ├── love.svg │ ├── password.svg │ ├── plus-card.svg │ ├── search.svg │ ├── sign-up.svg │ ├── sprites.svg │ ├── success.svg │ ├── support.svg │ ├── unplugged.svg │ ├── user.svg │ └── warning.svg ├── images │ ├── accenture.svg │ ├── architecture.png │ ├── arg1.svg │ ├── arg2.svg │ ├── arg3.svg │ ├── brought.svg │ ├── grid-bg.png │ ├── grid-bg.svg │ ├── logo.svg │ ├── metrics.png │ ├── mini-torus.png │ ├── nomadic.svg │ ├── particle.svg │ ├── post1.jpeg │ ├── torus-bg.svg │ ├── torus-cables.svg │ ├── torus-fg.svg │ ├── torus-logo.svg │ ├── torus.svg │ ├── tq.svg │ └── user1.jpg ├── index.html ├── link_logo.svg ├── logo.svg ├── logo192.png ├── manifest.json ├── mstile-150x150.png ├── robots.txt ├── safari-pinned-tab.svg └── site.webmanifest ├── src ├── App │ ├── App.components │ │ ├── Button │ │ │ ├── Button.constants.tsx │ │ │ ├── Button.controller.tsx │ │ │ ├── Button.style.tsx │ │ │ └── Button.view.tsx │ │ ├── Drawer │ │ │ ├── Drawer.actions.tsx │ │ │ ├── Drawer.controller.tsx │ │ │ ├── Drawer.reducers.tsx │ │ │ ├── Drawer.style.tsx │ │ │ ├── Drawer.styles │ │ │ │ ├── DrawerBackward.tsx │ │ │ │ └── DrawerForward.tsx │ │ │ └── Drawer.view.tsx │ │ ├── Footer │ │ │ ├── Footer.controller.tsx │ │ │ ├── Footer.style.tsx │ │ │ └── Footer.view.tsx │ │ ├── Hamburger │ │ │ ├── Hamburger.controller.tsx │ │ │ ├── Hamburger.style.tsx │ │ │ └── Hamburger.view.tsx │ │ ├── Header │ │ │ ├── Header.controller.tsx │ │ │ ├── Header.style.tsx │ │ │ └── Header.view.tsx │ │ ├── Input │ │ │ ├── Input.controller.tsx │ │ │ ├── Input.style.tsx │ │ │ └── Input.view.tsx │ │ ├── LoggedInRoute │ │ │ └── LoggedInRoute.controller.tsx │ │ ├── LoggedOutRoute │ │ │ └── LoggedOutRoute.controller.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.actions.tsx │ │ │ ├── ProgressBar.constants.tsx │ │ │ ├── ProgressBar.controller.tsx │ │ │ ├── ProgressBar.reducers.tsx │ │ │ ├── ProgressBar.style.tsx │ │ │ └── ProgressBar.view.tsx │ │ └── Toaster │ │ │ ├── Toaster.actions.tsx │ │ │ ├── Toaster.constants.tsx │ │ │ ├── Toaster.controller.tsx │ │ │ ├── Toaster.reducers.tsx │ │ │ ├── Toaster.style.tsx │ │ │ └── Toaster.view.tsx │ ├── App.config │ │ └── configureStore.ts │ ├── App.controller.tsx │ ├── App.style.tsx │ └── App.view.tsx ├── entities │ └── ProjectWithMetrics.ts ├── index.tsx ├── pages │ ├── Dashboard │ │ ├── Dashboard.components │ │ │ ├── Confetis │ │ │ │ └── Confetis.controller.tsx │ │ │ ├── LastRequests │ │ │ │ ├── LastRequests.style.tsx │ │ │ │ └── LastRequests.view.tsx │ │ │ ├── ModalContainer │ │ │ │ └── ModalContainer.controller.tsx │ │ │ ├── ModalProjectCreated │ │ │ │ ├── ModalProjectCreated.style.tsx │ │ │ │ └── ModalProjectCreated.view.tsx │ │ │ ├── ProjectName │ │ │ │ ├── ProjectName.style.tsx │ │ │ │ └── ProjectName.view.tsx │ │ │ ├── ProjectToken │ │ │ │ ├── ProjectToken.style.tsx │ │ │ │ └── ProjectToken.view.tsx │ │ │ ├── RPCUsagePie │ │ │ │ ├── RPCUsagePie.style.tsx │ │ │ │ └── RPCUsagePie.view.tsx │ │ │ ├── RequestsByDayLine │ │ │ │ ├── RequestsByDayLine.style.tsx │ │ │ │ └── RequestsByDayLine.view.tsx │ │ │ └── TokenCopy │ │ │ │ ├── TokenCopy.controller.tsx │ │ │ │ └── TokenCopy.view.tsx │ │ ├── Dashboard.controller.tsx │ │ ├── Dashboard.style.tsx │ │ └── Dashboard.view.tsx │ ├── Documentation │ │ ├── Documentation.controller.tsx │ │ ├── Documentation.style.tsx │ │ └── Documentation.view.tsx │ ├── Home │ │ ├── Home.style.tsx │ │ └── Home.view.tsx │ ├── NewProject │ │ ├── NewProject.components │ │ │ ├── FormInputField │ │ │ │ ├── FormInputField.controller.tsx │ │ │ │ ├── FormInputField.style.tsx │ │ │ │ └── FormInputField.view.tsx │ │ │ └── FormInputNetworkSelector │ │ │ │ ├── Select.controller.tsx │ │ │ │ ├── Select.style.tsx │ │ │ │ └── Select.view.tsx │ │ ├── NewProject.controller.tsx │ │ ├── NewProject.style.tsx │ │ ├── NewProject.validator.tsx │ │ └── NewProject.view.tsx │ ├── NotFound │ │ ├── NotFound.style.tsx │ │ └── NotFound.view.tsx │ ├── ProjectNotFound │ │ ├── ProjectNotFound.style.tsx │ │ └── ProjectNotFound.view.tsx │ ├── SignInProject │ │ ├── SignInProject.components │ │ │ └── FormInputField │ │ │ │ ├── FormInputField.controller.tsx │ │ │ │ ├── FormInputField.style.tsx │ │ │ │ └── FormInputField.view.tsx │ │ ├── SignInProject.controller.tsx │ │ ├── SignInProject.style.tsx │ │ ├── SignInProject.validator.tsx │ │ └── SignInProject.view.tsx │ └── Status │ │ ├── Status.controller.tsx │ │ ├── Status.style.tsx │ │ └── Status.view.tsx ├── react-app-env.d.ts ├── reducers.ts ├── serviceWorker.ts ├── styles │ ├── animations.ts │ ├── colors.ts │ ├── components.ts │ ├── global.ts │ ├── index.scss │ └── index.ts └── validators │ ├── index.ts │ └── projectNameValidator.ts ├── tsconfig.json └── yarn.lock /.github/workflows/cicd_front.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD Front 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'web/**' 7 | - 'Makefile' 8 | - '.github/workflows/cicd_front.yml' 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-18.04 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Install dependancies 18 | run: | 19 | make deps 20 | 21 | - name: Build frontend 22 | run: | 23 | make build-frontend 24 | env: 25 | CI: false 26 | 27 | - name: Upload artifact (only on master) 28 | if: github.ref == 'refs/heads/master' 29 | uses: actions/upload-artifact@v1 30 | with: 31 | name: build 32 | path: web/build 33 | 34 | deploy: 35 | name: Deploy 36 | runs-on: ubuntu-18.04 37 | needs: [build] 38 | if: github.ref == 'refs/heads/master' 39 | steps: 40 | - name: Configure AWS cli 41 | uses: aws-actions/configure-aws-credentials@v1 42 | with: 43 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 44 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 45 | aws-region: eu-west-1 46 | 47 | - name: Download artifact 48 | uses: actions/download-artifact@v1 49 | with: 50 | name: build 51 | 52 | - name: Deploy frontend 53 | run: | 54 | aws s3 sync ./build s3://tz-front 55 | -------------------------------------------------------------------------------- /.github/workflows/cicd_lambda_metrics_cleaner.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD Lambda Metric Cleaner 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'cmd/metrics/**' 7 | - 'Makefile' 8 | - '.github/workflows/cicd_lambda_metrics_cleaner.yml' 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-18.04 14 | steps: 15 | - name: Set up Go 1.13 16 | uses: actions/setup-go@v1 17 | with: 18 | go-version: 1.13 19 | id: go 20 | 21 | - uses: actions/checkout@v2 22 | 23 | - name: Get dependencies 24 | run: | 25 | go get -v -d ./... 26 | if [ -f Gopkg.toml ]; then 27 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 28 | dep ensure 29 | fi 30 | 31 | - name: Build the metrics cleaner lamdba 32 | run: make build-metrics-cleaner-lambda 33 | 34 | - name: Zip the metrics cleaner code 35 | run: | 36 | cp bin/metrics bin/main 37 | zip -j bin/metrics.zip bin/main 38 | 39 | - name: Upload artifact (only on master) 40 | if: github.ref == 'refs/heads/master' 41 | uses: actions/upload-artifact@v1 42 | with: 43 | name: metrics 44 | path: bin/metrics.zip 45 | 46 | deploy: 47 | name: Deploy 48 | runs-on: ubuntu-18.04 49 | needs: [build] 50 | if: github.ref == 'refs/heads/master' 51 | steps: 52 | - name: Configure AWS cli 53 | uses: aws-actions/configure-aws-credentials@v1 54 | with: 55 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 56 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 57 | aws-region: eu-west-1 58 | 59 | - name: Download artifact 60 | uses: actions/download-artifact@v1 61 | with: 62 | name: metrics 63 | 64 | - name: Deploy and update lambda 65 | run: | 66 | aws s3 cp metrics/metrics.zip s3://tzlink-metric-cleaner-lambda/v1.0.0/metrics.zip 67 | aws lambda update-function-code --function-name metrics --s3-bucket tzlink-metric-cleaner-lambda --s3-key v1.0.0/metrics.zip 68 | -------------------------------------------------------------------------------- /.github/workflows/infra.yml: -------------------------------------------------------------------------------- 1 | name: Infra 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.tf' 7 | - '**.hcl' 8 | - '.github/workflows/infra.yml' 9 | 10 | jobs: 11 | 12 | terraform-lint: 13 | name: lint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: 'Terraform Format' 18 | uses: hashicorp/terraform-github-actions@master 19 | with: 20 | tf_actions_version: 0.12.24 21 | tf_actions_subcommand: 'fmt' 22 | tf_actions_working_dir: './infra/terraform' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | 4 | # Project 5 | bin/* 6 | **/node_modules 7 | **/dist 8 | web/build 9 | 10 | # IDE 11 | .idea/ 12 | .vscode/ 13 | 14 | # Terragrunt 15 | **/.terragrunt-cache 16 | 17 | # Terraform 18 | **/.terraform 19 | **/*.tfstate 20 | **/*.tfstate.backup 21 | -------------------------------------------------------------------------------- /api-docs/api/swagger.yaml: -------------------------------------------------------------------------------- 1 | basePath: /api/v1 2 | definitions: 3 | inputs.NewProject: 4 | properties: 5 | title: 6 | type: string 7 | type: object 8 | model.Health: 9 | properties: 10 | connectedToDb: 11 | type: boolean 12 | type: object 13 | outputs.MetricsOutput: 14 | properties: 15 | requestsCount: 16 | type: integer 17 | type: object 18 | outputs.ProjectOutputWithMetrics: 19 | properties: 20 | metrics: 21 | $ref: '#/definitions/outputs.MetricsOutput' 22 | type: object 23 | title: 24 | type: string 25 | uuid: 26 | type: string 27 | type: object 28 | info: 29 | contact: 30 | email: email@ded.fr 31 | name: API Support 32 | description: API to manage projects 33 | license: {} 34 | title: Tezos Link API 35 | version: v1 36 | paths: 37 | /health: 38 | get: 39 | responses: 40 | "200": 41 | description: OK 42 | schema: 43 | $ref: '#/definitions/model.Health' 44 | summary: get application health 45 | /projects: 46 | post: 47 | parameters: 48 | - description: New Project 49 | in: body 50 | name: new-project 51 | required: true 52 | schema: 53 | $ref: '#/definitions/inputs.NewProject' 54 | type: object 55 | produces: 56 | - application/json 57 | responses: 58 | "201": {} 59 | "400": {} 60 | summary: Create a Project 61 | /projects/{uuid}: 62 | get: 63 | parameters: 64 | - description: Project UUID 65 | in: path 66 | name: uuid 67 | required: true 68 | type: string 69 | produces: 70 | - application/json 71 | responses: 72 | "200": 73 | description: OK 74 | schema: 75 | $ref: '#/definitions/outputs.ProjectOutputWithMetrics' 76 | summary: Get a Project with the associated metrics 77 | swagger: "2.0" 78 | -------------------------------------------------------------------------------- /build/package/api.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | ENV ENV local 4 | ENV DATABASE_URL postgres:5432 5 | ENV DATABASE_USERNAME user 6 | ENV DATABASE_PASSWORD pass 7 | ENV DATABASE_TABLE tezoslink 8 | ENV DATABASE_ADDITIONAL_PARAMETER sslmode=disable 9 | ENV SERVER_HOST localhost 10 | ENV SERVER_PORT 8000 11 | 12 | RUN apk --no-cache add ca-certificates 13 | 14 | RUN adduser -D api 15 | USER api 16 | 17 | WORKDIR /home/api 18 | 19 | COPY ./bin/api . 20 | COPY ./data/api ./data 21 | 22 | EXPOSE $SERVER_PORT 23 | 24 | CMD ["sh", "-c", "./api --conf ./data/conf/$ENV.toml"] 25 | -------------------------------------------------------------------------------- /build/package/proxy.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | ENV ENV local 4 | ENV DATABASE_URL postgres:5432 5 | ENV DATABASE_USERNAME user 6 | ENV DATABASE_PASSWORD pass 7 | ENV DATABASE_TABLE tezoslink 8 | ENV DATABASE_ADDITIONAL_PARAMETER sslmode=disable 9 | ENV ARCHIVE_NODES_URL node 10 | ENV TEZOS_ARCHIVE_PORT 1090 11 | ENV ROLLING_NODES_URL node-rolling 12 | ENV TEZOS_ROLLING_PORT 1090 13 | ENV SERVER_PORT 8001 14 | ENV TEZOS_NETWORK MAINNET 15 | 16 | RUN apk --no-cache add ca-certificates 17 | 18 | RUN adduser -D proxy 19 | USER proxy 20 | 21 | WORKDIR /home/proxy 22 | 23 | COPY ./bin/proxy . 24 | COPY ./data/proxy ./data 25 | 26 | EXPOSE $API_PORT 27 | 28 | CMD ["sh", "-c", "./proxy --conf ./data/conf/$ENV.toml"] 29 | -------------------------------------------------------------------------------- /cmd/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/go-chi/chi" 6 | "github.com/golang-migrate/migrate/v4" 7 | _ "github.com/golang-migrate/migrate/v4/database/postgres" 8 | _ "github.com/golang-migrate/migrate/v4/source/file" 9 | _ "github.com/lib/pq" 10 | "github.com/octo-technology/tezos-link/backend/config" 11 | "github.com/octo-technology/tezos-link/backend/internal/api/infrastructure/database" 12 | "github.com/octo-technology/tezos-link/backend/internal/api/infrastructure/rest" 13 | "github.com/octo-technology/tezos-link/backend/internal/api/usecases" 14 | pkgdatabase "github.com/octo-technology/tezos-link/backend/pkg/infrastructure/database" 15 | "log" 16 | "strings" 17 | ) 18 | 19 | var configPath = flag.String("conf", "", "Path to TOML config") 20 | 21 | // Always run 22 | func init() { 23 | flag.Parse() 24 | 25 | if *configPath == "" { 26 | log.Fatal("Program argument --conf is required") 27 | } else { 28 | _, err := config.ParseAPIConf(*configPath) 29 | if err != nil { 30 | log.Fatalf("Could not load config from %s. Reason: %s", *configPath, err) 31 | } 32 | } 33 | 34 | database.Configure() 35 | } 36 | 37 | func main() { 38 | router := chi.NewRouter() 39 | runMigrations() 40 | 41 | // Repositories 42 | projectRepo := pkgdatabase.NewPostgresProjectRepository(database.Connection) 43 | metricsRepo := pkgdatabase.NewPostgresMetricsRepository(database.Connection) 44 | 45 | // Use cases 46 | projectUsecase := usecases.NewProjectUsecase(projectRepo, metricsRepo) 47 | healthUsecase := usecases.NewHealthUsecase(projectRepo) 48 | 49 | // HTTP API 50 | restController := rest.NewRestController(router, projectUsecase, healthUsecase) 51 | restController.Initialize() 52 | restController.Run(config.APIConfig.Server.Port) 53 | } 54 | 55 | func runMigrations() { 56 | m, err := migrate.New( 57 | config.APIConfig.Migration.Path, 58 | config.APIConfig.Database.Url) 59 | if err != nil { 60 | log.Fatal("Could not apply db migration: ", err) 61 | } 62 | if err := m.Up(); err != nil { 63 | if !strings.Contains(err.Error(), "no change") { 64 | log.Fatal("Could not apply db migration: ", err) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /config/api_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/BurntSushi/toml" 7 | "github.com/sirupsen/logrus" 8 | "io/ioutil" 9 | "log" 10 | "strconv" 11 | ) 12 | 13 | type APIConf struct { 14 | Env string 15 | Debug bool 16 | Server struct { 17 | Hostname string 18 | Port int 19 | } 20 | Database struct { 21 | Url string 22 | } 23 | Migration struct { 24 | Path string 25 | } 26 | Jwt struct { 27 | SignKey string 28 | } 29 | Networks []string 30 | } 31 | 32 | var APIConfig APIConf 33 | 34 | func ParseAPIConf(cfg string) (*APIConf, error) { 35 | conf := APIConf{} 36 | 37 | if data, err := ioutil.ReadFile(cfg); err != nil { 38 | log.Fatalf("Could not read config file:%s because of %s.", cfg, err) 39 | } else { 40 | if _, err := toml.Decode(string(data), &conf); err != nil { 41 | return nil, errors.New("Could not read TOML config") 42 | } 43 | } 44 | 45 | dbUrl := getEnv("DATABASE_URL", "postgres:5432") 46 | dbUser := getEnv("DATABASE_USERNAME", "user") 47 | dbPass := getEnv("DATABASE_PASSWORD", "pass") 48 | dbTable := getEnv("DATABASE_TABLE", "tezoslink?sslmode=disable") 49 | dbParam := getEnv("DATABASE_ADDITIONAL_PARAMETER", "sslmode=disable") 50 | conf.Database.Url = fmt.Sprintf("postgres://%s:%s@%s/%s?%s", dbUser, dbPass, dbUrl, dbTable, dbParam) 51 | 52 | conf.Server.Hostname = getEnv("SERVER_HOST", "localhost") 53 | serverPort, err := strconv.Atoi(getEnv("SERVER_PORT", "8001")) 54 | if err != nil { 55 | logrus.Fatal(err) 56 | } 57 | conf.Server.Port = serverPort 58 | 59 | APIConfig = conf 60 | 61 | if conf.Debug { 62 | log.Println("Read config: ", fmt.Sprintf("%+v", conf)) 63 | } 64 | 65 | return &conf, nil 66 | } 67 | -------------------------------------------------------------------------------- /data/api/conf/dev.toml: -------------------------------------------------------------------------------- 1 | env = "dev" 2 | debug = true 3 | networks = ["MAINNET","CARTHAGENET"] 4 | 5 | [migration] 6 | path = "file://./data/migrations" 7 | 8 | [jwt] 9 | signkey = "dFcgNAqajhoQPqyOivx-n2VWt5KpSPkuPxLJKbnUPXYFcFONkuvFO_XWMg153otv" 10 | 11 | -------------------------------------------------------------------------------- /data/api/conf/local.toml: -------------------------------------------------------------------------------- 1 | env = "dev" 2 | debug = true 3 | networks = ["MAINNET","CARTHAGENET"] 4 | 5 | [migration] 6 | path = "file://./data/migrations" 7 | 8 | [jwt] 9 | signkey = "dFcgNAqajhoQPqyOivx-n2VWt5KpSPkuPxLJKbnUPXYFcFONkuvFO_XWMg153otv" 10 | 11 | -------------------------------------------------------------------------------- /data/api/migrations/1_projects_table.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS projects; 2 | -------------------------------------------------------------------------------- /data/api/migrations/2_projects_table.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS projects( 2 | id SERIAL PRIMARY KEY, 3 | title VARCHAR(1024) NOT NULL, 4 | uuid VARCHAR(1024) NOT NULL, 5 | creation_date TIMESTAMP WITH TIME ZONE NOT NULL); 6 | -------------------------------------------------------------------------------- /data/api/migrations/3_project_index.down.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS uuid_index; 2 | -------------------------------------------------------------------------------- /data/api/migrations/4_project_index.up.sql: -------------------------------------------------------------------------------- 1 | CREATE UNIQUE INDEX CONCURRENTLY uuid_index ON projects (uuid); 2 | -------------------------------------------------------------------------------- /data/api/migrations/5_metrics_table.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS metrics; 2 | -------------------------------------------------------------------------------- /data/api/migrations/6_metrics_table.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS metrics( 2 | id SERIAL PRIMARY KEY, 3 | path VARCHAR(1024) NOT NULL, 4 | uuid VARCHAR(1024) NOT NULL, 5 | remote_address VARCHAR(1024) NOT NULL, 6 | date_request TIMESTAMP WITH TIME ZONE NOT NULL); 7 | -------------------------------------------------------------------------------- /data/api/migrations/7_metrics_index.down.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS uuid_date_request_index; 2 | -------------------------------------------------------------------------------- /data/api/migrations/8_metrics_index.up.sql: -------------------------------------------------------------------------------- 1 | CREATE UNIQUE INDEX CONCURRENTLY uuid_date_request_index ON metrics (uuid, date_request); 2 | -------------------------------------------------------------------------------- /data/api/migrations/9_project_network.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE IF EXISTS projects DROP COLUMN IF EXISTS network; -------------------------------------------------------------------------------- /data/api/migrations/9_project_network.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE IF EXISTS projects ADD COLUMN network varchar(256); 2 | UPDATE projects SET network = 'MAINNET'; 3 | ALTER TABLE IF EXISTS projects ALTER COLUMN network SET NOT NULL; -------------------------------------------------------------------------------- /data/proxy/conf/local.toml: -------------------------------------------------------------------------------- 1 | debug = true 2 | networks = ["MAINNET","CARTHAGENET"] 3 | [tezos] 4 | 5 | 6 | [proxy] 7 | readTimeout = 1 8 | writeTimeout = 5 9 | idleTimeout = 120 10 | whitelistedMethods = [ 11 | "/chains/main/blocks(.*?)", 12 | "/mockserver/status" 13 | ] 14 | blockedMethods = [ 15 | "(.*?)context/contracts$", 16 | "/monitor(.*?)", 17 | "/network(.*?)", 18 | ] 19 | dontCache = [ 20 | "(.*?)/head/(.*?)", 21 | "/chains/main/blocks$", 22 | ] 23 | rateLimitPeriod = 100 24 | rateLimitCount = 1000000 25 | blockchainRequestsCacheMaxItems = 2000 26 | projectsCacheMaxItems = 1000 27 | cacheMaxMetricItems = 100 28 | routineDelaySeconds = 60 29 | whitelistedRolling = [ 30 | "(.*?)/head/(.*?)", 31 | "(.*?)/head", 32 | "(.*?)/injection/operation", 33 | ] 34 | -------------------------------------------------------------------------------- /data/proxy/conf/prod.toml: -------------------------------------------------------------------------------- 1 | debug = true 2 | networks = ["MAINNET","CARTHAGENET"] 3 | [tezos] 4 | 5 | 6 | [proxy] 7 | readTimeout = 5 8 | writeTimeout = 30 9 | idleTimeout = 120 10 | whitelistedMethods = [ 11 | "/chains/main/blocks(.*?)", 12 | ] 13 | blockedMethods = [ 14 | "(.*?)context/contracts$", 15 | "/monitor(.*?)", 16 | "/network(.*?)", 17 | ] 18 | dontCache = [ 19 | "(.*?)/head/(.*?)", 20 | "(.*?)/head", 21 | "/chains/main/blocks$", 22 | ] 23 | rateLimitPeriod = 100 24 | rateLimitCount = 1000000 25 | blockchainRequestsCacheMaxItems = 2000 26 | projectsCacheMaxItems = 2000 27 | cacheMaxMetricItems = 2000 28 | routineDelaySeconds = 5 29 | whitelistedRolling = [ 30 | "(.*?)/head/(.*?)", 31 | "(.*?)/head", 32 | "(.*?)/injection/operation", 33 | ] 34 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | postgres: 4 | image: postgres:9.6 5 | environment: 6 | POSTGRES_DB: tezoslink 7 | POSTGRES_USER: user 8 | POSTGRES_PASSWORD: pass 9 | ports: 10 | - "5432:5432" 11 | api: 12 | build: 13 | context: . 14 | dockerfile: build/package/api.Dockerfile 15 | ports: 16 | - "8000:8000" 17 | environment: 18 | ENV: local 19 | restart: on-failure 20 | depends_on: 21 | - postgres 22 | proxy: 23 | build: 24 | context: . 25 | dockerfile: build/package/proxy.Dockerfile 26 | ports: 27 | - "8001:8001" 28 | environment: 29 | ENV: local 30 | TEZOS_NETWORK: MAINNET 31 | restart: on-failure 32 | depends_on: 33 | - postgres 34 | proxy-carthagenet: 35 | build: 36 | context: . 37 | dockerfile: build/package/proxy.Dockerfile 38 | ports: 39 | - "8002:8002" 40 | environment: 41 | ENV: local 42 | TEZOS_NETWORK: CARTHAGENET 43 | SERVER_PORT: 8002 44 | restart: on-failure 45 | depends_on: 46 | - postgres 47 | node: 48 | image: mockserver/mockserver:mockserver-5.9.0 49 | environment: 50 | LOG_LEVEL: "DEBUG" 51 | SERVER_PORT: 1090 52 | ports: 53 | - "8765:1090" 54 | node-rolling: 55 | image: mockserver/mockserver:mockserver-5.9.0 56 | environment: 57 | LOG_LEVEL: "DEBUG" 58 | SERVER_PORT: 1090 59 | ports: 60 | - "8766:1090" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/octo-technology/tezos-link/backend 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/BurntSushi/toml v0.3.1 7 | github.com/Microsoft/go-winio v0.4.14 // indirect 8 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 9 | github.com/aws/aws-lambda-go v1.15.0 10 | github.com/aws/aws-sdk-go v1.17.7 11 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 12 | github.com/cenkalti/backoff v2.2.1+incompatible // indirect 13 | github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 // indirect 14 | github.com/docker/go-units v0.4.0 // indirect 15 | github.com/gamegos/jsend v0.0.0-20151011171802-f47e169f3d76 16 | github.com/go-chi/chi v4.0.3+incompatible 17 | github.com/go-chi/cors v1.0.0 18 | github.com/golang-migrate/migrate/v4 v4.8.0 19 | github.com/google/uuid v1.1.1 20 | github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect 21 | github.com/hashicorp/golang-lru v0.5.1 22 | github.com/lib/pq v1.3.0 23 | github.com/opencontainers/runc v0.1.1 // indirect 24 | github.com/ory/dockertest v3.3.5+incompatible 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/sirupsen/logrus v1.4.2 27 | github.com/stretchr/testify v1.4.0 28 | github.com/swaggo/http-swagger v0.0.0-20200103000832-0e9263c4b516 29 | github.com/swaggo/swag v1.6.3 30 | github.com/ulule/limiter v2.2.2+incompatible 31 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 32 | golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d // indirect 33 | google.golang.org/grpc v1.21.0 // indirect 34 | gopkg.in/yaml.v2 v2.2.4 // indirect 35 | ) 36 | -------------------------------------------------------------------------------- /infra/common.tfvars: -------------------------------------------------------------------------------- 1 | project_name = "tezos-link" 2 | vpc_cidr = "10.1.0.0/16" 3 | lambda_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDyEN/krNooLTAmC5xSc3ylkHt+ttAuOxsPW+vSV+B/Oi0xlMEY33wzgJRWyZz7FK5r8Mm/w/vWbYLeV+C5Ohqmr/RYkVxeqiAGsYHay/4xzg/f3YKeKWF8D6hCjyM2SR6fFtReQVvQpX/doW8Y+C2JtrwH5cEVaB0Di4gWfEz4tttUkIgGXCPPJk+lVBddOkcvdKS8Df/j5lZjsTPv7G4qmoUX4Zf4BTU0pIiQkuATGQ9YNa4N65au09Mwi1QmaTNR+HK7fpjO6sMQBTKJGSsvTz52LyQbOsFLPpJxhnXYbbIVRCDuLtdV6yPtDdLu6z38NR05snQFgUCFE7q9I8f loup.theron@AMAC02QD2NYG8WL" -------------------------------------------------------------------------------- /infra/img/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/infra/img/architecture.png -------------------------------------------------------------------------------- /infra/terraform/iam/ec2-assume-role/config.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | } -------------------------------------------------------------------------------- /infra/terraform/iam/ec2-assume-role/output.tf: -------------------------------------------------------------------------------- 1 | output "trusted_entity" { 2 | value = data.aws_iam_policy_document.tzlink_trusted_entity.json 3 | } 4 | 5 | output "ec2_instance_s3_snapshot_access" { 6 | value = data.aws_iam_policy_document.ec2_instance_s3_snapshot_access.json 7 | } 8 | 9 | output "ec2_instance_ssm_profile" { 10 | value = data.aws_iam_policy_document.ec2_instance_ssm_profile.json 11 | } -------------------------------------------------------------------------------- /infra/terraform/iam/ec2-assume-role/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-west-1" 4 | } -------------------------------------------------------------------------------- /infra/terraform/iam/ecs-task-assume-role/config.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | } -------------------------------------------------------------------------------- /infra/terraform/iam/ecs-task-assume-role/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "tzlink_ecs_task_trusted_entity" { 2 | statement { 3 | 4 | actions = ["sts:AssumeRole"] 5 | 6 | principals { 7 | type = "Service" 8 | identifiers = ["ecs-tasks.amazonaws.com"] 9 | } 10 | 11 | effect = "Allow" 12 | } 13 | } 14 | 15 | data "aws_iam_policy_document" "tzlink_ecs_task_ecr_and_awslogs" { 16 | statement { 17 | 18 | actions = [ 19 | "ecr:GetAuthorizationToken", 20 | "ecr:BatchCheckLayerAvailability", 21 | "ecr:GetDownloadUrlForLayer", 22 | "ecr:BatchGetImage", 23 | "logs:CreateLogStream", 24 | "logs:PutLogEvents" 25 | ] 26 | 27 | resources = [ 28 | "*" 29 | ] 30 | } 31 | 32 | statement { 33 | 34 | actions = [ 35 | "secretsmanager:GetSecretValue" 36 | ] 37 | 38 | resources = [ 39 | "arn:aws:secretsmanager:eu-west-1:*:secret:tzlink-database-password" 40 | ] 41 | } 42 | } -------------------------------------------------------------------------------- /infra/terraform/iam/ecs-task-assume-role/output.tf: -------------------------------------------------------------------------------- 1 | output "trusted_entity" { 2 | value = data.aws_iam_policy_document.tzlink_ecs_task_trusted_entity.json 3 | } 4 | 5 | output "ecs_task_access" { 6 | value = data.aws_iam_policy_document.tzlink_ecs_task_ecr_and_awslogs.json 7 | } 8 | -------------------------------------------------------------------------------- /infra/terraform/iam/ecs-task-assume-role/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-west-1" 4 | } -------------------------------------------------------------------------------- /infra/terraform/iam/lambda-assume-role/config.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | } -------------------------------------------------------------------------------- /infra/terraform/iam/lambda-assume-role/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "tzlink_lambda_trusted_entity" { 2 | statement { 3 | 4 | actions = ["sts:AssumeRole"] 5 | 6 | principals { 7 | type = "Service" 8 | identifiers = ["lambda.amazonaws.com"] 9 | } 10 | 11 | effect = "Allow" 12 | } 13 | } 14 | 15 | data "aws_iam_policy_document" "tzlink_global_lambda_access" { 16 | statement { 17 | 18 | actions = [ 19 | "logs:CreateLogGroup", 20 | "logs:CreateLogStream", 21 | "logs:PutLogEvents" 22 | ] 23 | 24 | resources = [ 25 | "*" 26 | ] 27 | } 28 | } 29 | 30 | data "aws_iam_policy_document" "tzlink_snapshot_lambda_access" { 31 | statement { 32 | 33 | actions = [ 34 | "s3:*" 35 | ] 36 | 37 | resources = [ 38 | "arn:aws:s3:::tzlink-blockchain-data-dev", 39 | "arn:aws:s3:::tzlink-blockchain-data-dev/*", 40 | "arn:aws:s3:::tzlink-snapshot-lambda-dev", 41 | "arn:aws:s3:::tzlink-snapshot-lambda-dev/*" 42 | ] 43 | } 44 | 45 | statement { 46 | actions = [ 47 | "ec2:Describe*" 48 | ] 49 | 50 | resources = [ 51 | "*" 52 | ] 53 | } 54 | } 55 | 56 | data "aws_iam_policy_document" "tzlink_metric_lambda_access" { 57 | statement { 58 | 59 | actions = [ 60 | "s3:*" 61 | ] 62 | 63 | resources = [ 64 | "arn:aws:s3:::tzlink-metric-lambda-dev", 65 | "arn:aws:s3:::tzlink-metric-lambda-dev/*" 66 | ] 67 | } 68 | 69 | statement { 70 | 71 | actions = [ 72 | "ec2:CreateNetworkInterface", 73 | "ec2:DescribeNetworkInterfaces", 74 | "ec2:DeleteNetworkInterface" 75 | ] 76 | 77 | resources = [ 78 | "*" 79 | ] 80 | } 81 | } 82 | 83 | data "aws_iam_policy_document" "tzlink_secretmanager_lambda_access" { 84 | statement { 85 | 86 | actions = [ 87 | "secretsmanager:GetSecretValue" 88 | ] 89 | 90 | resources = [ 91 | "arn:aws:secretsmanager:eu-west-1:*:secret:tzlink-database-password" 92 | ] 93 | } 94 | } -------------------------------------------------------------------------------- /infra/terraform/iam/lambda-assume-role/output.tf: -------------------------------------------------------------------------------- 1 | output "trusted_entity" { 2 | value = data.aws_iam_policy_document.tzlink_lambda_trusted_entity.json 3 | } 4 | 5 | output "tzlink_global_lambda_access" { 6 | value = data.aws_iam_policy_document.tzlink_global_lambda_access.json 7 | } 8 | 9 | output "tzlink_snapshot_lambda_access" { 10 | value = data.aws_iam_policy_document.tzlink_snapshot_lambda_access.json 11 | } 12 | 13 | output "tzlink_metric_lambda_access" { 14 | value = data.aws_iam_policy_document.tzlink_metric_lambda_access.json 15 | } 16 | 17 | output "tzlink_secretmanager_lambda_access" { 18 | value = data.aws_iam_policy_document.tzlink_secretmanager_lambda_access.json 19 | } -------------------------------------------------------------------------------- /infra/terraform/iam/lambda-assume-role/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-west-1" 4 | } -------------------------------------------------------------------------------- /infra/terraform/iam/personal-access-role/config.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | } -------------------------------------------------------------------------------- /infra/terraform/iam/personal-access-role/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "tzlink_personal_access" { 2 | statement { 3 | 4 | actions = [ 5 | "iam:Get*Role*", 6 | "iam:List*Role*", 7 | "iam:PassRole", 8 | ] 9 | 10 | resources = [ 11 | "arn:aws:iam::609827314188:role/tzlink_backup_access", 12 | "arn:aws:iam::609827314188:role/tzlink_ecs_tasks_access", 13 | "arn:aws:iam::609827314188:role/tzlink_lambda_access", 14 | ] 15 | } 16 | 17 | statement { 18 | 19 | actions = [ 20 | "iam:*InstanceProfile*", 21 | ] 22 | 23 | resources = [ 24 | "*", 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /infra/terraform/iam/personal-access-role/output.tf: -------------------------------------------------------------------------------- 1 | output "personal_access" { 2 | value = data.aws_iam_policy_document.tzlink_personal_access.json 3 | } -------------------------------------------------------------------------------- /infra/terraform/iam/personal-access-role/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-west-1" 4 | } -------------------------------------------------------------------------------- /infra/terraform/iam/vpc-flowlog-assume-role/config.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | } -------------------------------------------------------------------------------- /infra/terraform/iam/vpc-flowlog-assume-role/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "tzlink_trusted_entity" { 2 | statement { 3 | actions = ["sts:AssumeRole"] 4 | 5 | principals { 6 | type = "Service" 7 | identifiers = ["vpc-flow-logs.amazonaws.com"] 8 | } 9 | 10 | effect = "Allow" 11 | } 12 | } 13 | 14 | data "aws_iam_policy_document" "tzlink_vpc_flowlog" { 15 | statement { 16 | actions = [ 17 | "logs:CreateLogGroup", 18 | "logs:CreateLogStream", 19 | "logs:PutLogEvents", 20 | "logs:DescribeLogGroups", 21 | "logs:DescribeLogStreams" 22 | ] 23 | 24 | resources = [ 25 | "*" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /infra/terraform/iam/vpc-flowlog-assume-role/output.tf: -------------------------------------------------------------------------------- 1 | output "tzlink_vpc_flowlog" { 2 | value = data.aws_iam_policy_document.tzlink_vpc_flowlog.json 3 | } 4 | 5 | output "trusted_entity" { 6 | value = data.aws_iam_policy_document.tzlink_trusted_entity.json 7 | } -------------------------------------------------------------------------------- /infra/terraform/iam/vpc-flowlog-assume-role/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-west-1" 4 | } -------------------------------------------------------------------------------- /infra/terraform/modules/cloudfront/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | default = "eu-west-1" 4 | description = "The region where the module will be deployed." 5 | } 6 | 7 | variable "project_name" { 8 | type = string 9 | description = "The name of the project" 10 | } 11 | 12 | variable "certificate_arn" { 13 | type = string 14 | description = "The certificate associated to cloudwatch SSL" 15 | } -------------------------------------------------------------------------------- /infra/terraform/modules/ecs_cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | backend "s3" {} 4 | } 5 | 6 | resource "aws_ecs_cluster" "cluster" { 7 | name = "tzlink" 8 | 9 | setting { 10 | name = "containerInsights" 11 | value = "enabled" 12 | } 13 | } -------------------------------------------------------------------------------- /infra/terraform/modules/ecs_cluster/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | default = "eu-west-1" 4 | description = "The region where the module will be deployed." 5 | } -------------------------------------------------------------------------------- /infra/terraform/modules/lambda/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | default = "eu-west-1" 4 | description = "The region where the module will be deployed." 5 | } 6 | 7 | variable "project_name" { 8 | type = string 9 | description = "The name of the project" 10 | } 11 | 12 | variable "bucket_name" { 13 | type = string 14 | description = "The S3 bucket name where the code used is placed" 15 | } 16 | 17 | variable "code_path" { 18 | type = string 19 | description = "The path to the code used by the lambda function inside the S3 bucket" 20 | } 21 | 22 | variable "name" { 23 | type = string 24 | description = "The name of the lambda function" 25 | } 26 | 27 | variable "description" { 28 | type = string 29 | description = "The description of the lambda function" 30 | } 31 | 32 | variable "environment_variables" { 33 | type = map(string) 34 | description = "The map of the environment variables used by the lambda" 35 | default = {} 36 | } 37 | 38 | variable "run_every" { 39 | type = string 40 | description = "The cron expression which schedule the lambda trigger alarm. (default: every 12 hours)" 41 | default = "rate(12 hours)" 42 | } 43 | 44 | variable "vpc_config_enable" { 45 | type = bool 46 | description = "Enable the placement of the lambda inside the VPC" 47 | } 48 | 49 | variable "vpc_cidr" { 50 | type = string 51 | description = "[OPTIONAL] The VPC CIDR where the lambda will be deployed. (needed when vpc_config_enable=true)" 52 | default = "" 53 | } 54 | 55 | variable "subnet_name" { 56 | type = string 57 | description = "[OPTIONAL] The subnet where the lambda will be deployed. (needed when vpc_config_enable=true)" 58 | default = "" 59 | } 60 | 61 | variable "security_group_name" { 62 | type = string 63 | description = "[OPTIONAL] The security_group used by the lambda. (needed when vpc_config_enable=true)" 64 | default = "" 65 | } -------------------------------------------------------------------------------- /infra/terraform/modules/network/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | default = "eu-west-1" 4 | description = "The region where the module will be deployed" 5 | } 6 | 7 | variable "project_name" { 8 | type = string 9 | description = "The name of the project" 10 | } 11 | 12 | variable "vpc_cidr" { 13 | type = string 14 | description = "The CIDR of the VPC that will be deployed" 15 | } 16 | 17 | variable "subnet_tz_farm_cidr" { 18 | type = string 19 | description = "The CIDR of the subnet associated to the tezos-node farm" 20 | } 21 | 22 | variable "subnet_tz_public_ecs_cidr" { 23 | type = string 24 | description = "The CIDR of the subnet associated to the ecs's loadbalancers" 25 | } 26 | 27 | variable "subnet_tz_private_ecs_cidr" { 28 | type = string 29 | description = "The CIDR of the subnet associated to the ecs containers" 30 | } 31 | 32 | variable "subnet_tz_private_database_cidr" { 33 | type = string 34 | description = "The CIDR of the subnet associated to the databases" 35 | } -------------------------------------------------------------------------------- /infra/terraform/modules/rds_cluster/output.tf: -------------------------------------------------------------------------------- 1 | output "rds_endpoint" { 2 | value = aws_rds_cluster.database.endpoint 3 | } -------------------------------------------------------------------------------- /infra/terraform/modules/rds_cluster/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | default = "eu-west-1" 4 | description = "The region where the module will be deployed." 5 | } 6 | 7 | variable "project_name" { 8 | type = string 9 | description = "The name of the project." 10 | } 11 | 12 | variable "vpc_cidr" { 13 | type = string 14 | description = "The CIDR of the VPC where the Aurora will be placed." 15 | } 16 | 17 | variable "database_master_username" { 18 | type = string 19 | description = "The username used by the aurora database as the master username." 20 | } 21 | 22 | variable "database_name" { 23 | type = string 24 | description = "The name of the database created during the creation of the aurora." 25 | } -------------------------------------------------------------------------------- /infra/terraform/modules/s3_shared_bucket/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | backend "s3" {} 4 | } 5 | 6 | # S3 Bucket 7 | 8 | resource "aws_s3_bucket" "shared" { 9 | bucket = var.s3_bucket_name 10 | acl = "private" 11 | 12 | server_side_encryption_configuration { 13 | rule { 14 | apply_server_side_encryption_by_default { 15 | sse_algorithm = "AES256" 16 | } 17 | } 18 | } 19 | 20 | versioning { 21 | enabled = true 22 | } 23 | 24 | tags = { 25 | Name = var.s3_bucket_name 26 | Project = var.project_name 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /infra/terraform/modules/s3_shared_bucket/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "The region where the module is deployed" 4 | default = "eu-west-1" 5 | } 6 | 7 | variable "project_name" { 8 | type = string 9 | description = "The name of the project" 10 | } 11 | 12 | variable "s3_bucket_name" { 13 | type = string 14 | description = "The S3 bucket name" 15 | } -------------------------------------------------------------------------------- /infra/terraform/modules/service_api/templates/proxy_task_definition.json.tpl: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${task_name}", 4 | "image": "${task_image}", 5 | "portMappings": [ 6 | { 7 | "containerPort": ${task_port}, 8 | "hostPort": ${task_port}, 9 | "protocol": "tcp" 10 | } 11 | ], 12 | "cpu": ${task_cpu}, 13 | "memory": ${task_memory}, 14 | "environment": [ 15 | { 16 | "name": "SERVER_PORT", 17 | "value": "${task_port}" 18 | }, 19 | { 20 | "name": "DATABASE_URL", 21 | "value": "${database_url}" 22 | }, 23 | { 24 | "name": "DATABASE_USERNAME", 25 | "value": "${database_username}" 26 | }, 27 | { 28 | "name": "DATABASE_TABLE", 29 | "value": "${database_name}" 30 | }, 31 | { 32 | "name": "ENV", 33 | "value": "${configuration_file}" 34 | } 35 | ], 36 | "secrets": [ 37 | { 38 | "name": "DATABASE_PASSWORD", 39 | "valueFrom": "${database_password_arn}" 40 | } 41 | ], 42 | "logConfiguration": { 43 | "logDriver": "awslogs", 44 | "options": { 45 | "awslogs-group": "${log_group_name}", 46 | "awslogs-region": "${log_group_region}", 47 | "awslogs-stream-prefix": "${log_group_stream_prefix}" 48 | } 49 | } 50 | } 51 | ] -------------------------------------------------------------------------------- /infra/terraform/modules/service_api/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | default = "eu-west-1" 4 | description = "The region where the module will be deployed" 5 | } 6 | 7 | variable "project_name" { 8 | type = string 9 | description = "The name of the project" 10 | } 11 | 12 | variable "vpc_cidr" { 13 | type = string 14 | description = "The CIDR of the VPC where the ECS service will be deployed" 15 | } 16 | 17 | variable "docker_image_name" { 18 | type = string 19 | description = "The name of the deployed image" 20 | } 21 | 22 | variable "docker_image_version" { 23 | type = string 24 | description = "The version of the deployed image" 25 | } 26 | 27 | variable "desired_container_number" { 28 | type = number 29 | description = "The desired of container deployed by the service" 30 | } 31 | 32 | variable "port" { 33 | type = number 34 | description = "The port open by the container that will be targeted by the loadbalancer" 35 | } 36 | 37 | variable "cpu" { 38 | type = number 39 | description = "The CPU used by the service to run service's containers. (AWS CPU unit: 1vCPU = 1024)" 40 | } 41 | 42 | variable "memory" { 43 | type = number 44 | description = "The RAM used by the service to run service's containers" 45 | } 46 | 47 | variable "configuration_file" { 48 | type = string 49 | description = "the name of the configuration file inside the container" 50 | } 51 | 52 | variable "database_master_username" { 53 | type = string 54 | description = "The username used by the service to connect on the RDS database." 55 | } 56 | 57 | variable "database_name" { 58 | type = string 59 | description = "The name of the database used by the service" 60 | } -------------------------------------------------------------------------------- /infra/terraform/modules/service_proxy/templates/proxy_task_definition.json.tpl: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${task_name}", 4 | "image": "${task_image}", 5 | "portMappings": [ 6 | { 7 | "containerPort": ${task_port}, 8 | "hostPort": ${task_port}, 9 | "protocol": "tcp" 10 | } 11 | ], 12 | "cpu": ${task_cpu}, 13 | "memory": ${task_memory}, 14 | "ulimits": [ 15 | { 16 | "name": "nofile", 17 | "softLimit": 10000, 18 | "hardLimit": 12000 19 | } 20 | ], 21 | "environment": [ 22 | { 23 | "name": "SERVER_PORT", 24 | "value": "${task_port}" 25 | }, 26 | { 27 | "name": "DATABASE_URL", 28 | "value": "${database_url}" 29 | }, 30 | { 31 | "name": "DATABASE_USERNAME", 32 | "value": "${database_username}" 33 | }, 34 | { 35 | "name": "DATABASE_TABLE", 36 | "value": "${database_name}" 37 | }, 38 | { 39 | "name": "ARCHIVE_NODES_URL", 40 | "value": "${tezos_archive_hostname}" 41 | }, 42 | { 43 | "name": "TEZOS_ARCHIVE_PORT", 44 | "value": "${tezos_archive_port}" 45 | }, 46 | { 47 | "name": "ROLLING_NODES_URL", 48 | "value": "${tezos_rolling_hostname}" 49 | }, 50 | { 51 | "name": "TEZOS_ROLLING_PORT", 52 | "value": "${tezos_rolling_port}" 53 | }, 54 | { 55 | "name": "TEZOS_NETWORK", 56 | "value": "${tezos_network}" 57 | }, 58 | { 59 | "name": "ENV", 60 | "value": "${configuration_file}" 61 | } 62 | ], 63 | "secrets": [ 64 | { 65 | "name": "DATABASE_PASSWORD", 66 | "valueFrom": "${database_password_arn}" 67 | } 68 | ], 69 | "logConfiguration": { 70 | "logDriver": "awslogs", 71 | "options": { 72 | "awslogs-group": "${log_group_name}", 73 | "awslogs-region": "${log_group_region}", 74 | "awslogs-stream-prefix": "${log_group_stream_prefix}" 75 | } 76 | } 77 | } 78 | ] -------------------------------------------------------------------------------- /infra/terraform/modules/tezos_node/output.tf: -------------------------------------------------------------------------------- 1 | output "lb_farm_endpoint" { 2 | value = aws_alb.tz_farm.dns_name 3 | } 4 | -------------------------------------------------------------------------------- /infra/terraform/remote_configuration/config.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.12.20" 3 | } 4 | -------------------------------------------------------------------------------- /infra/terraform/remote_configuration/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "remote_state_storage" { 2 | bucket = "tzlink-remote-state" 3 | 4 | server_side_encryption_configuration { 5 | rule { 6 | apply_server_side_encryption_by_default { 7 | sse_algorithm = "AES256" 8 | } 9 | } 10 | } 11 | 12 | versioning { 13 | enabled = true 14 | } 15 | 16 | tags = { 17 | Name = "tzlink-remote-state" 18 | Project = "tezos-link" 19 | Environment = "global" 20 | } 21 | } 22 | 23 | resource "aws_dynamodb_table" "remote_state_lock" { 24 | name = "tzlink-remote-state-lock" 25 | read_capacity = 20 26 | write_capacity = 20 27 | hash_key = "LockID" 28 | 29 | attribute { 30 | name = "LockID" 31 | type = "S" 32 | } 33 | 34 | tags = { 35 | Name = "tzlink-remote-state-lock" 36 | Project = "tezos-link" 37 | Environment = "global" 38 | } 39 | } -------------------------------------------------------------------------------- /infra/terraform/remote_configuration/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-west-1" 4 | } -------------------------------------------------------------------------------- /infra/terraform/remote_configuration/terraform.tfstate.backup: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.12.20", 4 | "serial": 3, 5 | "lineage": "a7d43dab-ed60-c46f-2459-d22f64d45316", 6 | "outputs": {}, 7 | "resources": [] 8 | } 9 | -------------------------------------------------------------------------------- /infra/terragrunt/00_ecs_cluster/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | terraform { 6 | source = "../../terraform/modules/ecs_cluster" 7 | } -------------------------------------------------------------------------------- /infra/terragrunt/00_lambda_metrics_bucket/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | terraform { 6 | source = "../../terraform/modules/s3_shared_bucket" 7 | } 8 | 9 | inputs = { 10 | s3_bucket_name = "tzlink-metric-cleaner-lambda" 11 | } 12 | -------------------------------------------------------------------------------- /infra/terragrunt/00_lambda_snapshot_bucket/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | terraform { 6 | source = "../../terraform/modules/s3_shared_bucket" 7 | } 8 | 9 | inputs = { 10 | s3_bucket_name = "tzlink-snapshot-lambda" 11 | 12 | vpc_endpoint_enabled = true 13 | route_table_name = "tzlink-public" 14 | } 15 | -------------------------------------------------------------------------------- /infra/terragrunt/00_network/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | terraform { 6 | source = "../../terraform/modules/network" 7 | } 8 | 9 | inputs = { 10 | subnet_tz_farm_cidr = "10.1.0.0/24" 11 | subnet_tz_public_ecs_cidr = "10.1.1.0/24" 12 | subnet_tz_private_ecs_cidr = "10.1.2.0/24" 13 | subnet_tz_private_database_cidr = "10.1.3.0/24" 14 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_frontend/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | terraform { 6 | source = "../../terraform/modules/cloudfront" 7 | } 8 | 9 | inputs = { 10 | certificate_arn = "arn:aws:acm:us-east-1:912174778846:certificate/6f9b1bac-5177-488a-a350-95b10d056a2a" 11 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_rds_cluster/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/rds_cluster" 11 | } 12 | 13 | inputs = { 14 | database_master_username = "tezoslink_team" 15 | database_name = "tezoslink" 16 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_snapshot_bucket/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/s3_shared_bucket" 11 | } 12 | 13 | inputs = { 14 | s3_bucket_name = "tzlink-blockchain-data" 15 | vpc_endpoint_enabled = true 16 | route_table_name = "tzlink-public" 17 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_tezos_carthagenet_archive_node/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/tezos_node" 11 | } 12 | 13 | inputs = { 14 | tz_network = "carthagenet" 15 | tz_mode = "archive" 16 | 17 | min_instance_number = 1 18 | desired_instance_number = 1 19 | max_instance_number = 5 20 | 21 | health_check_grace_period = 1620 #sec (=27min) 22 | 23 | cpu_out_scaling_cooldown = 1800 #sec (=30min) 24 | cpu_out_scaling_threshold = 40 #% 25 | cpu_out_evaluation_periods = 30 #min 26 | 27 | cpu_down_scaling_cooldown = 300 #sec 28 | cpu_down_scaling_threshold = 5 #% 29 | cpu_down_evaluation_periods = 5 #min 30 | 31 | responsetime_out_scaling_cooldown = 60 #sec 32 | responsetime_out_scaling_threshold = 1.0 #sec 33 | responsetime_out_evaluation_periods = 2 #min 34 | 35 | instance_type = "i3.large" 36 | key_pair_name = "adbo" # TODO : use a shared keypair 37 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_tezos_carthagenet_rolling_node/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/tezos_node" 11 | } 12 | 13 | inputs = { 14 | tz_network = "carthagenet" 15 | tz_mode = "rolling" 16 | 17 | min_instance_number = 2 18 | desired_instance_number = 2 19 | max_instance_number = 6 20 | 21 | health_check_grace_period = 900 #sec (=15min) 22 | 23 | cpu_out_scaling_cooldown = 300 #sec (=5min) 24 | cpu_out_scaling_threshold = 40 #% 25 | cpu_out_evaluation_periods = 5 #min 26 | 27 | cpu_down_scaling_cooldown = 300 #sec 28 | cpu_down_scaling_threshold = 5 #% 29 | cpu_down_evaluation_periods = 5 #min 30 | 31 | responsetime_out_scaling_cooldown = 60 #sec 32 | responsetime_out_scaling_threshold = 0.6 #sec 33 | responsetime_out_evaluation_periods = 2 #min 34 | 35 | instance_type = "t3.small" 36 | key_pair_name = "adbo" # TODO : use a shared keypair 37 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_tezos_mainnet_archive_node/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/tezos_node" 11 | } 12 | 13 | inputs = { 14 | tz_network = "mainnet" 15 | tz_mode = "archive" 16 | 17 | min_instance_number = 1 18 | desired_instance_number = 1 19 | max_instance_number = 5 20 | 21 | health_check_grace_period = 1620 #sec (=27min) 22 | 23 | cpu_out_scaling_cooldown = 1800 #sec (=30min) 24 | cpu_out_scaling_threshold = 40 #% 25 | cpu_out_evaluation_periods = 30 #min 26 | 27 | cpu_down_scaling_cooldown = 300 #sec 28 | cpu_down_scaling_threshold = 5 #% 29 | cpu_down_evaluation_periods = 5 #min 30 | 31 | responsetime_out_scaling_cooldown = 60 #sec 32 | responsetime_out_scaling_threshold = 1.0 #sec 33 | responsetime_out_evaluation_periods = 2 #min 34 | 35 | instance_type = "i3.large" 36 | key_pair_name = "adbo" # TODO : use a shared keypair 37 | } -------------------------------------------------------------------------------- /infra/terragrunt/01_tezos_mainnet_rolling_node/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/tezos_node" 11 | } 12 | 13 | inputs = { 14 | tz_network = "mainnet" 15 | tz_mode = "rolling" 16 | 17 | min_instance_number = 2 18 | desired_instance_number = 2 19 | max_instance_number = 6 20 | 21 | health_check_grace_period = 900 #sec (=15min) 22 | 23 | cpu_out_scaling_cooldown = 300 #sec (=5min) 24 | cpu_out_scaling_threshold = 40 #% 25 | cpu_out_evaluation_periods = 5 #min 26 | 27 | cpu_down_scaling_cooldown = 300 #sec 28 | cpu_down_scaling_threshold = 5 #% 29 | cpu_down_evaluation_periods = 5 #min 30 | 31 | responsetime_out_scaling_cooldown = 60 #sec 32 | responsetime_out_scaling_threshold = 0.6 #sec 33 | responsetime_out_evaluation_periods = 2 #min 34 | 35 | instance_type = "t3.small" 36 | key_pair_name = "adbo" # TODO : use a shared keypair 37 | } -------------------------------------------------------------------------------- /infra/terragrunt/02_lambda_metrics/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = ["../00_network", "../01_rds_cluster"] 7 | } 8 | 9 | terraform { 10 | source = "../../terraform/modules/lambda" 11 | } 12 | 13 | inputs = { 14 | # lambda configuration 15 | name = "metrics" 16 | description = "RDS metrics cleaner" 17 | environment_variables = { 18 | DATABASE_USERNAME = "tezoslink_team" 19 | DATABASE_NAME = "tezoslink" 20 | DATABASE_URL = "tzlink-database.cluster-cjwwybxbgnpm.eu-west-1.rds.amazonaws.com" 21 | DATABASE_PASSWORD_SECRET_ARN = "arn:aws:secretsmanager:eu-west-1:912174778846:secret:tzlink-database-password-IwehdC" 22 | } 23 | run_every = "cron(0 1 * * ? *)" 24 | 25 | # based on 00_lambda_snapshot_bucket 26 | bucket_name = "tzlink-metric-cleaner-lambda" 27 | code_path = "v1.0.0/metrics.zip" 28 | 29 | # network configuration (if needed) 30 | vpc_config_enable = true 31 | subnet_name = "tzlink-private-database-*" 32 | security_group_name = "tzlink-database" 33 | } 34 | -------------------------------------------------------------------------------- /infra/terragrunt/02_lambda_snapshot_carthagenet/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = [ 7 | "../00_lambda_snapshot_bucket", 8 | "../01_tezos_carthagenet_archive_node" 9 | ] 10 | } 11 | 12 | terraform { 13 | source = "../../terraform/modules/lambda" 14 | } 15 | 16 | inputs = { 17 | # lambda configuration 18 | name = "snapshot-carthagenet" 19 | description = "Snapshot exporter lambda for carthagenet" 20 | environment_variables = { 21 | NODE_USER = "ubuntu" 22 | S3_REGION = "eu-west-1" 23 | S3_BUCKET = "tzlink-snapshot-lambda" 24 | S3_LAMBDA_KEY = "snapshot_lambda_key" 25 | NETWORK = "carthagenet" 26 | } 27 | run_every = "cron(0 1/12 * * ? *)" 28 | 29 | # based on 00_lambda_snapshot_bucket 30 | bucket_name = "tzlink-snapshot-lambda" 31 | code_path = "v1.0.0/snapshot.zip" 32 | 33 | # network configuration (if needed) 34 | vpc_config_enable = true 35 | subnet_name = "tzlink-farm-*" 36 | security_group_name = "tzlink_farm_carthagenet_archive" 37 | } 38 | -------------------------------------------------------------------------------- /infra/terragrunt/02_lambda_snapshot_mainnet/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = [ 7 | "../00_lambda_snapshot_bucket", 8 | "../01_tezos_mainnet_archive_node" 9 | ] 10 | } 11 | 12 | terraform { 13 | source = "../../terraform/modules/lambda" 14 | } 15 | 16 | inputs = { 17 | # lambda configuration 18 | name = "snapshot-mainnet" 19 | description = "Snapshot exporter lambda for mainnet" 20 | environment_variables = { 21 | NODE_USER = "ubuntu" 22 | S3_REGION = "eu-west-1" 23 | S3_BUCKET = "tzlink-snapshot-lambda" 24 | S3_LAMBDA_KEY = "snapshot_lambda_key" 25 | NETWORK = "mainnet" 26 | } 27 | run_every = "cron(0 1/12 * * ? *)" 28 | 29 | # based on 00_lambda_snapshot_bucket 30 | bucket_name = "tzlink-snapshot-lambda" 31 | code_path = "v1.0.0/snapshot.zip" 32 | 33 | # network configuration (if needed) 34 | vpc_config_enable = true 35 | subnet_name = "tzlink-farm-*" 36 | security_group_name = "tzlink_farm_mainnet_archive" 37 | } 38 | -------------------------------------------------------------------------------- /infra/terragrunt/02_service_api/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = [ 7 | "../00_ecs_cluster", 8 | "../01_rds_cluster" 9 | ] 10 | } 11 | 12 | terraform { 13 | source = "../../terraform/modules/service_api" 14 | } 15 | 16 | inputs = { 17 | docker_image_name = "louptheronlth/tezos-link" 18 | docker_image_version = "${get_env("TF_VAR_DOCKER_IMAGE_VERSION", "proxy-api")}" 19 | desired_container_number = 1 20 | 21 | port = 8001 22 | cpu = 256 23 | memory = 512 24 | 25 | configuration_file = "local" 26 | 27 | database_master_username = "tezoslink_team" 28 | database_name = "tezoslink" 29 | } -------------------------------------------------------------------------------- /infra/terragrunt/02_service_proxy_carthagenet/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = [ 7 | "../00_ecs_cluster", 8 | "../01_rds_cluster", 9 | "../01_tezos_carthagenet_archive_node", 10 | "../01_tezos_carthagenet_rolling_node" 11 | ] 12 | } 13 | 14 | terraform { 15 | source = "../../terraform/modules/service_proxy" 16 | } 17 | 18 | inputs = { 19 | docker_image_name = "louptheronlth/tezos-link" 20 | docker_image_version = "${get_env("TF_VAR_DOCKER_IMAGE_VERSION", "proxy-dev")}" 21 | desired_container_number = 2 22 | 23 | port = 8001 24 | cpu = 256 25 | memory = 512 26 | 27 | configuration_file = "prod" 28 | tz_network = "carthagenet" 29 | 30 | database_master_username = "tezoslink_team" 31 | database_name = "tezoslink" 32 | 33 | farm_archive_port = 80 34 | farm_rolling_port = 80 35 | } 36 | -------------------------------------------------------------------------------- /infra/terragrunt/02_service_proxy_mainnet/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | include { 2 | path = "${find_in_parent_folders()}" 3 | } 4 | 5 | dependencies { 6 | paths = [ 7 | "../00_ecs_cluster", 8 | "../01_rds_cluster", 9 | "../01_tezos_mainnet_archive_node", 10 | "../01_tezos_mainnet_rolling_node" 11 | ] 12 | } 13 | 14 | terraform { 15 | source = "../../terraform/modules/service_proxy" 16 | } 17 | 18 | inputs = { 19 | docker_image_name = "louptheronlth/tezos-link" 20 | docker_image_version = "${get_env("TF_VAR_DOCKER_IMAGE_VERSION", "proxy-dev")}" 21 | desired_container_number = 2 22 | 23 | port = 8001 24 | cpu = 256 25 | memory = 512 26 | 27 | configuration_file = "prod" 28 | tz_network = "mainnet" 29 | 30 | database_master_username = "tezoslink_team" 31 | database_name = "tezoslink" 32 | 33 | farm_archive_port = 80 34 | farm_rolling_port = 80 35 | } 36 | -------------------------------------------------------------------------------- /infra/terragrunt/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | remote_state { 2 | backend = "s3" 3 | config = { 4 | bucket = "tzlink-remote-state" 5 | key = "${get_env("TF_VAR_ENV", "prod")}/${path_relative_to_include()}/terraform.tfstate" 6 | encrypt = true 7 | region = "eu-west-1" 8 | dynamodb_table = "tzlink-remote-state-lock" 9 | } 10 | } 11 | 12 | terraform { 13 | extra_arguments "custom_vars" { 14 | commands = get_terraform_commands_that_need_vars() 15 | 16 | required_var_files = [ 17 | "${get_parent_terragrunt_dir()}/../common.tfvars" 18 | ] 19 | } 20 | } 21 | 22 | generate "provider" { 23 | path = "provider.tf" 24 | if_exists = "overwrite_terragrunt" 25 | contents = < Tips: you can setup a service systemd to run locust. It permits to avoid the server discconnection if the ssh connection is broken due to connection problem. 30 | 31 | Once the server is started, go on the url `ip_adress:8089` and you should see the following webpage. 32 | 33 | ![LocustWebpage](img/locust-server.png "LocustServer") 34 | 35 | Enter 36 | - the url to test (here `mainnet-archive.internal.tezoslink.io` or `mainnet-rolling.internal.tezoslink.io`) 37 | - the max user threashold 38 | - the number of new user every sec until the max user threashold is reached. 39 | 40 | Here we are, the test started. You can see graphs in the option. 41 | You should see something link this at the end. 42 | 43 | ![LocustGraph](img/locust-graph.png "LocustGraph") 44 | 45 | > Warning: the result is stored inside your browser and you could lose your result if you refresh the page so be aware of it. 46 | 47 | Enjoy your test. -------------------------------------------------------------------------------- /perf/img/locust-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/perf/img/locust-graph.png -------------------------------------------------------------------------------- /perf/img/locust-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/perf/img/locust-server.png -------------------------------------------------------------------------------- /perf/locust_load_on_head.py: -------------------------------------------------------------------------------- 1 | from locust import task, between 2 | from locust.contrib.fasthttp import FastHttpUser 3 | 4 | 5 | class RollingNodeUser(FastHttpUser): 6 | wait_time = between(5, 9) 7 | 8 | @task 9 | def spam_head(self): 10 | self.client.get("/chains/main/blocks/head") 11 | 12 | def on_start(self): 13 | pass 14 | -------------------------------------------------------------------------------- /perf/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2020.4.5.2 2 | chardet==3.0.4 3 | click==7.1.2 4 | ConfigArgParse==1.2.3 5 | confluent-kafka==1.4.2 6 | Flask==1.1.2 7 | Flask-BasicAuth==0.2.0 8 | gevent==20.6.2 9 | geventhttpclient==1.4.2 10 | greenlet==0.4.16 11 | idna==2.9 12 | itsdangerous==1.1.0 13 | Jinja2==2.11.3 14 | locust==1.0.3 15 | locust-plugins==1.0.12 16 | lxml==4.6.3 17 | MarkupSafe==1.1.1 18 | msgpack==1.0.0 19 | psutil==5.7.0 20 | psycogreen==1.0.2 21 | psycopg2-binary==2.8.5 22 | pymongo==3.10.1 23 | python-dateutil==2.8.1 24 | pyzmq==19.0.1 25 | requests==2.23.0 26 | selenium==3.141.0 27 | six==1.15.0 28 | urllib3==1.26.5 29 | websocket-client==0.57.0 30 | Werkzeug==1.0.1 31 | zope.event==4.4 32 | zope.interface==5.1.0 33 | -------------------------------------------------------------------------------- /pkg/domain/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import "errors" 4 | 5 | // ErrProjectNotFound when a project is not found 6 | var ErrProjectNotFound = errors.New("project not found") 7 | 8 | // ErrNoProjectName when a project has no name 9 | var ErrNoProjectName = errors.New("project name not defined") 10 | 11 | // ErrNoProxyResponse when the proxy doesn't respond 12 | var ErrNoProxyResponse = errors.New("no response from proxy") 13 | 14 | // ErrNoMetricsFound when there is no metrics found 15 | var ErrNoMetricsFound = errors.New("no old metrics") 16 | 17 | // ErrInvalidNetwork when there is an invalid network 18 | var ErrInvalidNetwork = errors.New("invalid network") 19 | -------------------------------------------------------------------------------- /pkg/domain/model/metrics.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // Metrics contains the fields to represent project's metrics 4 | type Metrics struct { 5 | RequestsCount int 6 | RequestsByDay []*RequestsByDayMetrics 7 | RPCUSage []*RPCUsageMetrics 8 | LastRequests []string 9 | } 10 | 11 | // NewMetrics returns a new metrics 12 | func NewMetrics( 13 | requestsCount int, 14 | requestsByDay []*RequestsByDayMetrics, 15 | rpcUsage []*RPCUsageMetrics, 16 | lastRequests []string) Metrics { 17 | return Metrics{ 18 | RequestsCount: requestsCount, 19 | RequestsByDay: requestsByDay, 20 | RPCUSage: rpcUsage, 21 | LastRequests: lastRequests, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/domain/model/project.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | // Project contains the fields to represent a project 6 | type Project struct { 7 | ID int64 8 | Title string 9 | UUID string 10 | CreationDate time.Time 11 | Network string 12 | } 13 | 14 | // NewProject returns a new project 15 | func NewProject(ID int64, title string, uuid string, creationDate time.Time, network string) Project { 16 | return Project{ 17 | ID: ID, 18 | Title: title, 19 | UUID: uuid, 20 | CreationDate: creationDate, 21 | Network: network, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/domain/model/request.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // Action represents the int 4 | type Action int 5 | 6 | // OBTAIN, PUSH and MODIFY methods 7 | const ( 8 | OBTAIN Action = iota + 1 9 | PUSH 10 | MODIFY 11 | ) 12 | 13 | // Request represents an request made to the proxy 14 | type Request struct { 15 | Path string 16 | UUID string 17 | Action Action 18 | RemoteAddr string 19 | } 20 | 21 | // NewRequest returns a new request 22 | func NewRequest(path string, UUID string, action Action, remoteAddr string) Request { 23 | return Request{ 24 | Path: path, 25 | UUID: UUID, 26 | Action: action, 27 | RemoteAddr: remoteAddr, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/domain/model/requests_by_day_metrics.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // RequestsByDayMetrics count requests made on a given date 4 | type RequestsByDayMetrics struct { 5 | Year string 6 | Month string 7 | Day string 8 | Value int 9 | } 10 | 11 | // NewRequestsByDayMetrics returns a new RequestsByDayMetrics 12 | func NewRequestsByDayMetrics(year string, month string, day string, value int) *RequestsByDayMetrics { 13 | return &RequestsByDayMetrics{ 14 | Year: year, 15 | Month: month, 16 | Day: day, 17 | Value: value, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/domain/model/rpc_usage_metrics.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // RPCUsageMetrics represents a number of requests for a given path 4 | type RPCUsageMetrics struct { 5 | Path string 6 | Value int 7 | } 8 | 9 | // NewRPCUsageMetrics returns a new RPCUsageMetrics 10 | func NewRPCUsageMetrics(path string, value int) *RPCUsageMetrics { 11 | return &RPCUsageMetrics{ 12 | Path: path, 13 | Value: value, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pkg/domain/repository/metric_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/octo-technology/tezos-link/backend/pkg/domain/model" 5 | "github.com/octo-technology/tezos-link/backend/pkg/infrastructure/database/inputs" 6 | "time" 7 | ) 8 | 9 | // MetricsRepository contains all available methods of the metrics repository 10 | type MetricsRepository interface { 11 | SaveMany(metricInputs []*inputs.MetricsInput) error 12 | Save(metricInput *inputs.MetricsInput) error 13 | CountAll(uuid string) (int, error) 14 | FindRequestsByDay(uuid string, from time.Time, to time.Time) ([]*model.RequestsByDayMetrics, error) 15 | CountRPCPathUsage(uuid string, from time.Time, to time.Time) ([]*model.RPCUsageMetrics, error) 16 | FindLastRequests(uuid string) ([]string, error) 17 | RemoveThreeMonthsOldMetrics() error 18 | } 19 | -------------------------------------------------------------------------------- /pkg/domain/repository/project_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | pkgmodel "github.com/octo-technology/tezos-link/backend/pkg/domain/model" 5 | "time" 6 | ) 7 | 8 | // ProjectRepository contains all methods available for the project repository 9 | type ProjectRepository interface { 10 | FindAll() ([]*pkgmodel.Project, error) 11 | FindByUUID(uuid string) (*pkgmodel.Project, error) 12 | Save(title string, uuid string, creationDate time.Time, network string) (*pkgmodel.Project, error) 13 | Ping() error 14 | } 15 | -------------------------------------------------------------------------------- /pkg/infrastructure/cache/lru_project_repository.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "fmt" 5 | lru "github.com/hashicorp/golang-lru" 6 | "github.com/octo-technology/tezos-link/backend/config" 7 | pkgmodel "github.com/octo-technology/tezos-link/backend/pkg/domain/model" 8 | "github.com/octo-technology/tezos-link/backend/pkg/domain/repository" 9 | "log" 10 | "time" 11 | ) 12 | 13 | type lruProjectRepository struct { 14 | cache *lru.Cache 15 | } 16 | 17 | // NewLRUProjectRepository returns a new project LRU cache repository 18 | func NewLRUProjectRepository() repository.ProjectRepository { 19 | cache, err := lru.New(config.ProxyConfig.Proxy.ProjectsCacheMaxItems) 20 | if err != nil { 21 | log.Fatal("could not init the LRU cache") 22 | } 23 | 24 | return &lruProjectRepository{ 25 | cache: cache, 26 | } 27 | } 28 | 29 | func (l lruProjectRepository) FindByUUID(uuid string) (*pkgmodel.Project, error) { 30 | val, ok := l.cache.Get(uuid) 31 | if !ok { 32 | return nil, fmt.Errorf("could not get cache for path: %s", uuid) 33 | } 34 | 35 | ret := val.(pkgmodel.Project) 36 | 37 | return &ret, nil 38 | } 39 | 40 | func (l lruProjectRepository) Save(title string, uuid string, creationDate time.Time, network string) (*pkgmodel.Project, error) { 41 | noID := 0 42 | project := pkgmodel.NewProject(int64(noID), title, uuid, creationDate, network) 43 | 44 | l.cache.Add(uuid, project) 45 | 46 | return &project, nil 47 | } 48 | 49 | func (l lruProjectRepository) FindAll() ([]*pkgmodel.Project, error) { 50 | panic("implement me") 51 | } 52 | 53 | func (l lruProjectRepository) Ping() error { 54 | panic("implement me") 55 | } 56 | -------------------------------------------------------------------------------- /pkg/infrastructure/cache/lru_project_repository_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/bmizerany/assert" 5 | "github.com/octo-technology/tezos-link/backend/config" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestLRUProjectRepository_FindByUUID_Unit(t *testing.T) { 11 | // Given 12 | _, err := config.ParseProxyConf("../../../test/proxy/conf/test.toml") 13 | if err != nil { 14 | t.Fatal("could not parse conf", err) 15 | } 16 | 17 | projectCache := NewLRUProjectRepository() 18 | projectUUID := "12234" 19 | projectTitle := "DummyProject" 20 | projectNetwork := "CARTHAGENET" 21 | projectCreationDate := time.Now() 22 | 23 | // When 24 | _, err = projectCache.Save(projectTitle, projectUUID, projectCreationDate, projectNetwork) 25 | if err != nil { 26 | t.Fatal("could not save in cache", err) 27 | } 28 | 29 | prj, err := projectCache.FindByUUID(projectUUID) 30 | if err != nil { 31 | t.Fatal("project not found in cache", err) 32 | } 33 | 34 | // Then 35 | assert.Equal(t, prj.UUID, projectUUID) 36 | assert.Equal(t, prj.Title, projectTitle) 37 | assert.Equal(t, prj.CreationDate, projectCreationDate) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /pkg/infrastructure/database/helpers_test.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "github.com/golang-migrate/migrate/v4" 7 | _ "github.com/golang-migrate/migrate/v4/database/postgres" 8 | _ "github.com/golang-migrate/migrate/v4/source/file" 9 | _ "github.com/lib/pq" 10 | "github.com/ory/dockertest" 11 | "log" 12 | ) 13 | 14 | func GetPostgresClient(pool dockertest.Pool) (*sql.DB, *dockertest.Resource) { 15 | var con *sql.DB 16 | var err error 17 | 18 | resource, err := pool.Run("postgres", "9.6", []string{"POSTGRES_PASSWORD=pass", "POSTGRES_DB=tezoslink"}) 19 | if err != nil { 20 | log.Fatalf("Could not start resource: %s", err) 21 | } 22 | 23 | // exponential backoff-retry, because the postgres in the container might not be ready to accept connections yet 24 | url := fmt.Sprintf("postgres://postgres:pass@localhost:%s/tezoslink?sslmode=disable", resource.GetPort("5432/tcp")) 25 | if err := pool.Retry(func() error { 26 | var err error 27 | con, err := sql.Open("postgres", url) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | return con.Ping() 33 | }); err != nil { 34 | log.Fatalf("Could not connect to docker: %s", err) 35 | } 36 | 37 | con, _ = sql.Open("postgres", url) 38 | runMigrations(url) 39 | 40 | return con, resource 41 | } 42 | 43 | func getDockerPool() *dockertest.Pool { 44 | pool, err := dockertest.NewPool("") 45 | if err != nil { 46 | log.Fatalf("Could not connect to docker: %s", err) 47 | } 48 | return pool 49 | } 50 | 51 | func runMigrations(url string) { 52 | m, err := migrate.New( 53 | "file:../../../data/api/migrations", 54 | url) 55 | if err != nil { 56 | log.Fatal("Could not apply db migration: ", err) 57 | } 58 | if err := m.Up(); err != nil { 59 | log.Fatal("Could not apply db migration: ", err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pkg/infrastructure/database/inputs/metrics_input.go: -------------------------------------------------------------------------------- 1 | package inputs 2 | 3 | import ( 4 | "github.com/octo-technology/tezos-link/backend/pkg/domain/model" 5 | "time" 6 | ) 7 | 8 | // MetricsInput represents a new metric insert for a request made on a given date 9 | type MetricsInput struct { 10 | Request *model.Request 11 | Date time.Time 12 | } 13 | 14 | // NewMetricsInput returns a new MetricsInput 15 | func NewMetricsInput(request *model.Request, date time.Time) MetricsInput { 16 | return MetricsInput{ 17 | Request: request, 18 | Date: date, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/proxy/conf/test.toml: -------------------------------------------------------------------------------- 1 | debug = true 2 | networks = ["MAINNET","CARTHAGENET"] 3 | [tezos] 4 | host = "127.0.0.1" 5 | port = 8765 6 | 7 | [server] 8 | port = 8899 9 | 10 | [proxy] 11 | readTimeout = 1 12 | writeTimeout = 5 13 | idleTimeout = 120 14 | whitelistedMethods = [ 15 | "/chains/main/blocks(.*?)", 16 | ] 17 | blockedMethods = [ 18 | "(.*?)context/contracts$", 19 | "/monitor(.*?)", 20 | "/network(.*?)", 21 | ] 22 | dontCache = [ 23 | "(.*?)/head/(.*?)", 24 | "(.*?)/head", 25 | "/chains/main/blocks$", 26 | ] 27 | rateLimitPeriod = 100 28 | rateLimitCount = 100 29 | blockchainRequestsCacheMaxItems = 2000 30 | projectsCacheMaxItems = 1000 31 | cacheMaxMetricItems = 100 32 | routineDelaySeconds = 60 33 | whitelistedRolling = [ 34 | "(.*?)/head/(.*?)", 35 | "(.*?)/head", 36 | "(.*?)/injection/operation", 37 | ] 38 | -------------------------------------------------------------------------------- /vendor/golint: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/vendor/golint -------------------------------------------------------------------------------- /web/.env: -------------------------------------------------------------------------------- 1 | NODE_PATH=./src 2 | -------------------------------------------------------------------------------- /web/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | dist -------------------------------------------------------------------------------- /web/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | public -------------------------------------------------------------------------------- /web/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /web/public/Chapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/Chapter.png -------------------------------------------------------------------------------- /web/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /web/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /web/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/apple-touch-icon.png -------------------------------------------------------------------------------- /web/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /web/public/docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | This is the documentation served at `https:///documentation` 4 | -------------------------------------------------------------------------------- /web/public/docs/menu.md: -------------------------------------------------------------------------------- 1 | - [Usage](##usage) 2 | - [Networks](#networks) 3 | - [Make Requests](#make-requests) 4 | - [Security](##security) 5 | - [RPC Endpoints](#rpc-endpoints) 6 | - [Nodes](#nodes) 7 | 8 | -------------------------------------------------------------------------------- /web/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/favicon-16x16.png -------------------------------------------------------------------------------- /web/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/favicon-32x32.png -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Bold.woff -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Bold.woff2 -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Light.woff -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Light.woff2 -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Regular.woff -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Regular.woff2 -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Semibold.woff -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Semibold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Semibold.woff2 -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Thin.woff -------------------------------------------------------------------------------- /web/public/fonts/ProximaNova-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/fonts/ProximaNova-Thin.woff2 -------------------------------------------------------------------------------- /web/public/icons/annimated-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/public/icons/arrow-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/public/icons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /web/public/icons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /web/public/icons/arrow-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/public/icons/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /web/public/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | close 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /web/public/icons/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/public/icons/cupport.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /web/public/icons/dialog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web/public/icons/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /web/public/icons/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | infomation-circle 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/input-error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /web/public/icons/input-success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/login.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /web/public/icons/love.svg: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /web/public/icons/password.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/plus-card.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /web/public/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/public/icons/sign-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /web/public/icons/success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web/public/icons/support.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /web/public/icons/unplugged.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/public/icons/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web/public/icons/warning.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/public/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/images/architecture.png -------------------------------------------------------------------------------- /web/public/images/grid-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/images/grid-bg.png -------------------------------------------------------------------------------- /web/public/images/metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/images/metrics.png -------------------------------------------------------------------------------- /web/public/images/mini-torus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/images/mini-torus.png -------------------------------------------------------------------------------- /web/public/images/particle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /web/public/images/post1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/images/post1.jpeg -------------------------------------------------------------------------------- /web/public/images/user1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/images/user1.jpg -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | Free Tezos APIs as a Service | Tezos Link 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /web/public/link_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /web/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/logo192.png -------------------------------------------------------------------------------- /web/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Tezos Link", 3 | "name": "Your gateway to the Tezos network", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "white", 14 | "background_color": "white" 15 | } 16 | -------------------------------------------------------------------------------- /web/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octo-technology/tezos-link/e749ddd6a97857ed1bbb5e656d729d7139a7b209/web/public/mstile-150x150.png -------------------------------------------------------------------------------- /web/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /web/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /web/src/App/App.components/Button/Button.constants.tsx: -------------------------------------------------------------------------------- 1 | export const BUTTON = 'button' 2 | export const SUBMIT = 'submit' 3 | export const RESET = 'reset' 4 | export type ButtonTypes = 'button' | 'submit' | 'reset' | undefined 5 | 6 | export const PRIMARY = 'primary' 7 | export const SECONDARY = 'secondary' 8 | export const TRANSPARENT = 'transparent' 9 | export type ButtonColors = 'primary' | 'secondary' | 'transparent' | undefined 10 | -------------------------------------------------------------------------------- /web/src/App/App.components/Button/Button.controller.tsx: -------------------------------------------------------------------------------- 1 | import * as PropTypes from 'prop-types' 2 | import * as React from 'react' 3 | import { useState } from 'react' 4 | 5 | import { BUTTON, ButtonTypes, ButtonColors, PRIMARY } from './Button.constants' 6 | import { ButtonView } from './Button.view' 7 | 8 | type ButtonProps = { 9 | text: string 10 | icon?: string 11 | color?: ButtonColors 12 | onClick?: () => void 13 | type?: ButtonTypes 14 | loading: boolean 15 | } 16 | 17 | export const Button = ({ text, icon, color, onClick, type, loading }: ButtonProps) => { 18 | const [clicked, setClicked] = useState(false) 19 | const clickCallback = () => { 20 | setClicked(true) 21 | setTimeout(() => setClicked(false), 1000) 22 | } 23 | return ( 24 | 34 | ) 35 | } 36 | 37 | Button.propTypes = { 38 | text: PropTypes.string.isRequired, 39 | icon: PropTypes.string, 40 | color: PropTypes.string, 41 | onClick: PropTypes.func, 42 | type: PropTypes.string, 43 | loading: PropTypes.bool 44 | } 45 | 46 | Button.defaultProps = { 47 | icon: undefined, 48 | color: PRIMARY, 49 | type: BUTTON, 50 | loading: false 51 | } 52 | -------------------------------------------------------------------------------- /web/src/App/App.components/Button/Button.view.tsx: -------------------------------------------------------------------------------- 1 | import * as PropTypes from 'prop-types' 2 | import * as React from 'react' 3 | 4 | import { BUTTON, ButtonColors, ButtonTypes, PRIMARY } from './Button.constants' 5 | import { ButtonIcon, ButtonLoadingIcon, ButtonStyled, ButtonText } from './Button.style' 6 | 7 | type ButtonViewProps = { 8 | text: string 9 | icon?: string 10 | color?: ButtonColors 11 | onClick?: () => void 12 | clickCallback: () => void 13 | clicked: boolean 14 | type?: ButtonTypes 15 | loading: boolean 16 | } 17 | 18 | export const ButtonView = ({ text, icon, color, onClick, clickCallback, clicked, type, loading }: ButtonViewProps) => { 19 | let buttonClasses = color 20 | if (clicked) buttonClasses += ' clicked' 21 | if (loading) buttonClasses += ' loading' 22 | return ( 23 | { 26 | clickCallback() 27 | onClick && onClick() 28 | }} 29 | type={type} 30 | > 31 | 32 | {loading ? ( 33 | <> 34 | Loading 35 | 36 | 37 | 38 | 39 | ) : ( 40 | <> 41 | {text} 42 | {icon && ( 43 | 44 | 45 | 46 | )} 47 | 48 | )} 49 | 50 | 51 | ) 52 | } 53 | 54 | ButtonView.propTypes = { 55 | text: PropTypes.string.isRequired, 56 | icon: PropTypes.string, 57 | color: PropTypes.string, 58 | onClick: PropTypes.func, 59 | clickCallback: PropTypes.func.isRequired, 60 | clicked: PropTypes.bool.isRequired, 61 | type: PropTypes.string, 62 | loading: PropTypes.bool 63 | } 64 | 65 | ButtonView.defaultProps = { 66 | icon: undefined, 67 | color: PRIMARY, 68 | type: BUTTON, 69 | loading: false 70 | } 71 | -------------------------------------------------------------------------------- /web/src/App/App.components/Drawer/Drawer.actions.tsx: -------------------------------------------------------------------------------- 1 | export const SHOW_DRAWER = 'SHOW_DRAWER' 2 | export const HIDE_DRAWER = 'HIDE_DRAWER' 3 | 4 | export const showDrawer = () => (dispatch: any) => { 5 | dispatch({ 6 | type: SHOW_DRAWER 7 | }) 8 | } 9 | 10 | export const hideDrawer = () => (dispatch: any) => { 11 | dispatch({ 12 | type: HIDE_DRAWER 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /web/src/App/App.components/Drawer/Drawer.controller.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { useLocation } from 'react-router-dom' 4 | 5 | import { hideDrawer } from './Drawer.actions' 6 | import { DrawerView } from './Drawer.view' 7 | 8 | export const Drawer = () => { 9 | const dispatch = useDispatch() 10 | const showing = useSelector((state: any) => state.drawer && state.drawer.showing) 11 | const user = useSelector((state: any) => state && state.auth && state.auth.user) 12 | const { pathname } = useLocation() 13 | 14 | const hideCallback = () => { 15 | dispatch(hideDrawer()) 16 | } 17 | 18 | function removeAuthUserCallback() { 19 | // dispatch(removeAuthUser()) 20 | } 21 | 22 | return ( 23 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /web/src/App/App.components/Drawer/Drawer.reducers.tsx: -------------------------------------------------------------------------------- 1 | import { SHOW_DRAWER, HIDE_DRAWER } from './Drawer.actions' 2 | 3 | const initialState = { 4 | showing: false 5 | } 6 | 7 | export function drawerReducers(state = initialState, action: any) { 8 | switch (action.type) { 9 | case SHOW_DRAWER: 10 | return { 11 | ...state, 12 | showing: true 13 | } 14 | case HIDE_DRAWER: 15 | return { 16 | ...state, 17 | showing: false 18 | } 19 | default: 20 | return state 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/src/App/App.components/Drawer/Drawer.style.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/macro' 2 | 3 | import { primaryColor, textColor, backgroundColor } from '../../../styles' 4 | import { DrawerBackward } from './Drawer.styles/DrawerBackward' 5 | import { DrawerForward } from './Drawer.styles/DrawerForward' 6 | 7 | export const DrawerMask = styled.div` 8 | position: fixed; 9 | z-index: 9; 10 | top: 0; 11 | left: 0; 12 | opacity: 0; 13 | will-change: opacity; 14 | transition: opacity 0.2s ease-in-out; 15 | 16 | &.true { 17 | width: 100vw; 18 | height: 100vh; 19 | opacity: 0.5; 20 | background-color: black; 21 | } 22 | ` 23 | 24 | export const DrawerStyled = styled.div` 25 | position: fixed; 26 | top: 0; 27 | left: 0; 28 | height: 100vh; 29 | z-index: 10; 30 | width: 300px; 31 | max-width: calc(100vw - 20px); 32 | padding: 60px 20px 20px 30px; 33 | background-color: ${backgroundColor}; 34 | box-shadow: 1px 7px 14px -5px rgba(0, 0, 0, 0.2); 35 | transform: translate3d(-300px, 0, 0); 36 | transition: 0.2s ease-in-out; 37 | will-change: transform; 38 | 39 | &.true { 40 | transform: translate3d(0px, 0, 0); 41 | /* animation: ${DrawerForward} 1s linear; 42 | animation-fill-mode: forwards; */ 43 | } 44 | 45 | &.false { 46 | transform: translate3d(-300px, 0, 0); 47 | /* animation: ${DrawerBackward} 1s linear; 48 | animation-fill-mode: forwards; */ 49 | } 50 | ` 51 | 52 | export const DrawerItem = styled.div` 53 | margin-top: 20px; 54 | 55 | > a { 56 | display: inline-block; 57 | font-weight: bold; 58 | line-height: 24px; 59 | display: flex; 60 | } 61 | 62 | &.current-path > a { 63 | color: ${primaryColor}; 64 | } 65 | 66 | > a > svg { 67 | display: inline-block; 68 | width: 24px; 69 | height: 24px; 70 | stroke: ${textColor}; 71 | fill: ${textColor}; 72 | margin-right: 20px; 73 | } 74 | 75 | > a > img { 76 | width: 24px; 77 | height: 24px; 78 | margin-right: 20px; 79 | border-radius: 12px; 80 | } 81 | 82 | &.current-path > a > svg { 83 | stroke: ${primaryColor}; 84 | } 85 | ` 86 | -------------------------------------------------------------------------------- /web/src/App/App.components/Footer/Footer.controller.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { FooterView } from './Footer.view' 4 | 5 | export const Footer: any = () => 6 | -------------------------------------------------------------------------------- /web/src/App/App.components/Footer/Footer.style.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/macro' 2 | import { secondaryColor, tertiaryColor } from '../../../styles' 3 | 4 | export const FooterStyled = styled.div` 5 | padding: 24px 50px; 6 | text-align: center; 7 | background-color: ${secondaryColor}; 8 | color: ${tertiaryColor}; 9 | a { 10 | color: ${tertiaryColor} !important; 11 | } 12 | ` 13 | -------------------------------------------------------------------------------- /web/src/App/App.components/Footer/Footer.view.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { FooterStyled } from './Footer.style' 4 | 5 | export const FooterView: any = () => ( 6 | 7 | Made with love by{' '} 8 | OCTO Technology 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /web/src/App/App.components/Hamburger/Hamburger.controller.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | 4 | import { HamburgerView } from './Hamburger.view' 5 | import { hideDrawer, showDrawer } from '../Drawer/Drawer.actions' 6 | 7 | export const Hamburger = () => { 8 | const dispatch = useDispatch() 9 | const activated = useSelector((state: any) => state.drawer && state.drawer.showing) 10 | 11 | const activateCallback = () => { 12 | dispatch(activated ? hideDrawer() : showDrawer()) 13 | } 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /web/src/App/App.components/Hamburger/Hamburger.style.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/macro' 2 | 3 | import { 4 | textColor, 5 | HamburgerTopForward, 6 | HamburgerBottomBackward, 7 | HamburgerBottomForward, 8 | HamburgerTopBackward 9 | } from '../../../styles' 10 | 11 | export const HamburgerStyled = styled.div` 12 | position: fixed; 13 | left: 18px; 14 | top: 23px; 15 | overflow: visible; 16 | margin: 0; 17 | height: 14px; 18 | box-sizing: content-box; 19 | cursor: pointer; 20 | z-index: 11; 21 | ` 22 | 23 | export const HamburgerBox = styled.div` 24 | position: relative; 25 | display: inline-block; 26 | width: 24px; 27 | height: 14px; 28 | ` 29 | 30 | export const HamburgerInner = styled.div` 31 | position: absolute; 32 | width: 24px; 33 | height: 1px; 34 | border-radius: 1px; 35 | will-change: transform; 36 | background-color: ${textColor}; 37 | ` 38 | 39 | export const HamburgerInnerTop = styled.div` 40 | position: absolute; 41 | width: 24px; 42 | height: 1px; 43 | border-radius: 1px; 44 | will-change: transform; 45 | background-color: ${textColor}; 46 | top: 0; 47 | 48 | &.true { 49 | animation: ${HamburgerTopForward} 1s linear; 50 | animation-fill-mode: forwards; 51 | } 52 | 53 | &.false { 54 | animation: ${HamburgerTopBackward} 1s linear; 55 | animation-fill-mode: forwards; 56 | } 57 | ` 58 | 59 | export const HamburgerInnerMiddle = styled.div` 60 | position: absolute; 61 | width: 24px; 62 | height: 1px; 63 | border-radius: 1px; 64 | will-change: transform; 65 | background-color: ${textColor}; 66 | display: block; 67 | top: calc(50% - 1px); 68 | ` 69 | 70 | export const HamburgerInnerBottom = styled.div` 71 | position: absolute; 72 | width: 24px; 73 | height: 1px; 74 | border-radius: 1px; 75 | will-change: transform; 76 | background-color: ${textColor}; 77 | bottom: 1px; 78 | 79 | &.true { 80 | animation: ${HamburgerBottomForward} 1s linear; 81 | animation-fill-mode: forwards; 82 | } 83 | 84 | &.false { 85 | animation: ${HamburgerBottomBackward} 1s linear; 86 | animation-fill-mode: forwards; 87 | } 88 | ` 89 | -------------------------------------------------------------------------------- /web/src/App/App.components/Hamburger/Hamburger.view.tsx: -------------------------------------------------------------------------------- 1 | import * as PropTypes from 'prop-types' 2 | import * as React from 'react' 3 | 4 | import { 5 | HamburgerBox, 6 | HamburgerInnerBottom, 7 | HamburgerInnerMiddle, 8 | HamburgerInnerTop, 9 | HamburgerStyled 10 | } from './Hamburger.style' 11 | 12 | type HamburgerViewProps = { 13 | activated: boolean 14 | activateCallback: () => void 15 | } 16 | 17 | export const HamburgerView = ({ activated, activateCallback }: HamburgerViewProps) => ( 18 | activateCallback()}> 19 | 20 | 21 | 22 | 23 | 24 | 25 | ) 26 | 27 | HamburgerView.propTypes = { 28 | activated: PropTypes.bool, 29 | activateCallback: PropTypes.func 30 | } 31 | 32 | HamburgerView.defaultProps = { 33 | activated: false 34 | } 35 | -------------------------------------------------------------------------------- /web/src/App/App.components/Header/Header.controller.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | 4 | import { HeaderView } from './Header.view' 5 | 6 | export const Header = () => { 7 | const dispatch = useDispatch() 8 | const user = useSelector((state: any) => (state && state.auth ? state.auth.user : {})) 9 | const router = useSelector((state: any) => state.router) 10 | 11 | function removeAuthUserCallback() { 12 | dispatch(null) 13 | } 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /web/src/App/App.components/Header/Header.style.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/macro' 2 | import { backgroundColor, shadowColor } from 'src/styles' 3 | 4 | export const HeaderStyled = styled.div` 5 | z-index: 10; 6 | height: 64px; 7 | background-color: ${backgroundColor}; 8 | position: fixed; 9 | top: 0; 10 | width: 100vw; 11 | box-shadow: 0 6px 10px ${shadowColor}80; 12 | 13 | .header-triangle { 14 | position: absolute; 15 | top: 64px; 16 | left: calc(50vw - 70px); 17 | width: 0; 18 | height: 0; 19 | border-left: 70px solid transparent; 20 | border-right: 70px solid transparent; 21 | border-top: 40px solid ${backgroundColor}; 22 | filter: drop-shadow(0 4px 5px ${shadowColor}); 23 | } 24 | ` 25 | 26 | export const HeaderMenu = styled.div` 27 | position: absolute; 28 | height: 64px; 29 | top: 4px; 30 | left: 60px; 31 | line-height: 64px; 32 | 33 | @media (max-width: 900px) { 34 | display: none; 35 | } 36 | ` 37 | 38 | export const HeaderLogo = styled.div` 39 | position: absolute; 40 | top: 13px; 41 | left: calc(50vw - 30px); 42 | 43 | img { 44 | height: 68px; 45 | margin-top: 5px; 46 | filter: drop-shadow(0 4px 4px rgba(0, 0, 0, 0.25)); 47 | } 48 | ` 49 | 50 | export const HeaderLoggedOut = styled.div` 51 | position: absolute; 52 | top: 14px; 53 | right: 14px; 54 | display: grid; 55 | grid-template-columns: 130px 120px; 56 | grid-gap: 10px; 57 | 58 | @media (max-width: 900px) { 59 | display: none; 60 | } 61 | ` 62 | -------------------------------------------------------------------------------- /web/src/App/App.components/Header/Header.view.tsx: -------------------------------------------------------------------------------- 1 | import * as PropTypes from 'prop-types' 2 | import * as React from 'react' 3 | import { Link } from 'react-router-dom' 4 | 5 | import { Button } from '../Button/Button.controller' 6 | // prettier-ignore 7 | import { 8 | HeaderLoggedOut, 9 | HeaderMenu, 10 | HeaderStyled, 11 | HeaderLogo 12 | } from "./Header.style" 13 | 14 | export const HeaderView: any = () => { 15 | return ( 16 | 17 | 18 | 19 | Tezos Link 20 | 21 | 22 | 23 |
24 | 25 | 26 | entire stack 27 | 28 | 29 | 30 | {loggedOutHeader()} 31 |
32 | ) 33 | } 34 | 35 | function loggedOutHeader() { 36 | return ( 37 | 38 | 39 |