Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beta #536

Closed
wants to merge 8 commits into from
Closed

Beta #536

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/beta-build-images.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build Docker Images

on:
push:
branches: ['beta']

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: linux/amd64,linux/arm64

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Docker Login
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push (APP)
uses: docker/build-push-action@v5
with:
context: .
file: apps/app/Dockerfile
platforms: linux/amd64
push: true
tags: ghcr.io/nearblocks/app:beta
cache-from: type=registry,ref=ghcr.io/nearblocks/app:beta
cache-to: type=inline
2 changes: 2 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"geoip-lite": "1.4.10",
"helmet": "7.1.0",
"ioredis": "5.4.1",
"ip": "^2.0.1",
"lodash-es": "4.17.21",
"near-api-js": "1.1.0",
"near-contract-parser": "1.0.0",
Expand All @@ -50,6 +51,7 @@
"@types/cors": "~2.8",
"@types/express": "~4.17",
"@types/geoip-lite": "~1.4",
"@types/ip": "^1.1.3",
"@types/lodash-es": "~4.17",
"@types/node": "~20.8",
"@types/passport-anonymous": "~1.0",
Expand Down
22 changes: 22 additions & 0 deletions apps/api/src/middlewares/rateLimiter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NextFunction, Request, Response } from 'express';
import ip from 'ip';
import { RateLimiterRedis, RateLimiterUnion } from 'rate-limiter-flexible';

import config from '#config';
Expand Down Expand Up @@ -36,11 +37,22 @@ const FREE_PLAN: Plan = {
const KITWALLET_PATH = '/v1/kitwallet';
const SEARCH_PATH = '/v1/search';

const SUBNETS = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];

const rateLimiter = catchAsync(
async (req: Request, res: Response, next: NextFunction) => {
const id = (req.user as User)?.id;
const keyId = (req.user as User)?.key_id;
const date = dayjs.utc().toISOString();

const reqIp = req.ip;

const ipAddress: string =
reqIp && reqIp.startsWith('::ffff:') ? reqIp.slice(7) : reqIp || '';

if (checkIPInSubnets(ipAddress)) {
return next();
}
// Handle rate limit for app
const authHeader = req.headers.authorization || '';
const token = authHeader.replace('Bearer ', '');
Expand Down Expand Up @@ -125,6 +137,16 @@ const rateLimiter = catchAsync(
},
);

const checkIPInSubnets = (ipAddress: string): boolean => {
for (const subnet of SUBNETS) {
if (ip.cidrSubnet(subnet).contains(ipAddress)) {
return true;
}
}

return false;
};

const useFreePlan = async (
req: Request,
res: Response,
Expand Down
43 changes: 0 additions & 43 deletions apps/app/i18n.js

This file was deleted.

1 change: 1 addition & 0 deletions apps/app/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
7 changes: 5 additions & 2 deletions apps/app/next.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const path = require('path');
const { configureRuntimeEnv } = require('next-runtime-env/build/configure');
const nextTranslate = require('next-translate-plugin');

const withNextIntl = require('next-intl/plugin')();

configureRuntimeEnv();

/** @type {import('next').NextConfig} */
const nextConfig = nextTranslate({
const nextConfig = withNextIntl({
reactStrictMode: true,
poweredByHeader: false,
optimizeFonts: false,
Expand Down Expand Up @@ -49,6 +51,7 @@ module.exports = nextConfig;

const { withSentryConfig } = require('@sentry/nextjs');

// Skip all paths that should not be internationalized
module.exports = withSentryConfig(module.exports, {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options
Expand Down
2 changes: 2 additions & 0 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
"near-api-js": "5.0.0",
"near-social-vm": "github:NearSocial/VM#2.5.5",
"nearblock-translations": "https://github.com/Nearblocks/translations.git#516d489658dea178226e90f1707935e970598c51",
"nearblocks-trans-next-intl": "https://github.com/kevin-devstack/translations.git#c23d13c6e049ad5721dfd71a8f0cbd8e6725c6ae",
"next": "14.2.5",
"next-intl": "^3.19.4",
"next-runtime-env": "1.x",
"next-sitemap": "4.2.3",
"next-themes": "^0.3.0",
Expand Down
120 changes: 120 additions & 0 deletions apps/app/src/app/[locale]/address/[id]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import Buttons from '@/components/app/Icons/Button';
import SponserdText from '@/components/app/SponserdText';
import FaCheckCircle from '@/components/Icons/FaCheckCircle';
import ListCheck from '@/components/Icons/ListCheck';
import { appUrl, networkId } from '@/utils/app/config';
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
import dynamic from 'next/dynamic';

const network = process.env.NEXT_PUBLIC_NETWORK_ID;
const ogUrl = process.env.NEXT_PUBLIC_OG_URL;

export async function generateMetadata({
params,
}: {
params: { hash: string; locale: string };
}) {
unstable_setRequestLocale(params?.locale);

const t = await getTranslations({ locale: params.locale });

const thumbnail = `${ogUrl}/thumbnail/basic?title=${encodeURIComponent(
t('block.heading', { block: params.hash }) || 'Latest Near Protocol Blocks',
)}&brand=near`;

const metaTitle =
t('block.metaTitle', { block: params.hash }) ||
'All Near Latest Protocol Blocks | NearBlocks';
const metaDescription =
t('block.metaDescription', { block: params.hash }) ||
'All Near (Ⓝ Blocks that are included in Near blockchain. The timestamp, author, gas used, gas price and included transactions are shown.';

return {
title: `${network === 'testnet' ? 'TESTNET ' : ''}${metaTitle}`,
description: metaDescription,
openGraph: {
title: metaTitle,
description: metaDescription,
images: [thumbnail],
},
twitter: {
title: metaTitle,
description: metaDescription,
images: [thumbnail],
},
alternates: {
canonical: `${appUrl}/blocks/${params.hash}`,
},
};
}

export default function HaseLayout({
params: { id },
children,
}: {
params: { id: string };
children: React.ReactNode;
}) {
const RpcMenu = dynamic(
() => import('../../../../components/app/Layouts/RpcMenu'),
{
ssr: false,
},
);
return (
<>
<div className="relative container mx-auto px-3 mb-10">
<div className="flex items-center justify-between flex-wrap pt-4">
<div className="flex md:flex-wrap w-full">
<div className="flex justify-between md:items-center dark:text-neargray-10 border-b w-full mb-5 dark:border-black-200">
<h1 className="py-2 break-all space-x-2 text-xl text-gray-700 leading-8 px-2 ">
Near Account:&nbsp;
{id && (
<span className="text-green-500 dark:text-green-250">
@<span className="font-semibold">{id}</span>
</span>
)}
<Buttons address={id as string} />
</h1>
<ul className="flex relative md:pt-0 pt-2 items-center text-gray-500 text-xs">
<RpcMenu />
<li className="ml-3 max-md:mb-2">
<span className="group flex w-full relative h-full">
<a
className={`md:flex justify-center w-full hover:text-green-500 dark:hover:text-green-250 hover:no-underline px-0 py-1`}
href="#"
>
<div className="py-2 px-2 h-8 bg-gray-100 dark:bg-black-200 rounded border">
<ListCheck className="h-4 dark:filter dark:invert" />
</div>
</a>
<ul className="bg-white dark:bg-black-600 soft-shadow hidden min-w-full absolute top-full right-0 rounded-lg group-hover:block py-1 z-[99]">
<li className="pb-2">
<a
className={`flex items-center whitespace-nowrap px-2 pt-2 hover:text-green-400 dark:text-neargray-10 dark:hover:text-green-250`}
href={`https://validate.nearblocks.io/address/${id}?network=${networkId}`}
target="_blank"
>
Validate Account
<span className="w-4 ml-3 dark:text-green-250">
<FaCheckCircle />
</span>
</a>
</li>
</ul>
</span>
</li>
</ul>
</div>
<div className="container mx-auto pl-2 pb-6 text-nearblue-600">
<div className="min-h-[80px] md:min-h-[25px]">
<SponserdText />
</div>
</div>
</div>
</div>
{children}
</div>
</>
);
}
76 changes: 76 additions & 0 deletions apps/app/src/app/[locale]/address/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use client';
import classNames from 'classnames';
import { useSearchParams } from 'next/navigation';
import BalanceSkeleton from '@/components/app/skeleton/address/balance';
import TabPanelGeneralSkeleton from '@/components/app/skeleton/address/dynamicTab';
import { Link } from '@/i18n/routing';
import { useTranslations } from 'next-intl';

const tabs = [
{ name: 'txns', message: 'Transactions', label: 'Transactions' },
{ name: 'receipts', message: 'Receipts', label: 'Receipts' },
{ name: 'tokentxns', message: 'tokenTxns', label: 'Token Txns' },
{ name: 'nfttokentxns', message: 'nftTokenTxns', label: 'NFT Token Txns' },
{ name: 'accesskeys', message: 'accessKeys', label: 'Access Keys' },
{ name: 'contract', message: 'contract', label: 'Contract' },
];
export default function AddressLoading() {
const t = useTranslations();
const searchParams = useSearchParams();
const tab = searchParams?.get('tab');

const getClassName = (selected: boolean) =>
classNames(
'text-xs leading-4 font-medium overflow-hidden inline-block cursor-pointer p-2 mb-3 mr-2 focus:outline-none rounded-lg',
{
'hover:bg-neargray-800 bg-neargray-700 dark:bg-black-200 hover:text-nearblue-600 text-nearblue-600 dark:text-neargray-10':
!selected,
'bg-green-600 dark:bg-green-250 text-white': selected,
},
);
return (
<>
<BalanceSkeleton />
<div className="py-6"></div>
<div className="block lg:flex lg:space-x-2 mb-10">
<div className="w-full ">
<div className="flex flex-wrap ">
{tabs?.map(({ name, label, message }: any) => {
return (
<Link
key={name}
href={`#`}
className={getClassName(name === tab)}
>
<h2>{t(`${message}`) || label}</h2>
</Link>
);
})}
</div>
<div className="bg-white dark:bg-black-600 soft-shadow rounded-xl pb-1 w-full">
{!tab || tab === 'txns' ? (
<TabPanelGeneralSkeleton tab={tab || 'txns'} />
) : null}
{!tab || tab === 'receipts' ? (
<TabPanelGeneralSkeleton tab={tab || 'receipts'} />
) : null}

{tab === 'tokentxns' ? <TabPanelGeneralSkeleton tab={tab} /> : null}

{tab === 'nfttokentxns' ? (
<TabPanelGeneralSkeleton tab={tab} />
) : null}

{tab === 'accesskeys' ? (
<TabPanelGeneralSkeleton tab={tab} />
) : null}
{tab === 'contract' ? (
<TabPanelGeneralSkeleton tab={'contract'} />
) : null}
</div>
</div>
</div>
<div className="mb-10"></div>
</>
);
}
Loading