Skip to content

Commit

Permalink
feat(idea/frontend): add code verifier (#1707)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitayutanov authored Jan 27, 2025
1 parent 8bea095 commit 11614ec
Show file tree
Hide file tree
Showing 51 changed files with 845 additions and 137 deletions.
11 changes: 2 additions & 9 deletions .github/workflows/release-gear-idea.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ jobs:
VITE_INDEXER_API_URL=${{ secrets.VITE_INDEXER_API_URL }}
VITE_TESTNET_DNS_API_URL=${{ secrets.VITE_TESTNET_DNS_API_URL }}
VITE_MAINNET_DNS_API_URL=${{ secrets.VITE_MAINNET_DNS_API_URL }}
VITE_CODE_VERIFIER_API_URL=${{ secrets.VITE_CODE_VERIFIER_API_URL }}
build-faucet-image:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -210,14 +211,7 @@ jobs:
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-gear-idea-explorer:${{ env.ENVIRONMENT }}

deploy-to-k8s:
needs:
[
build-frontend-image,
build-faucet-image,
build-meta-storage,
build-squid-image,
build-squid-explorer
]
needs: [build-frontend-image, build-faucet-image, build-meta-storage, build-squid-image, build-squid-explorer]
runs-on: ubuntu-latest

steps:
Expand All @@ -239,7 +233,6 @@ jobs:
echo "deployments=squid-testnet-v2 explorer frontend-nginx meta-storage faucet"
fi
- name: Deploy to k8s
uses: sergeyfilyanin/kubectl-aws-eks@master
with:
Expand Down
2 changes: 1 addition & 1 deletion idea/gear/common/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "gear-idea-common",
"private": true,
"version": "1.0.0",
"version": "1.1.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
Expand Down
6 changes: 3 additions & 3 deletions idea/gear/explorer/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "gear-idea-explorer",
"version": "1.0.0",
"version": "1.1.0",
"private": true,
"dependencies": {
"class-validator": "0.14.1",
"cron": "^3.1.7",
"dotenv": "^16.4.5",
"express": "4.19.2",
"gear-idea-common": "1.0.0",
"gear-idea-indexer-db": "1.0.0",
"gear-idea-common": "1.1.0",
"gear-idea-indexer-db": "1.1.0",
"nanoid": "^5.0.7",
"pg": "8.12.0",
"redis": "^4.6.15",
Expand Down
4 changes: 2 additions & 2 deletions idea/gear/faucet/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "gear-idea-faucet",
"private": true,
"version": "1.0.0",
"version": "1.1.0",
"main": "server.js",
"scripts": {
"build": "rm -rf dist && npx tsc",
Expand All @@ -20,7 +20,7 @@
"cron": "^3.1.6",
"dotenv": "10.0.0",
"express": "4.18.1",
"gear-idea-common": "1.0.0",
"gear-idea-common": "1.1.0",
"hcaptcha": "0.1.1",
"nodemon": "2.0.16",
"pg": "8.7.1",
Expand Down
1 change: 1 addition & 0 deletions idea/gear/frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ VITE_GTM_ID=
VITE_DEFAULT_TRANSFER_BALANCE_VALUE=
VITE_MAINNET_DNS_API_URL=
VITE_TESTNET_DNS_API_URL=
VITE_CODE_VERIFIER_API_URL=
6 changes: 4 additions & 2 deletions idea/gear/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ ARG VITE_NODE_ADDRESS \
VITE_TESTNET_VOUCHERS_API_URL \
VITE_INDEXER_API_URL \
VITE_MAINNET_DNS_API_URL \
VITE_TESTNET_DNS_API_URL
VITE_TESTNET_DNS_API_URL \
VITE_CODE_VERIFIER_API_URL

ENV VITE_NODE_ADDRESS=${VITE_NODE_ADDRESS} \
VITE_NODES_API_URL=${VITE_NODES_API_URL} \
Expand All @@ -30,7 +31,8 @@ ENV VITE_NODE_ADDRESS=${VITE_NODE_ADDRESS} \
VITE_TESTNET_VOUCHERS_API_URL=${VITE_TESTNET_VOUCHERS_API_URL} \
VITE_INDEXER_API_URL=${VITE_INDEXER_API_URL} \
VITE_MAINNET_DNS_API_URL=${VITE_MAINNET_DNS_API_URL} \
VITE_TESTNET_DNS_API_URL=${VITE_TESTNET_DNS_API_URL}
VITE_TESTNET_DNS_API_URL=${VITE_TESTNET_DNS_API_URL} \
VITE_CODE_VERIFIER_API_URL=${VITE_CODE_VERIFIER_API_URL}

RUN yarn build:gear-idea-frontend

Expand Down
4 changes: 2 additions & 2 deletions idea/gear/frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gear-idea-frontend",
"version": "1.0.0",
"version": "1.1.0",
"private": true,
"scripts": {
"start": "npx vite --open --port 3000",
Expand Down Expand Up @@ -38,7 +38,7 @@
"react-gtm-module": "2.0.11",
"react-hook-form": "7.52.2",
"react-number-format": "5.3.1",
"react-router-dom": "6.16.0",
"react-router-dom": "6.28.2",
"react-transition-group": "4.4.5",
"sails-js": "0.3.0",
"sails-js-parser": "0.1.0",
Expand Down
9 changes: 9 additions & 0 deletions idea/gear/frontend/src/features/code-verifier/api/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const API_URL = import.meta.env.VITE_CODE_VERIFIER_API_URL as string;

const METHOD = {
VERIFY: 'verify',
VERIFY_STATUS: 'verify/status',
CODE: 'code',
} as const;

export { API_URL, METHOD };
54 changes: 54 additions & 0 deletions idea/gear/frontend/src/features/code-verifier/api/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { HexString } from '@gear-js/api';
import { useAlert } from '@gear-js/react-hooks';
import { useMutation, useQuery } from '@tanstack/react-query';
import { STATUS_CODES } from 'http';
import { useEffect } from 'react';

import { getVerificationStatus, getVerifiedCode, verifyCode } from './requests';

function useVerifyCode() {
return useMutation({
mutationKey: ['verify-code'],
mutationFn: verifyCode,
});
}

function useIsCodeVerified(codeId: HexString | null | undefined) {
const alert = useAlert();

const query = useQuery({
queryKey: ['code-verification-status', codeId],
queryFn: () => getVerifiedCode(codeId!),
select: (response) => Boolean(response),
enabled: Boolean(codeId),
});

const { error } = query;

useEffect(() => {
if (error && error.message !== STATUS_CODES[404]) alert.error(error.message);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [error]);

return query;
}

function useVerificationStatus(id: string) {
const alert = useAlert();

const query = useQuery({
queryKey: ['verification-status', id],
queryFn: () => getVerificationStatus(id),
});

const { error } = query;

useEffect(() => {
if (error) alert.error(error.message);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [error]);

return query;
}

export { useVerifyCode, useIsCodeVerified, useVerificationStatus };
3 changes: 3 additions & 0 deletions idea/gear/frontend/src/features/code-verifier/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useVerifyCode, useIsCodeVerified, useVerificationStatus } from './hooks';

export { useVerifyCode, useIsCodeVerified, useVerificationStatus };
13 changes: 13 additions & 0 deletions idea/gear/frontend/src/features/code-verifier/api/requests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { fetchWithGuard } from '@/shared/helpers';
import { CodeResponse, StatusResponse, VerifyParameters, VerifyResponse } from './types';
import { API_URL } from './consts';

const verifyCode = (parameters: VerifyParameters) =>
fetchWithGuard<VerifyResponse>(`${API_URL}/verify`, 'POST', parameters);

const getVerificationStatus = (id: string) =>
fetchWithGuard<StatusResponse>(`${API_URL}/verify/status?id=${id}`, 'GET');

const getVerifiedCode = (id: string) => fetchWithGuard<CodeResponse>(`${API_URL}/code?id=${id}`, 'GET');

export { verifyCode, getVerificationStatus, getVerifiedCode };
29 changes: 29 additions & 0 deletions idea/gear/frontend/src/features/code-verifier/api/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { HexString } from '@gear-js/api';

type VerifyParameters = {
build_idl: boolean;
code_id: HexString;
network: string;
project: { Name: string } | { PathToCargoToml: string };
repo_link: string;
version: string;
};

type VerifyResponse = {
id: string;
};

type StatusResponse = {
status: 'pending' | 'verified' | 'failed' | 'in_progress';
failed_reason: string | null;
created_at: number;
};

type CodeResponse = {
id: string;
idl_hash: string | null;
name: string;
repo_link: string;
};

export type { VerifyParameters, VerifyResponse, StatusResponse, CodeResponse };
10 changes: 10 additions & 0 deletions idea/gear/frontend/src/features/code-verifier/assets/refresh.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { VerifyLink } from './verify-link';
import { VerificationStatus } from './verification-status';
import { VerifyForm } from './verify-form';

export { VerifyLink, VerificationStatus, VerifyForm };
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { VerificationStatus } from './verification-status';

export { VerificationStatus };
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.status {
padding: 4px 8px;

font-size: 12px;
font-weight: 600;
line-height: 15.6px;
white-space: nowrap;

border-radius: 20px;
}

.verified {
color: #2bd071;

background-color: rgba(#2bd071, 0.1);
}

.in_progress,
.pending {
color: #f2c94c;

background-color: rgba(#f2c94c, 0.1);
}

.failed {
color: #f24a4a;

background-color: rgba(#f24a4a, 0.1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import clsx from 'clsx';

import { Skeleton } from '@/shared/ui';

import styles from './verification-status.module.scss';

type Props = {
value: 'verified' | 'failed' | 'pending' | 'in_progress';
};

const TEXT = {
verified: 'Verified',
failed: 'Failed',
pending: 'Pending',
in_progress: 'In Progress',
} as const;

function VerificationStatus({ value }: Props) {
return <span className={clsx(styles.status, styles[value])}>{TEXT[value]}</span>;
}

function VerificationStatusSkeleton({ disabled }: { disabled: boolean }) {
return <Skeleton width="5rem" className={styles.status} disabled={disabled} />;
}

VerificationStatus.Skeleton = VerificationStatusSkeleton;

export { VerificationStatus };
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { z } from 'zod';

import { GENESIS } from '@/shared/config';

import { isCodeIdValid } from '../../utils';

const FIELD_NAME = {
DOCKER_IMAGE_VERSION: 'version',
CODE_ID: 'codeId',
REPO_LINK: 'repoLink',
PROJECT_ID_TYPE: 'projectIdType',
PROJECT_ID: 'projectId',
NETWORK: 'network',
BUILD_IDL: 'buildIdl',
} as const;

const NETWORK = {
[GENESIS.MAINNET]: 'vara_mainnet',
[GENESIS.TESTNET]: 'vara_testnet',
} as const;

const NETWORK_OPTIONS = [
{ label: 'Mainnet', value: NETWORK[GENESIS.MAINNET] },
{ label: 'Testnet', value: NETWORK[GENESIS.TESTNET] },
] as const;

const PROJECT_ID_TYPE = {
NAME: 'name',
CARGO_TOML_PATH: 'cargoTomlPath',
} as const;

const DEFAULT_VALUES = {
[FIELD_NAME.DOCKER_IMAGE_VERSION]: '',
[FIELD_NAME.CODE_ID]: '',
[FIELD_NAME.REPO_LINK]: '',
[FIELD_NAME.PROJECT_ID_TYPE]: PROJECT_ID_TYPE.NAME as (typeof PROJECT_ID_TYPE)[keyof typeof PROJECT_ID_TYPE],
[FIELD_NAME.PROJECT_ID]: '',
[FIELD_NAME.NETWORK]: NETWORK_OPTIONS[0].value as (typeof NETWORK)[keyof typeof NETWORK],
[FIELD_NAME.BUILD_IDL]: false,
};

const SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
const GITHUB_REPO_URL_REGEX = /^https?:\/\/(www\.)?github\.com\/([\w-]+)\/([\w-]+)(\/.*)?$/;
const CARGO_TOML_PATH_REGEX = /^(?:\.\/)?(?:[^/]+\/)*Cargo\.toml$/;

const SCHEMA = z
.object({
[FIELD_NAME.DOCKER_IMAGE_VERSION]: z
.string()
.trim()
.refine((value) => SEMVER_REGEX.test(value), { message: 'Invalid version format' }),

[FIELD_NAME.CODE_ID]: z
.string()
.trim()
.refine((value) => isCodeIdValid(value), { message: 'Invalid hex' }),

[FIELD_NAME.REPO_LINK]: z
.string()
.trim()
.refine((value) => GITHUB_REPO_URL_REGEX.test(value), { message: 'Invalid GitHub repository URL' }),

[FIELD_NAME.PROJECT_ID_TYPE]: z.string(),
[FIELD_NAME.PROJECT_ID]: z.string().trim().min(1),
[FIELD_NAME.NETWORK]: z.string(),
[FIELD_NAME.BUILD_IDL]: z.boolean(),
})
.refine(
({ projectIdType, projectId }) =>
projectIdType === PROJECT_ID_TYPE.CARGO_TOML_PATH ? CARGO_TOML_PATH_REGEX.test(projectId) : true,
{
message: 'Invalid path to Cargo.toml',
path: [FIELD_NAME.PROJECT_ID],
},
);

export { DEFAULT_VALUES, SCHEMA, NETWORK, FIELD_NAME, PROJECT_ID_TYPE, NETWORK_OPTIONS };
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { VerifyForm } from './verify-form';

export { VerifyForm };
Loading

0 comments on commit 11614ec

Please sign in to comment.