From 449109efeb0cb769daa788177caff76ef1060e67 Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Mon, 6 May 2024 16:13:14 -0600 Subject: [PATCH 1/4] Add Dockerfile and travis config --- .travis.yml | 49 ++++++++++++++++++++ Dockerfile | 40 ++++++++++++++++ app/v1/account/[account]/history/route.ts | 12 ++--- app/v1/block/[block_id]/route.ts | 14 +++--- app/v1/contract/[contract_id]/abi/route.ts | 4 +- app/v1/decode/events/route.ts | 4 +- app/v1/decode/operations/route.ts | 4 +- app/v1/transaction/[transaction_id]/route.ts | 6 +-- app/v1/transaction/prepare/route.ts | 4 +- app/v1/transaction/submit/route.ts | 4 +- next.config.js | 4 +- utils/contracts.ts | 4 +- utils/events.ts | 4 +- utils/operations.ts | 4 +- 14 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 .travis.yml create mode 100644 Dockerfile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2a52c54 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,49 @@ +language: generic + +os: linux + +addons: + apt: + update: true + packages: + - golang-go + +jobs: + include: + - name: "Docker and Integration Tests" + os: linux + dist: jammy + services: + - docker + env: + - TAG=`if [ $TRAVIS_BRANCH == "master" ]; then echo -n latest; else echo -n $TRAVIS_BRANCH; fi` + - REST_TAG=$TAG + before_install: + - sudo systemctl stop docker.service && sudo systemctl stop docker.socket + - sudo apt-get install ca-certificates curl + - sudo install -m 0755 -d /etc/apt/keyrings + - sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + - sudo chmod a+r /etc/apt/keyrings/docker.asc + - | + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + - sudo apt-get update + install: + - sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + - git clone https://github.com/koinos/koinos-integration-tests.git + - pushd koinos-integration-tests + - go get ./... + - popd + before_script: + - echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin + - docker build . -t $TRAVIS_REPO_SLUG:$TAG + script: + - pushd koinos-integration-tests + - ./run.sh + after_success: + - | + if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then + docker push $TRAVIS_REPO_SLUG:$TAG + fi \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..772df17 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# https://towardsserverless.com/articles/dockerize-nextjs-app + +FROM node:18-alpine AS build +# Install dependencies only when needed +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app +# Copy and install the dependencies for the project +COPY package.json yarn.lock ./ +RUN yarn install +# Copy all other project files to working directory +COPY . . +# Run the next build process and generate the artifacts +RUN yarn build + +# we are using multi stage build process to keep the image size as small as possible +FROM node:18-alpine +# update and install latest dependencies, add dumb-init package +# add a non root user +RUN apk update && apk upgrade && apk add dumb-init && adduser -D nextuser + +# set work dir as app +WORKDIR /app +# copy the public folder from the project as this is not included in the build process +COPY --from=build --chown=nextuser:nextuser /app/public ./public +# copy the standalone folder inside the .next folder generated from the build process +COPY --from=build --chown=nextuser:nextuser /app/.next/standalone ./ +# copy the static folder inside the .next folder generated from the build process +COPY --from=build --chown=nextuser:nextuser /app/.next/static ./.next/static +# set non root user +USER nextuser + +# expose 3000 on container +EXPOSE 3000 + +# set app host ,port and node env +ENV HOST=0.0.0.0 PORT=3000 NODE_ENV=production +# start the app with dumb init to spawn the Node.js runtime process +# with signal support +CMD ["dumb-init","node","server.js"] \ No newline at end of file diff --git a/app/v1/account/[account]/history/route.ts b/app/v1/account/[account]/history/route.ts index 216e82a..0185dff 100644 --- a/app/v1/account/[account]/history/route.ts +++ b/app/v1/account/[account]/history/route.ts @@ -3,7 +3,7 @@ import { AppError, getErrorMessage, handleError } from '@/utils/errors' import { decodeEvents } from '@/utils/events' import { decodeOperations } from '@/utils/operations' import { getProvider } from '@/utils/providers' -import { interfaces } from 'koilib' +import { BlockHeaderJson, EventData, TransactionJson, TransactionReceipt } from 'koilib' import { NextRequest, NextResponse } from 'next/server' /** @@ -126,9 +126,9 @@ export type BlockReceiptJson = { network_bandwidth_used?: string compute_bandwidth_used?: string state_merkle_root?: string - events?: interfaces.EventData[] + events?: EventData[] token_events: string[] - transaction_receipts?: interfaces.TransactionReceipt[] + transaction_receipts?: TransactionReceipt[] logs?: string[] disk_storage_charged?: string network_bandwidth_charged?: string @@ -147,7 +147,7 @@ export type TransactionReceiptJson = { network_bandwidth_used: string compute_bandwidth_used: string reverted: boolean - events: interfaces.EventData[] + events: EventData[] token_events: string[] logs: string[] amount?: string @@ -155,12 +155,12 @@ export type TransactionReceiptJson = { export type HistoryRecord = { trx?: { - transaction: interfaces.TransactionJson + transaction: TransactionJson receipt: TransactionReceiptJson } block?: { - header: interfaces.BlockHeaderJson + header: BlockHeaderJson receipt: BlockReceiptJson } diff --git a/app/v1/block/[block_id]/route.ts b/app/v1/block/[block_id]/route.ts index 79f202d..8507230 100644 --- a/app/v1/block/[block_id]/route.ts +++ b/app/v1/block/[block_id]/route.ts @@ -1,4 +1,4 @@ -import { interfaces } from 'koilib' +import { BlockJson, EventData, TransactionReceipt } from 'koilib' import { AppError, handleError } from '@/utils/errors' import { getProvider } from '@/utils/providers' import { decodeEvents } from '@/utils/events' @@ -127,10 +127,10 @@ export async function GET(request: Request, { params }: { params: { block_id: st block_items: { block_id: string block_height: string - block: interfaces.BlockJson + block: BlockJson receipt: { - events: interfaces.EventData[] - transaction_receipts: interfaces.TransactionReceipt[] + events: EventData[] + transaction_receipts: TransactionReceipt[] } }[] }>('block_store.get_blocks_by_id', { @@ -143,10 +143,10 @@ export async function GET(request: Request, { params }: { params: { block_id: st block_items: { block_id: string block_height: string - block: interfaces.BlockJson + block: BlockJson receipt: { - events: interfaces.EventData[] - transaction_receipts: interfaces.TransactionReceipt[] + events: EventData[] + transaction_receipts: TransactionReceipt[] } }[] }>('block_store.get_blocks_by_height', { diff --git a/app/v1/contract/[contract_id]/abi/route.ts b/app/v1/contract/[contract_id]/abi/route.ts index e30d9d1..3fd1d9d 100644 --- a/app/v1/contract/[contract_id]/abi/route.ts +++ b/app/v1/contract/[contract_id]/abi/route.ts @@ -1,7 +1,7 @@ import { getContractId } from '@/utils/contracts' import { AppError, handleError } from '@/utils/errors' import { getProvider } from '@/utils/providers' -import { interfaces } from 'koilib' +import { Abi } from 'koilib' import { convert } from '@roamin/koinos-pb-to-proto' import protobufjs from 'protobufjs' @@ -70,7 +70,7 @@ export async function GET(request: Request, { params }: { params: { contract_id: throw new AppError(`abi not available for contract ${contract_id}`) } - const abi: interfaces.Abi = { + const abi: Abi = { ...JSON.parse(response.meta.abi) } diff --git a/app/v1/decode/events/route.ts b/app/v1/decode/events/route.ts index 63240d4..517d861 100644 --- a/app/v1/decode/events/route.ts +++ b/app/v1/decode/events/route.ts @@ -1,5 +1,5 @@ import { AppError, getErrorMessage, handleError } from '@/utils/errors' -import { interfaces } from 'koilib' +import { EventData } from 'koilib' import { NextRequest, NextResponse } from 'next/server' import { decodeEvents } from '@/utils/events' @@ -92,7 +92,7 @@ import { decodeEvents } from '@/utils/events' export async function POST(request: NextRequest) { try { try { - const events = (await request.json()) as interfaces.EventData[] + const events = (await request.json()) as EventData[] const result = await decodeEvents(events) return NextResponse.json(result) diff --git a/app/v1/decode/operations/route.ts b/app/v1/decode/operations/route.ts index 5bfb42b..1358588 100644 --- a/app/v1/decode/operations/route.ts +++ b/app/v1/decode/operations/route.ts @@ -1,5 +1,5 @@ import { AppError, getErrorMessage, handleError } from '@/utils/errors' -import { interfaces } from 'koilib' +import { OperationJson } from 'koilib' import { NextRequest, NextResponse } from 'next/server' import { decodeOperations } from '@/utils/operations' @@ -55,7 +55,7 @@ import { decodeOperations } from '@/utils/operations' export async function POST(request: NextRequest) { try { try { - const operations = (await request.json()) as interfaces.OperationJson[] + const operations = (await request.json()) as OperationJson[] const result = await decodeOperations(operations) return NextResponse.json(result) diff --git a/app/v1/transaction/[transaction_id]/route.ts b/app/v1/transaction/[transaction_id]/route.ts index ca99f56..eca9ed4 100644 --- a/app/v1/transaction/[transaction_id]/route.ts +++ b/app/v1/transaction/[transaction_id]/route.ts @@ -2,7 +2,7 @@ import { AppError, handleError } from '@/utils/errors' import { decodeEvents } from '@/utils/events' import { decodeOperations } from '@/utils/operations' import { getProvider } from '@/utils/providers' -import { interfaces } from 'koilib' +import { BlockJson, TransactionReceipt } from 'koilib' /** * @swagger @@ -173,9 +173,9 @@ export async function GET(request: Request, { params }: { params: { transaction_ block_items: { block_id: string block_height: string - block: interfaces.BlockJson + block: BlockJson receipt: { - transaction_receipts: interfaces.TransactionReceipt[] + transaction_receipts: TransactionReceipt[] } }[] }>('block_store.get_blocks_by_id', { diff --git a/app/v1/transaction/prepare/route.ts b/app/v1/transaction/prepare/route.ts index 655d9d4..b18ba38 100644 --- a/app/v1/transaction/prepare/route.ts +++ b/app/v1/transaction/prepare/route.ts @@ -1,5 +1,5 @@ import { AppError, getErrorMessage, handleError } from '@/utils/errors' -import { interfaces, Transaction } from 'koilib' +import { Transaction, TransactionJson } from 'koilib' import { getProvider } from '@/utils/providers' import { NextRequest, NextResponse } from 'next/server' @@ -106,7 +106,7 @@ export async function POST(request: NextRequest) { try { try { const provider = getProvider() - const transaction = (await request.json()) as interfaces.TransactionJson + const transaction = (await request.json()) as TransactionJson const preparedTransaction = await Transaction.prepareTransaction(transaction, provider) diff --git a/app/v1/transaction/submit/route.ts b/app/v1/transaction/submit/route.ts index 1646faa..d5de6f4 100644 --- a/app/v1/transaction/submit/route.ts +++ b/app/v1/transaction/submit/route.ts @@ -1,5 +1,5 @@ import { AppError, getErrorMessage, handleError } from '@/utils/errors' -import { interfaces } from 'koilib' +import { TransactionJson } from 'koilib' import { getProvider } from '@/utils/providers' import { NextRequest, NextResponse } from 'next/server' import { revalidatePath } from 'next/cache' @@ -39,7 +39,7 @@ export async function POST(request: NextRequest) { // Get the JSON RPC provider const provider = getProvider() - const transaction = (await request.json()) as interfaces.TransactionJson + const transaction = (await request.json()) as TransactionJson const { searchParams } = new URL(request.url) const broadcast = searchParams.get('broadcast') !== 'false' diff --git a/next.config.js b/next.config.js index 767719f..bbab259 100644 --- a/next.config.js +++ b/next.config.js @@ -1,4 +1,6 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = { + output: 'standalone' +} module.exports = nextConfig diff --git a/utils/contracts.ts b/utils/contracts.ts index c4e3d71..3517d77 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -1,5 +1,5 @@ import { ProtoDescriptor, convert } from '@roamin/koinos-pb-to-proto' -import { Contract, interfaces, utils } from 'koilib' +import { Abi, Contract, utils } from 'koilib' import { Root, Type, parse } from 'protobufjs' import { AppError } from './errors' import { getAddress } from './addresses' @@ -31,7 +31,7 @@ export async function getContractId(str: string) { return contract_id } -export function fixAbi(abi: interfaces.Abi): interfaces.Abi { +export function fixAbi(abi: Abi): Abi { Object.keys(abi.methods).forEach((name) => { abi.methods[name] = { ...abi!.methods[name] diff --git a/utils/events.ts b/utils/events.ts index b0ccee2..612b0fd 100644 --- a/utils/events.ts +++ b/utils/events.ts @@ -1,10 +1,10 @@ -import { Serializer, interfaces, utils } from 'koilib' +import { EventData, Serializer } from 'koilib' import { getContract } from './contracts' import koinosJson from '@koinos/proto-js/index.json' // @ts-ignore const serializer = new Serializer(koinosJson) -export async function decodeEvents(events: interfaces.EventData[]) { +export async function decodeEvents(events: EventData[]) { for (let index = 0; index < events.length; index++) { const event = events[index] diff --git a/utils/operations.ts b/utils/operations.ts index 94a7758..c0c65e7 100644 --- a/utils/operations.ts +++ b/utils/operations.ts @@ -1,7 +1,7 @@ -import { interfaces } from 'koilib' +import { OperationJson } from 'koilib' import { getContract } from './contracts' -export async function decodeOperations(operations: interfaces.OperationJson[]) { +export async function decodeOperations(operations: OperationJson[]) { for (let index = 0; index < operations.length; index++) { const operation = operations[index] if (operation.call_contract) { From 1c2ce4cc4db27ec1ec92e82b3000670661c41398 Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Mon, 6 May 2024 16:54:22 -0600 Subject: [PATCH 2/4] Don't run integration tests --- .travis.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2a52c54..8dc5814 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,6 @@ os: linux addons: apt: update: true - packages: - - golang-go jobs: include: @@ -32,16 +30,9 @@ jobs: - sudo apt-get update install: - sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - - git clone https://github.com/koinos/koinos-integration-tests.git - - pushd koinos-integration-tests - - go get ./... - - popd before_script: - echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin - docker build . -t $TRAVIS_REPO_SLUG:$TAG - script: - - pushd koinos-integration-tests - - ./run.sh after_success: - | if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then From d9441d35f166321f98b5c96d061e00db924ce6e4 Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Wed, 8 May 2024 09:26:15 -0600 Subject: [PATCH 3/4] Don't create nextuser --- Dockerfile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 772df17..7ccccbc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,18 +17,16 @@ RUN yarn build FROM node:18-alpine # update and install latest dependencies, add dumb-init package # add a non root user -RUN apk update && apk upgrade && apk add dumb-init && adduser -D nextuser +RUN apk update && apk upgrade && apk add dumb-init # set work dir as app WORKDIR /app # copy the public folder from the project as this is not included in the build process -COPY --from=build --chown=nextuser:nextuser /app/public ./public +COPY --from=build /app/public ./public # copy the standalone folder inside the .next folder generated from the build process -COPY --from=build --chown=nextuser:nextuser /app/.next/standalone ./ +COPY --from=build /app/.next/standalone ./ # copy the static folder inside the .next folder generated from the build process -COPY --from=build --chown=nextuser:nextuser /app/.next/static ./.next/static -# set non root user -USER nextuser +COPY --from=build /app/.next/static ./.next/static # expose 3000 on container EXPOSE 3000 From 3248532e5bb2891c63a1d7680a410985c350e56e Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Wed, 8 May 2024 09:36:20 -0600 Subject: [PATCH 4/4] Install python3 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7ccccbc..cec4a03 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM node:18-alpine AS build # Install dependencies only when needed # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat +RUN apk add --no-cache libc6-compat python3 WORKDIR /app # Copy and install the dependencies for the project COPY package.json yarn.lock ./ @@ -17,7 +17,7 @@ RUN yarn build FROM node:18-alpine # update and install latest dependencies, add dumb-init package # add a non root user -RUN apk update && apk upgrade && apk add dumb-init +RUN apk update && apk upgrade && apk add dumb-init python3 # set work dir as app WORKDIR /app