diff --git a/.dockerignore b/.dockerignore index 386c86a71..891c644f7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,5 +2,17 @@ node_modules /dist logs c2d_storage -.env.local -.env \ No newline at end of file +databases +.env +.env.* +.git +.github +docs +src/test +*.md +*.log +.nyc_output +coverage +docker-compose.yml +elasticsearch-compose.yml +typesense-compose.yml diff --git a/Dockerfile b/Dockerfile index 6ba093edb..67e813964 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,44 +1,56 @@ -FROM ubuntu:22.04 AS base -RUN apt-get update && apt-get -y install bash curl git wget libatomic1 python3 build-essential -COPY .nvmrc /usr/src/app/ -RUN rm /bin/sh && ln -s /bin/bash /bin/sh -ENV NVM_DIR=/usr/local/nvm -RUN mkdir $NVM_DIR -ENV NODE_VERSION=v22.15.0 -# Install nvm with node and npm -RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \ - && source $NVM_DIR/nvm.sh \ - && nvm install $NODE_VERSION \ - && nvm alias default $NODE_VERSION \ - && nvm use default -ENV NODE_PATH=$NVM_DIR/$NODE_VERSION/lib/node_modules -ENV PATH=$NVM_DIR/versions/node/$NODE_VERSION/bin:$PATH -ENV IPFS_GATEWAY='https://ipfs.io/' -ENV ARWEAVE_GATEWAY='https://arweave.net/' - -FROM base AS builder -COPY package*.json /usr/src/app/ -COPY scripts/ /usr/src/app/scripts/ -WORKDIR /usr/src/app/ +FROM node:22.15.0-bookworm@sha256:a1f1274dadd49738bcd4cf552af43354bb781a7e9e3bc984cfeedc55aba2ddd8 AS builder +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 \ + build-essential \ + libatomic1 \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /usr/src/app +COPY package*.json ./ +COPY scripts/ ./scripts/ RUN npm ci +COPY . . +RUN npm run build && npm prune --omit=dev + + +FROM node:22.15.0-bookworm-slim@sha256:557e52a0fcb928ee113df7e1fb5d4f60c1341dbda53f55e3d815ca10807efdce AS runner +RUN apt-get update && apt-get install -y --no-install-recommends \ + dumb-init \ + gosu \ + libatomic1 \ + && rm -rf /var/lib/apt/lists/* + +ENV NODE_ENV=production \ + IPFS_GATEWAY='https://ipfs.io/' \ + ARWEAVE_GATEWAY='https://arweave.net/' \ + P2P_ipV4BindTcpPort=9000 \ + P2P_ipV4BindWsPort=9001 \ + P2P_ipV6BindTcpPort=9002 \ + P2P_ipV6BindWsPort=9003 \ + P2P_ipV4BindWssPort=9005 \ + HTTP_API_PORT=8000 + +EXPOSE 9000 9001 9002 9003 9005 8000 + +# GID of the docker group on the host. Needs to match so the node user can access +# /var/run/docker.sock for compute jobs. Default is 999 (common on Debian/Ubuntu). +# Override at build time if your host differs: docker build --build-arg DOCKER_GID=$(getent group docker | cut -d: -f3) . +ARG DOCKER_GID=999 +RUN groupadd -g ${DOCKER_GID} docker && usermod -aG docker node + +WORKDIR /usr/src/app + +COPY --chown=node:node --from=builder /usr/src/app/dist ./dist +COPY --chown=node:node --from=builder /usr/src/app/node_modules ./node_modules +COPY --chown=node:node --from=builder /usr/src/app/schemas ./schemas +COPY --chown=node:node --from=builder /usr/src/app/package.json ./ +COPY --chown=node:node --from=builder /usr/src/app/config.json ./ + +RUN mkdir -p databases c2d_storage logs +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh -FROM base AS runner -COPY . /usr/src/app -WORKDIR /usr/src/app/ -COPY --from=builder /usr/src/app/node_modules/ /usr/src/app/node_modules/ -RUN npm run build -ENV P2P_ipV4BindTcpPort=9000 -EXPOSE 9000 -ENV P2P_ipV4BindWsPort=9001 -EXPOSE 9001 -ENV P2P_ipV6BindTcpPort=9002 -EXPOSE 9002 -ENV P2P_ipV6BindWsPort=9003 -EXPOSE 9003 -ENV P2P_ipV4BindWssPort=9005 -EXPOSE 9005 -ENV HTTP_API_PORT=8000 -EXPOSE 8000 -ENV NODE_ENV='production' -CMD ["npm","run","start"] +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +CMD ["node", "--max-old-space-size=28784", "--trace-warnings", "--experimental-specifier-resolution=node", "dist/index.js"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 000000000..46f21fdbb --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Fix ownership of directories that may be mounted as volumes (owned by root). +# Runs as root, then drops to 'node' user via gosu. +chown -R node:node /usr/src/app/databases /usr/src/app/c2d_storage /usr/src/app/logs 2>/dev/null || true + +exec gosu node dumb-init -- "$@" diff --git a/src/components/Indexer/index.ts b/src/components/Indexer/index.ts index fd9e94476..17460bf83 100644 --- a/src/components/Indexer/index.ts +++ b/src/components/Indexer/index.ts @@ -38,6 +38,7 @@ import { create256Hash } from '../../utils/crypt.js' import { getDatabase, isReachableConnection } from '../../utils/database.js' import { sleep } from '../../utils/util.js' import { isReindexingNeeded } from './version.js' +import { getPackageVersion } from '../../utils/version.js' import { DB_EVENTS, ES_CONNECTION_EVENTS } from '../database/ElasticsearchConfigHelper.js' /** @@ -535,7 +536,7 @@ export class OceanIndexer { * Checks if reindexing is needed and triggers it for all chains */ public async checkAndTriggerReindexing(): Promise { - const currentVersion = process.env.npm_package_version + const currentVersion = getPackageVersion() const dbActive = this.getDatabase() if (!dbActive || !(await isReachableConnection(dbActive.getConfig().url))) { INDEXER_LOGGER.error(`Giving up reindexing. DB is not online!`) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 224414f86..73ca98587 100755 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -35,6 +35,7 @@ import { createWriteStream, existsSync, mkdirSync, + chmodSync, rmSync, writeFileSync, appendFileSync, @@ -2810,6 +2811,7 @@ export class C2DEngineDocker extends C2DEngine { if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }) } + chmodSync(dir, 0o777) } return true } catch (e) { diff --git a/src/components/core/utils/statusHandler.ts b/src/components/core/utils/statusHandler.ts index 2b7d73c9c..dca790bce 100644 --- a/src/components/core/utils/statusHandler.ts +++ b/src/components/core/utils/statusHandler.ts @@ -14,6 +14,7 @@ import { typesenseSchemas } from '../../database/TypesenseSchemas.js' import { SupportedNetwork } from '../../../@types/blockchain.js' import { getAdminAddresses } from '../../../utils/auth.js' import HumanHasher from 'humanhash' +import { getPackageVersion } from '../../../utils/version.js' function getSupportedStorageTypes(config: OceanNodeConfig): StorageTypes { return { @@ -126,7 +127,7 @@ export async function status( publicKey: publicKeyHex, friendlyName: new HumanHasher().humanize(publicKeyHex), address: oceanNode.getKeyManager().getEthAddress(), - version: process.env.npm_package_version, + version: getPackageVersion(), http: config.hasHttp, p2p: config.hasP2P, provider: [], diff --git a/src/utils/version.ts b/src/utils/version.ts new file mode 100644 index 000000000..470f95abe --- /dev/null +++ b/src/utils/version.ts @@ -0,0 +1,7 @@ +import { createRequire } from 'module' + +const require = createRequire(import.meta.url) + +export function getPackageVersion(): string { + return process.env.npm_package_version ?? require('../../package.json').version +}