diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 63b421e6..187bb742 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -5,8 +5,7 @@ on: inputs: tags: description: "Tags for the image" - required: true - default: "debug" + required: false images: description: "Images to build and push" required: true diff --git a/README.md b/README.md index 799a7f3d..a379d2ee 100644 --- a/README.md +++ b/README.md @@ -196,29 +196,29 @@ The server is configured via a config file. Here is an example config file: ```json5 { - "connection": { - "port": 5101, + connection: { + port: 5101, // for the example retrom-db container below - "db_url": "postgres://minecraft_steve:super_secret_password@retrom-db/retrom" + db_url: "postgres://minecraft_steve:super_secret_password@retrom-db/retrom", // or, bring your own database: // "db_url": "postgres://{db_user}:{db_password}@{db_host}/{db_name}" }, - "content_directories": [ + content_directories: [ { - "path": "path/to/my/library/", - "storage_type": "MultiFileGame" + path: "path/to/my/library/", + storage_type: "MultiFileGame", }, { - "path": "path/to/my/library/with/single_file_games/", - "storage_type": "SingleFileGame" - } + path: "path/to/my/library/with/single_file_games/", + storage_type: "SingleFileGame", + }, ], - "igdb": { - "client_secret": "super_secret_client_secret!!!1", - "client_id": "my_IGDB_ID_1234" - } + igdb: { + client_secret: "super_secret_client_secret!!!1", + client_id: "my_IGDB_ID_1234", + }, } ``` @@ -239,24 +239,24 @@ Here is the example config file: ```json5 { - "connection": { - "port": 5101, - "db_url": "postgres://minecraft_steve:super_secret_password@retrom-db/retrom" + connection: { + port: 5101, + db_url: "postgres://minecraft_steve:super_secret_password@retrom-db/retrom", }, - "content_directories": [ + content_directories: [ { - "path": "/library1", // this path is **inside the container** - "storage_type": "MultiFileGame" + path: "/library1", // this path is **inside the container** + storage_type: "MultiFileGame", }, { - "path": "/library2", // this path is **inside the container** - "storage_type": "SingleFileGame" - } + path: "/library2", // this path is **inside the container** + storage_type: "SingleFileGame", + }, ], - "igdb": { - "client_secret": "super_secret_client_secret!!!1", - "client_id": "my_IGDB_ID_1234" - } + igdb: { + client_secret: "super_secret_client_secret!!!1", + client_id: "my_IGDB_ID_1234", + }, } ``` @@ -268,6 +268,7 @@ services: image: ghcr.io/jmberesford/retrom-service:latest ports: - 5101:5101 + - 3000:3000 # to access the web client volumes: - /home/minecraft_steve/config_dir:/config/ # directory containing your config file - /home/minecraft_steve/library1:/library1 # directory containing your first library @@ -289,11 +290,12 @@ services: POSTGRES_USER: minecraft_steve # db user, used to connect to the db, should match the db_user in your config file POSTGRES_PASSWORD: super_secret_password # db password for above user, should match the db_password in your config file POSTGRES_DB: retrom # db name, should match the db_name in your config file - ``` You can then run `docker-compose up` in the directory containing your `docker-compose.yml` file to start the service. +The web client will be accessible at port 3000, and the service itself on port 5101 -- which can be accessed by any desktop clients. + ### Client #### Desktop Client @@ -319,21 +321,5 @@ The following may help you differentiate between the different versions: #### Web Client -> [!NOTE] -> There are plans to bundle the web client along with the service in a single container in the future, -> for ease of use. - -The web client is currently only available as a Docker container. You can run it with the following -`docker-compose.yml` file, as an example: - -```yaml -retrom-web: - image: ghcr.io/jmberesford/retrom-web:latest - container_name: retrom-web - hostname: retrom-web - ports: - - 3000:3000 -``` - -Then, you can run `docker-compose up` in the directory containing your `docker-compose.yml` file to start the service. -You can then reach the web client at `http://localhost:3000` in your browser, if running locally. +> [!WARNING] +> The web client image has been deprecated. Use the web client bundled with the service image instead. diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.yml similarity index 65% rename from docker/docker-compose.dev.yml rename to docker/docker-compose.yml index 1f147772..f0f720c1 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.yml @@ -30,25 +30,11 @@ services: hostname: retrom env_file: ./.env ports: - - 5101:${RETROM_PORT:-5101} + - 5101:5101 + - 3000:3000 volumes: - ${CONTENT_DIR1:-./mock_content/}/:/lib1 - ${CONTENT_DIR2:-./mock_content_single}:/lib2 - - ${CONFIG_DIR:-./config-dev/}:/config + - ${CONFIG_DIR:-./config_dev/}:/config depends_on: - retrom-db - - retrom-web: - build: - context: ../ - dockerfile: docker/web.Dockerfile - container_name: retrom-web - hostname: retrom-web - env_file: ./.env - ports: - - 3000:${RETROM_WEB_PORT:-3000} - environment: - VITE_RETROM_WEB_PORT: ${RETROM_WEB_PORT:-3000} - VITE_RETROM_HOST: ${RETROM_HOST:-retrom:5101} - depends_on: - - retrom diff --git a/docker/service.Dockerfile b/docker/service.Dockerfile index 72477204..a936c8a6 100644 --- a/docker/service.Dockerfile +++ b/docker/service.Dockerfile @@ -1,26 +1,67 @@ -FROM rust:slim-bookworm as builder +FROM node:20-bookworm-slim AS common +COPY . ./app + +### WEB CLIENT +FROM node:20-bookworm-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable + +FROM base AS web-builder +RUN apt-get update && apt-get install protobuf-compiler ca-certificates -y + +COPY --from=common /app /app +WORKDIR /app + +RUN pnpm install --frozen-lockfile +RUN pnpm exec buf generate +RUN pnpm --filter=web build +RUN pnpm deploy --filter=web /web +RUN mv /app/packages/client/web/dist /web/dist + +### SERVICE BINARY +FROM rust:slim-bookworm AS service-builder + +COPY --from=common /app /usr/src/retrom WORKDIR /usr/src/retrom -COPY . . RUN apt-get update && apt-get install protobuf-compiler openssl pkg-config libssl-dev libpq-dev -y RUN cargo install --path ./packages/service -FROM debian:bookworm-slim -RUN apt-get update && apt-get install openssl libssl-dev libpq-dev ca-certificates -y && rm -rf /var/lib/apt/lists/* +FROM base AS runner +ENV UID=1505 +ENV GID=1505 +ENV UMASK=000 +ENV USER=retrom -ENV UID=1001 -ENV GID=1001 +RUN addgroup --gid $GID ${USER} +RUN adduser --gid $GID --uid $UID ${USER} + +RUN apt-get update && apt-get install openssl libssl-dev libpq-dev ca-certificates -y + +### Service env ENV RUST_LOG=info +ENV RETROM_CONFIG=/config/config.json +EXPOSE 5101 + +### Web env +ENV NODE_ENV=production +ENV RETROM_LOCAL_SERVICE_HOST=http://localhost:5101 +EXPOSE 3000 -RUN addgroup --system --gid $GID retrom -RUN adduser --system --uid $UID retrom +COPY --from=service-builder /usr/local/cargo/bin/retrom-service /app/retrom-service +COPY docker/start.sh /app/start.sh +RUN chmod +x /app/start.sh -COPY --from=builder /usr/local/cargo/bin/retrom-service /app/retrom-service +COPY --from=web-builder /web /app/web + +RUN chmod -R 777 /app/web WORKDIR /app -USER retrom -ENV RETROM_CONFIG=/config/config.json +USER ${USER} + +RUN umask ${UMASK} -CMD ["./retrom-service"] +CMD ./start.sh diff --git a/docker/start.sh b/docker/start.sh new file mode 100644 index 00000000..d064154a --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Start the web server +cd /app/web +pnpm preview & + +# Start the API server +cd /app + +./retrom-service & + +wait -n + +exit $? diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile index 32c8fade..883d27a9 100644 --- a/docker/web.Dockerfile +++ b/docker/web.Dockerfile @@ -1,48 +1,4 @@ -FROM node:20-alpine AS base +FROM alpine:3.14 -FROM base AS deps -RUN apk add --no-cache libc6-compat protobuf-dev -WORKDIR /app +CMD echo 'This image has been deprecated. Please use the web-client bundled in retrom-service.' && exit 1 -# top level deps -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ - -# package deps -RUN mkdir -p packages/client/ && mkdir -p packages/codegen/ -COPY buf*.yaml ./ -COPY packages/codegen/protos/ ./packages/codegen/protos/ -COPY packages/client/package.json ./packages/client/ -COPY packages/client/web/ ./packages/client/web/ - -RUN corepack enable pnpm && pnpm i - -RUN pnpm exec buf generate - -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/. ./ - -RUN corepack enable pnpm && pnpm --filter web build - -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV=production -ENV UID=1001 -ENV GID=1001 -ENV PORT=3000 -ENV RETROM_PORT=5101 -ENV RETROM_HOSTNAME=http://localhost -ENV RETROM_HOST=http://localhost:5101 - -RUN addgroup --system --gid $GID retrom -RUN adduser --system --uid $UID retrom - - -COPY --from=builder --chown=retrom:retrom /app/packages/client/web/dist ./dist - -USER retrom - -EXPOSE $PORT - -CMD npx vite preview --host --port $PORT diff --git a/packages/client/web/package.json b/packages/client/web/package.json index c66a89ac..e755be73 100644 --- a/packages/client/web/package.json +++ b/packages/client/web/package.json @@ -2,6 +2,7 @@ "name": "web", "private": true, "type": "module", + "packageManager": "pnpm@9.5.0", "scripts": { "dev": "vite", "build": "tsc -b && vite build", diff --git a/packages/client/web/src/components/menubar/file-menu/index.tsx b/packages/client/web/src/components/menubar/file-menu/index.tsx index 240e00bf..a4ca30cf 100644 --- a/packages/client/web/src/components/menubar/file-menu/index.tsx +++ b/packages/client/web/src/components/menubar/file-menu/index.tsx @@ -5,7 +5,6 @@ import { MenubarSeparator, } from "@/components/ui/menubar"; import { ConfigMenuItem } from "./config-menu-item"; -import { DesktopOnly } from "@/lib/env"; import { CloseMenuItem } from "./close-menu-item"; export function FileMenu() { @@ -18,11 +17,9 @@ export function FileMenu() { - - + - - + ); diff --git a/packages/client/web/src/components/menubar/index.tsx b/packages/client/web/src/components/menubar/index.tsx index 38d91713..09f66370 100644 --- a/packages/client/web/src/components/menubar/index.tsx +++ b/packages/client/web/src/components/menubar/index.tsx @@ -21,7 +21,9 @@ export function Menubar() { Retrom - + + + diff --git a/packages/client/web/src/components/modals/setup/context.tsx b/packages/client/web/src/components/modals/setup/context.tsx index b6fa451c..e0f63cb4 100644 --- a/packages/client/web/src/components/modals/setup/context.tsx +++ b/packages/client/web/src/components/modals/setup/context.tsx @@ -1,4 +1,3 @@ -import { checkIsDesktop } from "@/lib/env"; import { useNavigate } from "@tanstack/react-router"; import { createContext, @@ -33,7 +32,7 @@ export function useSetupModal() { } const nextStepTransitions: Record = { - ServerHost: checkIsDesktop() ? "ClientName" : "Confirm", + ServerHost: "ClientName", ClientName: "Confirm", Confirm: "ServerHost", }; @@ -41,7 +40,7 @@ const nextStepTransitions: Record = { const previousStepTransitions: Record = { ServerHost: undefined, ClientName: "ServerHost", - Confirm: checkIsDesktop() ? "ClientName" : "ServerHost", + Confirm: "ClientName", }; export function SetupModalProvider(props: React.PropsWithChildren) { diff --git a/packages/client/web/src/components/modals/setup/index.tsx b/packages/client/web/src/components/modals/setup/index.tsx index bb7359e1..2c4f3bfe 100644 --- a/packages/client/web/src/components/modals/setup/index.tsx +++ b/packages/client/web/src/components/modals/setup/index.tsx @@ -2,17 +2,20 @@ import { Dialog, DialogContent } from "@/components/ui/dialog"; import { SetupModalProvider } from "./context"; import { SetupModalSteps } from "./steps"; import { Route as RootRoute } from "@/routes/__root"; +import { DesktopOnly } from "@/lib/env"; export function SetupModal() { const { setupModal } = RootRoute.useSearch(); return ( - - - - - - - + + + + + + + + + ); } diff --git a/packages/client/web/src/providers/retrom-client/index.tsx b/packages/client/web/src/providers/retrom-client/index.tsx index b08fa36d..d00765f8 100644 --- a/packages/client/web/src/providers/retrom-client/index.tsx +++ b/packages/client/web/src/providers/retrom-client/index.tsx @@ -1,6 +1,7 @@ import { PropsWithChildren, createContext, useContext, useMemo } from "react"; import { RetromClient } from "./client"; import { useConfig } from "../config"; +import { checkIsDesktop } from "@/lib/env"; const context = createContext(undefined); @@ -11,7 +12,9 @@ export function RetromClientProvider(props: PropsWithChildren) { const { children } = props; const client = useMemo(() => { - const host = hostname + (port ? `:${port}` : ""); + const host = checkIsDesktop() + ? hostname + (port ? `:${port}` : "") + : "/api"; return new RetromClient(host); }, [hostname, port]); diff --git a/packages/client/web/vite.config.ts b/packages/client/web/vite.config.ts index e49ccf64..0f6ac255 100644 --- a/packages/client/web/vite.config.ts +++ b/packages/client/web/vite.config.ts @@ -3,10 +3,32 @@ import react from "@vitejs/plugin-react"; import path from "path"; import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; +const localServiceHost = + process.env.RETROM_LOCAL_SERVICE_HOST || "http://localhost:5101"; + // https://vitejs.dev/config/ export default defineConfig({ server: { port: 3000, + host: "0.0.0.0", + proxy: { + "/api": { + target: localServiceHost, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ""), + }, + }, + }, + preview: { + port: 3000, + host: "0.0.0.0", + proxy: { + "/api": { + target: localServiceHost, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ""), + }, + }, }, plugins: [TanStackRouterVite(), react()], resolve: {