From c6e35d29ec0145806f15dac6095cbe05579b2ee5 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 25 May 2024 10:21:28 +0200 Subject: [PATCH 1/5] Compile server file together with the rest of the project on build --- package.json | 2 +- server.ts | 7 +++---- tsconfig.build-server.json | 8 ++++++++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 tsconfig.build-server.json diff --git a/package.json b/package.json index ed5a73e..44b2d6f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "MIT", "type": "module", "scripts": { - "build": "react-router build", + "build": "react-router build && tsc --project tsconfig.build-server.json", "test": "vitest run", "test:ci": "npm run test -- --coverage", "lint": "npm run lint:js", diff --git a/server.ts b/server.ts index e19cd7b..eece4c1 100644 --- a/server.ts +++ b/server.ts @@ -1,9 +1,8 @@ import { createRequestHandler } from '@react-router/express'; import express from 'express'; import { serverContainer } from './app/container/container.server'; -import { isProd } from './app/utils/env.server'; -const viteDevServer = isProd() +const viteDevServer = process.env.NODE_ENV === 'production' ? null : await import('vite').then( (vite) => @@ -16,13 +15,13 @@ const app = express(); app.use( viteDevServer ? viteDevServer.middlewares - : express.static('build/client'), + : express.static('client'), ); const build = viteDevServer ? () => viteDevServer.ssrLoadModule('virtual:react-router/server-build') // @ts-expect-error This code branch is used only when that file is built - : await import('./build/server/index.js'); + : await import('./server/index.js'); // Fork entity manager on every request app.use(serverContainer.emForkMiddleware); diff --git a/tsconfig.build-server.json b/tsconfig.build-server.json new file mode 100644 index 0000000..609b070 --- /dev/null +++ b/tsconfig.build-server.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["./server.ts"], + "compilerOptions": { + "noEmit": false, + "outDir": "./build" + } +} From 4c96c2ab1530de07656b51a2812010d7f34e189b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 26 May 2024 10:00:54 +0200 Subject: [PATCH 2/5] Add Dockerfile --- .dockerignore | 12 ++++++++++++ Dockerfile | 33 +++++++++++++++++++++++++++++++++ server.ts | 9 +++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..51cb668 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +./.github +./build +./coverage +./node_modules +./test +./home +./data +./docs +./.eslintrc +./docker-compose* +./indocker +./vitest.config.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f9c052f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +FROM node:22.2-alpine as builder +COPY . /shlink-dashboard +ARG VERSION="latest" +ENV VERSION ${VERSION} +# Install dev dependencies and build project +RUN cd /shlink-dashboard && npm ci && node --run build + + +FROM node:22.2-alpine +ARG UID=101 +ARG VERSION="latest" +ENV VERSION ${VERSION} +LABEL maintainer="Alejandro Celaya " +ENV NODE_ENV "production" + +USER root +COPY --from=builder /shlink-dashboard/build /shlink-dashboard +COPY package.json /shlink-dashboard/package.json +COPY package-lock.json /shlink-dashboard/package-lock.json +COPY LICENSE /shlink-dashboard/LICENSE +COPY README.md /shlink-dashboard/README.md + +WORKDIR /shlink-dashboard +RUN npm ci --omit dev +RUN mkdir data && chown $UID:0 data + +# Expose default port +EXPOSE 3005 + +# Switch to non-privileged UID as the last step +USER $UID + +ENTRYPOINT ["node", "./server.js"] diff --git a/server.ts b/server.ts index eece4c1..795a534 100644 --- a/server.ts +++ b/server.ts @@ -2,7 +2,9 @@ import { createRequestHandler } from '@react-router/express'; import express from 'express'; import { serverContainer } from './app/container/container.server'; -const viteDevServer = process.env.NODE_ENV === 'production' +const { NODE_ENV, SHLINK_DASHBOARD_PORT = '3005' } = process.env; + +const viteDevServer = NODE_ENV === 'production' ? null : await import('vite').then( (vite) => @@ -27,4 +29,7 @@ const build = viteDevServer app.use(serverContainer.emForkMiddleware); app.all('*', createRequestHandler({ build })); -app.listen(3005, () => console.log('App listening on http://localhost:3005')); +app.listen( + Number(SHLINK_DASHBOARD_PORT), + () => console.log(`App listening on http://localhost:${SHLINK_DASHBOARD_PORT}`), +); From 50a1e2da769bb0e25f6d6d1f7c401c4bd2652364 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 26 May 2024 10:05:19 +0200 Subject: [PATCH 3/5] Add GitHub Actions workflow to build docker image --- .dockerignore | 6 +++--- .github/workflows/docker-image-build.yml | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/docker-image-build.yml diff --git a/.dockerignore b/.dockerignore index 51cb668..e11c1c4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,11 +1,11 @@ ./.github ./build ./coverage -./node_modules -./test -./home ./data ./docs +./home +./node_modules +./test ./.eslintrc ./docker-compose* ./indocker diff --git a/.github/workflows/docker-image-build.yml b/.github/workflows/docker-image-build.yml new file mode 100644 index 0000000..f302f6d --- /dev/null +++ b/.github/workflows/docker-image-build.yml @@ -0,0 +1,15 @@ +name: Build and publish docker image + +on: + push: + tags: + - 'v*' + +jobs: + build: + uses: shlinkio/github-actions/.github/workflows/docker-publish-image.yml@main + secrets: inherit + with: + image-name: shlinkio/shlink-dashboard + version-arg-name: VERSION + platforms: 'linux/arm64/v8,linux/amd64' From cee62ccce655ebf6404d65a0637542b8df7fb46d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 26 May 2024 10:27:07 +0200 Subject: [PATCH 4/5] Adjust dockerignore file --- .dockerignore | 24 ++++++++++++------------ Dockerfile | 6 ++++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.dockerignore b/.dockerignore index e11c1c4..74bb8f5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,12 +1,12 @@ -./.github -./build -./coverage -./data -./docs -./home -./node_modules -./test -./.eslintrc -./docker-compose* -./indocker -./vitest.config.ts +.github +build +coverage +data +docs +home +node_modules +test +.eslintrc +docker-compose* +indocker +vitest.config.ts diff --git a/Dockerfile b/Dockerfile index f9c052f..1e0472a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,11 @@ FROM node:22.2-alpine as builder -COPY . /shlink-dashboard ARG VERSION="latest" ENV VERSION ${VERSION} + +COPY . /shlink-dashboard +WORKDIR /shlink-dashboard # Install dev dependencies and build project -RUN cd /shlink-dashboard && npm ci && node --run build +RUN npm ci && node --run build FROM node:22.2-alpine From 47c294e37e831c4f9a965d60992153f69f7b2316 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 19 Jun 2024 09:36:34 +0200 Subject: [PATCH 5/5] Move port definition to env vars --- app/utils/env.server.ts | 1 + server.ts | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/utils/env.server.ts b/app/utils/env.server.ts index 6ed4907..fc5745e 100644 --- a/app/utils/env.server.ts +++ b/app/utils/env.server.ts @@ -6,6 +6,7 @@ export type DbEngine = typeof supportedDbEngines[number]; const envVariables = z.object({ NODE_ENV: z.enum(['production', 'development', 'test']).optional(), + SHLINK_DASHBOARD_PORT: z.number().optional().default(3005), // Database connection options SHLINK_DASHBOARD_DB_DRIVER: z.enum(supportedDbEngines).optional(), diff --git a/server.ts b/server.ts index 795a534..455db09 100644 --- a/server.ts +++ b/server.ts @@ -1,10 +1,9 @@ import { createRequestHandler } from '@react-router/express'; import express from 'express'; import { serverContainer } from './app/container/container.server'; +import { env, isProd } from './app/utils/env.server'; -const { NODE_ENV, SHLINK_DASHBOARD_PORT = '3005' } = process.env; - -const viteDevServer = NODE_ENV === 'production' +const viteDevServer = isProd() ? null : await import('vite').then( (vite) => @@ -30,6 +29,6 @@ app.use(serverContainer.emForkMiddleware); app.all('*', createRequestHandler({ build })); app.listen( - Number(SHLINK_DASHBOARD_PORT), - () => console.log(`App listening on http://localhost:${SHLINK_DASHBOARD_PORT}`), + env.SHLINK_DASHBOARD_PORT, + () => console.log(`App listening on http://localhost:${env.SHLINK_DASHBOARD_PORT}`), );