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

build(docker): add docker for easily bootstrapping dev env #13

Merged
merged 1 commit into from
Jan 31, 2024
Merged
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
107 changes: 19 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,31 @@
[SPDX-License-Identifier: Apache-2.0]::
[SPDX-FileCopyrightText: 2021-2023 OKTET Labs Ltd.]::

# Bublik UI

## Package manager

We use pnpm as our package manager so to start you will need to install it, please refer to [pnpm documentation](https://pnpm.io/) for installing instructions

All commands should be run like this `pnpm exec nx ...etc` or you can add alias to your .bashrc like this `alias pnx="pnpm nx --"`, then you can run like this `pnx ...`

## Monorepo tools

We use [Nx](https://nx.dev/getting-started/intro) for monorepo, you would need to install nx cli globally or run commands via `npx` or `pnpm dlx...`

## Project structure

```
📦 dist
┣ 📂 apps - Built production ready applications
📦 apps
┣ 📂 bublik - Bublik UI Application
📦 libs
┣ 📂 bublik - Bublik UI application specific libs
┃ ┣ 📂 +state - Redux state
┃ ┣ 📂 config - Different Bublik UI configs (needed for UI to work correctly)
┃ ┣ 📂 features - Specific Bublik UI featires implementation
┃ ┣ 📂 router - Router helpers, encoding/decoding search params
┣ 📂 env - Env helper lib
┣ 📂 services - General services for data-access
┃ ┗ 📂 bublik-api - Bublik API hooks/methods (RTK Query)
┣ 📂 shared - Shared libraries for multiple apps to consume
┃ ┣ 📂 charts - Apache Echarts components
┃ ┣ 📂 hooks - Shared react hooks
┃ ┣ 📂 icons - UI Icons
┃ ┣ 📂 tailwind-ui - Shared UI components
┃ ┣ 📂 types - Shared interfaces and types
┃ ┣ 📂 utils - Shared general utils
```

## Config

To create new Bublik UI frontend for deployment you would need to follow these steps:

1. Add needed configuration to `apps/bublik/project.json`
2. `base` - where the app will be mounted it **must start with `/`** and **no** trailing slash at the end

Example:

```json
{
"demo": {
"base": "/prefix",
"outputPath": "dist/apps/bublik-app"
}
}
```

## Local development

Make sure you installed nrwl [nx monorepo tools](https://nx.dev/getting-started/intro)
Example: 'pnpm add -g nx'

To start development, please follow this steps:

1. Clone repo
2. Run `pnpm install` to install dependencies
3. Run `pnpm start` to start development server
## Before you start (skip if you want to run via docker)

## Commands
1. Make sure you installed node, you can use [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm)
2. Make sure you installed [pnpm](https://pnpm.io/)
3. Make sure you installed [nx](https://nx.dev/getting-started/installation#installing-nx-globally) globally
4. Make sure you installed [docker](https://docs.docker.com/desktop/) (you can install docker desktop or just docker engine)
5. Create env file `apps/bublik/.env.local` (see `apps/bublik/.env.local.example` for reference)

- `pnpm start` - starts Bublik UI in development mode
- `pnpm test` - runs tests in parallel for all projects
- `pnpm bublik:serve:prod` - starts Bublik UI in production mode
- `pnpm bublik:storybook` - starts storybook for Bublik UI
- `pnpm bublik:build-storybook` - builds storybook for Bublik UI
- `pnpm bublik:ci:build` - builds all Bublik UI application in production mode
### To test everything working run following commands

## Misc
1. `node -v` used node version in file `.nvmrc`
2. `pnpm -v`
3. `nx --version`
4. `docker version`

- If you want to connect to particular backend remote or local you can proxy all you requests with `apps/bublik/vite.config.ts`
## Run locally (run with docker)

## Common errors and solutions
You can run UI the following way:

- `pnpm ...` - command not working
- try to run command with `pnpm exec nx ...`
- Error installing packages like `react-vtree` and `legacy-peer-deps` errors
- try to add flag `legacy-peer-deps`
- Tailwind classes not working in storybook
- try adding lib/app path to `libs/shared/storybook/tailwind.config.js`
- Storybook not finding stories in your lib/app
- try adding lib/app path to `libs/shared/storybook/.storybook/main.js`
- Storybook tailwind classes not working
- try removing `node_module` and installing with `pnpm install`
1. Create env file `apps/bublik/.env.local` (see `apps/bublik/.env.local.example` for reference)
2. Build image `pnpm run docker:build` OR `docker build -f apps/bublik/Dockerfile.dev . -t bublik-ui`
3. Run image `pnpm run docker:start` OR `docker run -it --rm -p 4200:4200 -v $(pwd):/app -v /app/node_modules --env-file apps/bublik/.env.local bublik-ui`

## Release
Caveats:
- Add flag --network host to run image command if django is served from host
- If you add new dependencies to package.json you need to rebuild image

We are using release-it to automate releases. To release new version of Bublik UI you need to run `pnpm release` and follow the instructions.
Or run `pnpm release -- --dry-run` to see what will be done.
You can also release from GitHub actions menu specifying what type of release you want to do.
18 changes: 14 additions & 4 deletions apps/bublik/.env.local.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Username and password used to proxy requests to JSON logs
BUBLIK_UI_DEV_LOGS_AUTH="<USERNAME>:<PASSWORD>"
# This configuration is necessary for proper proxying of requests to django backend

#####################################################
# Example for connecting to ts-factory
# BUBLIK_UI_DEV_LOGS_TARGET=https://ts-factory.io
# BUBLIK_UI_DEV_BACKEND_TARGET=https://ts-factory.io
# URL_PREFIX=/bublik/v2

#####################################################
# This examples shows how to setup env for frontend served from `http://localhost/prefix/v2`
# Target of JSON logs (protocol, host, port)
BUBLIK_UI_DEV_LOGS_TARGET="https://example.com"
BUBLIK_UI_DEV_LOGS_TARGET=http://localhost
# Where backend is served (protocol, host, port)
BUBLIK_UI_DEV_BACKEND_TARGET="http://localhost:8000"
BUBLIK_UI_DEV_BACKEND_TARGET=http://localhost
# Adds prefix from where backend is served e.g (http://localhost/prefix/api/v2)
URL_PREFIX=/prefix/v2
1 change: 1 addition & 0 deletions apps/bublik/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Local
.env.local
.env
21 changes: 12 additions & 9 deletions apps/bublik/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
FROM node:17.9.0
FROM node:20-slim AS base

ENV PNPM_HOME="/root/.local/share/pnpm"
ENV PATH="${PATH}:${PNPM_HOME}"
ARG PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1

RUN npm install --global pnpm
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

RUN pnpm add -g nx
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=${PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD}
ENV BASE_URL="/v2"

RUN corepack enable

COPY . /app
WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN pnpm install
FROM base as runner

COPY . .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install

EXPOSE 4200

CMD ["pnpm", "run", "nx", "serve", "--host=0.0.0.0"]
CMD ["pnpm", "run", "nx", "serve", "--host=0.0.0.0", "--base=${BASE_URL}"]
59 changes: 46 additions & 13 deletions apps/bublik/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,66 @@
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2021-2023 OKTET Labs Ltd. */
import { defineConfig, loadEnv } from 'vite';

import { defineConfig, HttpProxy, loadEnv, ProxyOptions } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import svgr from 'vite-plugin-svgr';

const createRequestLogger =
(domain: string) => (proxy: HttpProxy.Server, _options: ProxyOptions) => {
proxy.on('error', (err, _req, _res) => {
console.log(`[${domain}] Error:`, err);
});
proxy.on('proxyReq', (proxyReq, req, _res) => {
console.log(`[${domain}] Request:`, req.method, req.url);
});
proxy.on('proxyRes', (proxyRes, req, _res) => {
console.log(`[${domain}] Response:`, proxyRes.statusCode, req.url);
});
};

export default defineConfig(async ({ mode }) => {
const mdx = await import('@mdx-js/rollup');

let env: Record<string, string> = {};
if (mode === 'development') {
// You need to run `pnpm start again if you change env to load it correctly`
env = loadEnv(mode, process.cwd(), 'BUBLIK_UI_DEV');
env = loadEnv(mode, process.cwd(), '');
}

const URL_PREFIX = env.BASE_URL?.replace('/v2', '');
const DJANGO_TARGET = env.BUBLIK_UI_DEV_BACKEND_TARGET;
const LOGS_TARGET = env.BUBLIK_UI_DEV_LOGS_TARGET;

// Derived
const API_PATHNAME = `${URL_PREFIX}/api/v2`;
const AUTH_PATHNAME = `${URL_PREFIX}/auth`;
const EXTERNAL_PATHNAME = `${URL_PREFIX}/external`;

return {
root: __dirname,
server: {
port: 4200,
host: 'localhost',
proxy: {
'/api/v2': {
target: env['BUBLIK_UI_DEV_BACKEND_TARGET'],
[API_PATHNAME]: {
target: DJANGO_TARGET,
changeOrigin: true,
secure: false
secure: false,
configure: createRequestLogger('API')
},
'/auth': {
target: env['BUBLIK_UI_DEV_BACKEND_TARGET'],
[AUTH_PATHNAME]: {
target: DJANGO_TARGET,
changeOrigin: true,
secure: false
secure: false,
configure: createRequestLogger('AUTH')
},
'/external': {
target: env['BUBLIK_UI_DEV_LOGS_TARGET'],
[EXTERNAL_PATHNAME]: {
target: LOGS_TARGET,
changeOrigin: true,
secure: false,
auth: env['BUBLIK_UI_DEV_LOGS_AUTH'],
followRedirects: true,
rewrite: (path) => {
rewrite: (path: string) => {
const externalUrl = /=([^&]+)/.exec(path)?.[1];

if (!externalUrl) {
Expand All @@ -45,7 +70,7 @@ export default defineConfig(async ({ mode }) => {
console.log(`[PROXY] Rewrite path: ${path}`);
console.log(`[PROXY] External URL: ${externalUrl}`);

return externalUrl;
return externalUrl.replace(LOGS_TARGET, '');
}
}
}
Expand All @@ -60,6 +85,9 @@ export default defineConfig(async ({ mode }) => {
],

build: {
outDir: '../../dist/apps/bublik',
reportCompressedSize: true,
commonjsOptions: { transformMixedEsModules: true },
rollupOptions: {
output: {
manualChunks: {
Expand All @@ -80,6 +108,11 @@ export default defineConfig(async ({ mode }) => {
// },

test: {
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/apps/bublik',
provider: 'v8'
},
globals: true,
cache: {
dir: '../../node_modules/.vitest'
Expand Down
5 changes: 4 additions & 1 deletion libs/services/bublik-api/src/lib/endpoints/log-endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
BublikHttpError,
isBublikParsableError
} from '../error-handling';
import { config } from '@/bublik/config';

type GetLogJsonInputs = {
id: string | number;
Expand Down Expand Up @@ -63,7 +64,9 @@ export const logEndpoints = {

const options: RequestInit = { credentials: 'include' };

const response = await fetch(externalUrl, options);
const response = config.isDev
? await fetch(`${config.rootUrl}/external?url=${externalUrl}`)
: await fetch(externalUrl, options);

if (!response.ok) throw getBublikFromStatusCode(response);

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"nx": "nx",
"start": "nx serve",
"build": "nx build",
"docker:build": "docker build -f apps/bublik/Dockerfile.dev . -t bublik-ui",
"docker:start": "docker run -it --rm -p 4200:4200 -v $(pwd):/app -v /app/node_modules --env-file apps/bublik/.env.local bublik-ui",
"bublik:build-all": "nx run bublik:build-all",
"bublik:build-storybook": "nx run bublik:storybook:ci",
"bublik:storybook": "nx run bublik:storybook",
Expand Down
Loading