diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 89b48fd2..9d8d1cf0 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,5 +1,5 @@ import { CacheModule, CacheStore } from '@nestjs/cache-manager'; -import { Module } from '@nestjs/common'; +import { Logger, Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { SentryModule } from '@sentry/nestjs/setup'; import type { RedisClientOptions } from 'redis'; @@ -14,6 +14,8 @@ import { AppController } from './app.controller'; import { BootstrapService } from './bootstrap.service'; import { EventEmitterModule } from '@nestjs/event-emitter'; +const logger = new Logger('CacheStore'); + async function createCacheStore(redisUrl: string) { const store = await redisStore({ url: redisUrl, @@ -21,10 +23,20 @@ async function createCacheStore(redisUrl: string) { }); return { async set(key: string, value: T, ttl?: number): Promise { - return store.set(key, value, ttl); + try { + return store.set(key, value, ttl); + } catch (e) { + logger.error(`Failed to set cache key ${key}: ${e}`); + return undefined; + } }, async get(key: string): Promise { - return store.get(key); + try { + return store.get(key); + } catch (e) { + logger.error(`Failed to get cache key ${key}: ${e}`); + return undefined; + } }, async del(key: string): Promise { return store.del(key); diff --git a/backend/src/common/date.ts b/backend/src/common/date.ts index 5f0f0121..8db849c8 100644 --- a/backend/src/common/date.ts +++ b/backend/src/common/date.ts @@ -1,4 +1,3 @@ -export const ONE_MONTH_MS = 30 * 24 * 60 * 60 * 1000; export const ONE_HOUR_MS = 60 * 60 * 1000; export const TEN_MINUTES_MS = 10 * 60 * 1000; export const ONE_DAY_MS = 24 * 60 * 60 * 1000; diff --git a/backend/src/core/bitcoin-api/bitcoin-api.service.ts b/backend/src/core/bitcoin-api/bitcoin-api.service.ts index cc4f5945..53b00076 100644 --- a/backend/src/core/bitcoin-api/bitcoin-api.service.ts +++ b/backend/src/core/bitcoin-api/bitcoin-api.service.ts @@ -7,7 +7,7 @@ import { IBitcoinDataProvider } from './bitcoin-api.interface'; import { ElectrsService } from './provider/electrs.service'; import { MempoolService } from './provider/mempool.service'; import { ChainInfo, Transaction } from './bitcoin-api.schema'; -import { ONE_HOUR_MS, ONE_MONTH_MS, TEN_MINUTES_MS } from 'src/common/date'; +import { ONE_HOUR_MS, ONE_DAY_MS, TEN_MINUTES_MS } from 'src/common/date'; import { Cacheable } from 'src/decorators/cacheable.decorator'; import * as Sentry from '@sentry/nestjs'; import { PLimit } from 'src/decorators/plimit.decorator'; @@ -206,7 +206,7 @@ export class BitcoinApiService { @Cacheable({ namespace: 'bitcoinApiService', key: ({ txid }) => `getTx:${txid}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, shouldCache: (tx: Transaction) => tx.status.confirmed && !!tx.status.block_time && @@ -223,7 +223,7 @@ export class BitcoinApiService { @Cacheable({ namespace: 'bitcoinApiService', key: ({ txid }) => `getTxOutSpends:${txid}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getTxOutSpends({ txid }: { txid: string }) { return this.call('getTxOutSpends', { txid }); @@ -241,7 +241,7 @@ export class BitcoinApiService { @Cacheable({ namespace: 'bitcoinApiService', key: ({ hash }) => `getBlock:${hash}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getBlock({ hash }: { hash: string }) { return this.call('getBlock', { hash }); @@ -250,7 +250,7 @@ export class BitcoinApiService { @Cacheable({ namespace: 'bitcoinApiService', key: ({ hash, startIndex }) => `getBlockTxs:${hash}:${startIndex}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getBlockTxs({ hash, startIndex }: { hash: string; startIndex?: number }) { return this.call('getBlockTxs', { hash, startIndex }); @@ -268,7 +268,7 @@ export class BitcoinApiService { @Cacheable({ namespace: 'bitcoinApiService', key: ({ hash }) => `getBlockTxids:${hash}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getBlockTxids({ hash }: { hash: string }) { return this.call('getBlockTxids', { hash }); diff --git a/backend/src/core/blockchain/blockchain.service.ts b/backend/src/core/blockchain/blockchain.service.ts index 6ff3ac60..c093b778 100644 --- a/backend/src/core/blockchain/blockchain.service.ts +++ b/backend/src/core/blockchain/blockchain.service.ts @@ -10,7 +10,7 @@ import { TransactionWithStatusResponse, } from './blockchain.interface'; import { Cacheable } from 'src/decorators/cacheable.decorator'; -import { ONE_MONTH_MS } from 'src/common/date'; +import { ONE_DAY_MS } from 'src/common/date'; import { CKB_MIN_SAFE_CONFIRMATIONS } from 'src/constants'; import * as Sentry from '@sentry/nestjs'; import { Chain } from '@prisma/client'; @@ -118,7 +118,7 @@ export class BlockchainService { } return key; }, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, shouldCache: async (tx: TransactionWithStatusResponse, that: BlockchainService) => { if (tx.tx_status.status !== 'committed' || !tx.tx_status.block_number) { return false; @@ -157,7 +157,7 @@ export class BlockchainService { } return key; }, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, shouldCache: async (block: Block, that: BlockchainService) => { if (!block?.header) { return false; @@ -202,7 +202,7 @@ export class BlockchainService { } return key; }, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, shouldCache: async (block: Block, that: BlockchainService) => { const { number } = block.header; return that.isSafeConfirmations(number); @@ -235,7 +235,7 @@ export class BlockchainService { @Cacheable({ namespace: 'BlockchainService', key: (blockHash: string) => `getBlockEconomicState:${blockHash}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getBlockEconomicState(blockHash: string): Promise { await this.websocketReady; diff --git a/backend/src/core/ckb-explorer/ckb-explorer.service.ts b/backend/src/core/ckb-explorer/ckb-explorer.service.ts index dbeaba14..7195512e 100644 --- a/backend/src/core/ckb-explorer/ckb-explorer.service.ts +++ b/backend/src/core/ckb-explorer/ckb-explorer.service.ts @@ -19,7 +19,7 @@ import { TransactionFeesStatistic, TransactionListSortType, } from './ckb-explorer.interface'; -import { ONE_MONTH_MS } from 'src/common/date'; +import { ONE_DAY_MS } from 'src/common/date'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; import { Cacheable } from 'src/decorators/cacheable.decorator'; import { CkbRpcWebsocketService } from '../ckb-rpc/ckb-rpc-websocket.service'; @@ -132,7 +132,7 @@ export class CkbExplorerService { @Cacheable({ namespace: 'CkbExplorerService', key: (heightOrHash: string) => `getBlock:${heightOrHash}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, shouldCache: async (block: NonPaginatedResponse, that: CkbExplorerService) => { const { number } = block.data.attributes; return that.isSafeConfirmations(number); @@ -192,7 +192,7 @@ export class CkbExplorerService { @Cacheable({ namespace: 'CkbExplorerService', key: (txHash: string) => `getTransaction:${txHash}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, shouldCache: async (tx: NonPaginatedResponse, that: CkbExplorerService) => { const { tx_status, block_number } = tx.data.attributes; const isSafeConfirmations = await that.isSafeConfirmations(block_number); diff --git a/backend/src/core/core.service.ts b/backend/src/core/core.service.ts index 7ab16874..b67070d9 100644 --- a/backend/src/core/core.service.ts +++ b/backend/src/core/core.service.ts @@ -14,7 +14,7 @@ import { Env } from 'src/env'; import { Transaction } from './blockchain/blockchain.interface'; import { BlockchainServiceFactory } from './blockchain/blockchain.factory'; import { LeapDirection } from '@prisma/client'; -import { ONE_MONTH_MS } from 'src/common/date'; +import { ONE_DAY_MS } from 'src/common/date'; import { Cacheable } from 'src/decorators/cacheable.decorator'; export const CELLBASE_TX_HASH = @@ -79,7 +79,7 @@ export class CoreService { key: (chainId: number, ckbTx: Transaction) => { return `getLeapDirectionByCkbTx:${chainId}:${ckbTx.hash}`; }, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getLeapDirectionByCkbTx(chainId: number, ckbTx: Transaction) { const blockchainService = this.blockchainServiceFactory.getService(chainId); diff --git a/backend/src/main.ts b/backend/src/main.ts index a41f60e6..c085b069 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -4,8 +4,9 @@ import { AppModule } from './app.module'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import { envSchema } from './env'; import { BootstrapService } from './bootstrap.service'; -import { LogLevel } from '@nestjs/common'; +import { Logger, LogLevel } from '@nestjs/common'; import { ClusterService } from './cluster.service'; +import Redis from 'ioredis'; const env = envSchema.parse(process.env); const LOGGER_LEVELS: LogLevel[] = ['verbose', 'debug', 'log', 'warn', 'error']; @@ -18,6 +19,8 @@ function getLoggerOptions() { return LOGGER_LEVELS.slice(index); } +const logger = new Logger('Main'); + async function bootstrap() { const app = await NestFactory.create( AppModule, @@ -29,6 +32,18 @@ async function bootstrap() { }, ); + const redis = new Redis(env.REDIS_CACHE_URL); + await new Promise((resolve, reject) => { + redis.on('ready', () => { + logger.log('Redis cache ready'); + resolve(undefined); + }); + redis.on('error', (error) => { + logger.error(`Redis cache error: ${error}`); + reject(undefined); + }); + }); + const bootstrapService = app.get(BootstrapService); await bootstrapService.bootstrap(); diff --git a/backend/src/modules/rgbpp/transaction/transaction.service.ts b/backend/src/modules/rgbpp/transaction/transaction.service.ts index 057008b9..56190b25 100644 --- a/backend/src/modules/rgbpp/transaction/transaction.service.ts +++ b/backend/src/modules/rgbpp/transaction/transaction.service.ts @@ -11,7 +11,7 @@ import * as CkbRpcInterface from 'src/core/ckb-rpc/ckb-rpc.interface'; import { RgbppService } from '../rgbpp.service'; import { BI, HashType } from '@ckb-lumos/lumos'; import { Cacheable } from 'src/decorators/cacheable.decorator'; -import { ONE_MONTH_MS } from 'src/common/date'; +import { ONE_DAY_MS } from 'src/common/date'; import { LeapDirection } from '@prisma/client'; import { PrismaService } from 'src/core/database/prisma/prisma.service'; import { CKB_CHAIN_ID } from 'src/constants'; @@ -105,7 +105,7 @@ export class RgbppTransactionService { @Cacheable({ namespace: 'RgbppTransactionService', key: (tx: CkbRpcInterface.Transaction) => `getLeapDirectionByCkbTx:${tx.hash}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async getLeapDirectionByCkbTx(ckbTx: CkbRpcInterface.Transaction) { const inputCells = await Promise.all( @@ -157,7 +157,7 @@ export class RgbppTransactionService { @Cacheable({ namespace: 'RgbppTransactionService', key: (btcTx: BitcoinApiInterface.Transaction) => `queryRgbppLockTx:${btcTx.txid}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async queryRgbppLockTx(btcTx: BitcoinApiInterface.Transaction) { const ckbTxs = await Promise.all( @@ -199,7 +199,7 @@ export class RgbppTransactionService { @Cacheable({ namespace: 'RgbppTransactionService', key: (btcTx: BitcoinApiInterface.Transaction) => `queryRgbppBtcTimeLockTx:${btcTx.txid}`, - ttl: ONE_MONTH_MS, + ttl: ONE_DAY_MS, }) public async queryRgbppBtcTimeLockTx(btcTx: BitcoinApiInterface.Transaction) { const ckbTxs = ( diff --git a/frontend/src/app/[lang]/assets/coins/[typeHash]/holders/page.tsx b/frontend/src/app/[lang]/assets/coins/[typeHash]/holders/page.tsx index 06453aa9..c596c737 100644 --- a/frontend/src/app/[lang]/assets/coins/[typeHash]/holders/page.tsx +++ b/frontend/src/app/[lang]/assets/coins/[typeHash]/holders/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { t } from '@lingui/macro' +import { Trans } from '@lingui/macro' import { VStack } from 'styled-system/jsx' import ComingSoonSVG from '@/assets/coming-soon.svg' @@ -10,7 +10,9 @@ export default function Page() { return ( - {t`Coming soon, please stay tuned`} + + Coming soon, please stay tuned + ) } diff --git a/frontend/src/constants/env.ts b/frontend/src/constants/env.ts index e8b7aedf..9ede1e64 100644 --- a/frontend/src/constants/env.ts +++ b/frontend/src/constants/env.ts @@ -20,7 +20,7 @@ const publicEnvSchema = z.object({ .default('https://github.com/ckb-cell/RGBPlusPlus-design/blob/main/docs/lockscript-design-prd-en.md'), RGBPP_SDK_URL: z.string().default('https://github.com/ckb-cell/rgbpp-sdk'), UTXO_STACK_TWITTER_URL: z.string().default('https://x.com/utxostack'), - CKB_CELL_GITHUB_URL: z.string().default('https://github.com/ckb-cell'), + CKB_CELL_GITHUB_URL: z.string().default('https://github.com/utxostack'), UTXO_STACK_MEDIUM_URL: z.string().default('https://medium.com/@utxostack'), RGBPP_EXPLORER_TESTNET_URL: z.string().default('https://testnet.explorer.utxostack.network'), RGBPP_EXPLORER_MAINNET_URL: z.string().default('https://explorer.utxostack.network'), @@ -54,5 +54,6 @@ export const env = { RGBPP_SCRIPT_URL: process.env.NEXT_PUBLIC_RGBPP_SCRIPT_URL, RGBPP_SDK_URL: process.env.NEXT_PUBLIC_RGBPP_SDK_URL, RGBPP_DOMAINS: process.env.NEXT_PUBLIC_RGBPP_DOMAINS, + CKB_CELL_GITHUB_URL: process.env.NEXT_PUBLIC_CKB_CELL_GITHUB_URL, }), } diff --git a/frontend/src/locales/en/messages.po b/frontend/src/locales/en/messages.po index c877df8a..39b80d47 100644 --- a/frontend/src/locales/en/messages.po +++ b/frontend/src/locales/en/messages.po @@ -109,8 +109,8 @@ msgid "Available" msgstr "Available" #: src/app/[lang]/address/[address]/transactions/page.tsx:265 -msgid "Back to first page" -msgstr "Back to first page" +#~ msgid "Back to first page" +#~ msgstr "Back to first page" #: src/app/not-found.tsx:19 msgid "Back to Home" @@ -211,7 +211,7 @@ msgstr "Coming" msgid "Coming Soon" msgstr "Coming Soon" -#: src/app/[lang]/assets/coins/[typeHash]/holders/page.tsx:13 +#: src/app/[lang]/assets/coins/[typeHash]/holders/page.tsx:14 #: src/components/coming-soon.tsx:14 msgid "Coming soon, please stay tuned" msgstr "Coming soon, please stay tuned" @@ -312,7 +312,7 @@ msgstr "Fee rate:<0>{0}{feeUnit}/vB" #~ msgid "Fee rate:<0>{0}sats/vB" #~ msgstr "Fee rate:<0>{0}sats/vB" -#: src/components/pagination-searchparams.tsx:44 +#: src/components/pagination-searchparams.tsx:43 msgid "Go" msgstr "Go" @@ -431,15 +431,19 @@ msgid "Nervos CKB" msgstr "Nervos CKB" #: src/app/[lang]/address/[address]/transactions/page.tsx:277 -msgid "Next" -msgstr "Next" +#~ msgid "Next" +#~ msgstr "Next" #: src/components/no-data.tsx:14 msgid "No Data" msgstr "No Data" -#: src/app/[lang]/address/[address]/transactions/page.tsx:247 -#: src/app/[lang]/address/[address]/transactions/page.tsx:303 +#: src/components/infinite-list-bottom.tsx:29 +msgid "No More" +msgstr "No More" + +#: src/app/[lang]/address/[address]/transactions/btc-tx-list.tsx:55 +#: src/app/[lang]/address/[address]/transactions/ckb-tx-list.tsx:57 msgid "No Transaction" msgstr "No Transaction" @@ -522,10 +526,10 @@ msgstr "RGB++ Txns" msgid "sat/VB" msgstr "sat/VB" -#: src/app/[lang]/block/btc/[hashOrHeight]/transactions/page.tsx:99 -#: src/app/[lang]/block/ckb/[hashOrHeight]/transactions/page.tsx:102 -#: src/components/btc/btc-transaction-card-in-address.tsx:37 +#: src/components/btc/btc-transaction-card-in-address.tsx:35 +#: src/components/btc/btc-transaction-card-with-query-in-block.tsx:20 #: src/components/btc/btc-transaction-overview.tsx:57 +#: src/components/ckb/ckb-transaction-card-with-query-in-block.tsx:20 msgid "sats" msgstr "sats" @@ -563,7 +567,7 @@ msgstr "SDK" msgid "Search by Address/Tx Hash/Block Hash/AssetID" msgstr "Search by Address/Tx Hash/Block Hash/AssetID" -#: src/app/[lang]/address/[address]/transactions/page.tsx:316 +#: src/components/ckb/ckb-transaction-card-with-query-in-address.tsx:24 msgid "shannons" msgstr "shannons" @@ -621,9 +625,8 @@ msgstr "Testnet" msgid "Time" msgstr "Time" -#: src/app/[lang]/address/[address]/transactions/page.tsx:325 #: src/app/[lang]/assets/coins/[typeHash]/transactions/page.tsx:106 -#: src/app/[lang]/assets/coins/page.tsx:53 +#: src/app/[lang]/assets/coins/page.tsx:54 msgid "Total {0} Items" msgstr "Total {0} Items" @@ -635,7 +638,7 @@ msgstr "Transaction" #: src/app/[lang]/address/[address]/layout.tsx:103 #: src/app/[lang]/assets/coins/[typeHash]/layout.tsx:76 #: src/app/[lang]/block/btc/[hashOrHeight]/layout.tsx:67 -#: src/app/[lang]/block/ckb/[hashOrHeight]/layout.tsx:62 +#: src/app/[lang]/block/ckb/[hashOrHeight]/layout.tsx:59 #: src/components/transaction-header.tsx:45 msgid "Transactions" msgstr "Transactions"