From f56362fd257f114af4c8405e3c3c7513dd115d76 Mon Sep 17 00:00:00 2001 From: Vladimir nett00n Budylnikov Date: Sun, 25 Jan 2026 23:44:16 +0400 Subject: [PATCH] add docker and docker-compose --- .claude/settings.local.json | 7 +++ .dockerignore | 34 +++++++++++ .gitignore | 34 +++++------ .yarnrc.yml | 2 - Dockerfile | 35 +++++++++++ Dockerfile.dev | 51 ++++++++++++++++ docker-compose.yml | 110 ++++++++++++++++++++++++++++++++++ medusa-config.js | 63 ++++++++++++++++++++ package.json | 1 + start.sh | 114 ++++++++++++++++++++++++++++++++++++ tsconfig.json | 6 ++ 11 files changed, 438 insertions(+), 19 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 Dockerfile.dev create mode 100644 docker-compose.yml create mode 100644 medusa-config.js create mode 100755 start.sh create mode 100644 tsconfig.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..373aaff873 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(sudo docker compose:*)" + ] + } +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..03003170b3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,34 @@ +node_modules +.git +.env +.env* +*.log +.nyc_output +coverage +.DS_Store +.turbo +.idea +.vscode + +# Test files +integration-tests/ +**/__tests__/ +**/*.spec.ts +**/*.test.ts + +# Documentation +www/ +**/*.md +!README.md +docs/ + +# Build artifacts +**/dist/ +**/build/ + +# Cache +.yarn/cache +.yarn/install-state.gz + +# Misc +.claude/ diff --git a/.gitignore b/.gitignore index 9080d17679..94469b4abb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,21 @@ -.env node_modules -*yarn-error.log -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -packages/**/.yarn/* -integration-tests/**/.yarn/* -www/**/.yarn/* - -packages/**/plugins/**/.medusa/ - +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.git +.gitignore +README.md +.env.test +.nyc_output +coverage .DS_Store +*.log +dist +build +.yarn +.env +.env* +/packages .eslintcache @@ -34,4 +34,4 @@ dist/** /packages/**/.cache .cursorignore -**/.medusa \ No newline at end of file +**/.medusa diff --git a/.yarnrc.yml b/.yarnrc.yml index b8a66c9613..e29c474693 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -11,5 +11,3 @@ plugins: spec: "@yarnpkg/plugin-workspace-tools" - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs spec: "@yarnpkg/plugin-interactive-tools" - -yarnPath: .yarn/releases/yarn-3.2.1.cjs diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..d15c6a8404 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +ARG NODE_VERSION=20 +ARG NODE_ENV=production + +FROM node:${NODE_VERSION}-alpine AS deps +WORKDIR /server +RUN corepack enable && corepack prepare yarn@3.2.1 --activate +COPY package.json yarn.lock .yarnrc.yml ./ +COPY .yarn .yarn +# Copy package.json files to preserve workspace structure +COPY packages packages +RUN yarn install --immutable + +FROM node:${NODE_VERSION}-alpine AS builder +WORKDIR /server +RUN corepack enable && corepack prepare yarn@3.2.1 --activate +COPY --from=deps /server . +COPY . . +RUN yarn build + +# Build draft-order plugin admin extensions +RUN cd packages/plugins/draft-order && npx medusa plugin:build + +FROM node:${NODE_VERSION}-alpine AS runtime +WORKDIR /server +RUN addgroup -g 1001 medusa && adduser -D -u 1001 -G medusa medusa +RUN corepack enable && corepack prepare yarn@3.2.1 --activate +COPY --from=builder --chown=medusa:medusa /server . +USER medusa +EXPOSE 9000 5173 +HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \ + CMD wget -q --spider http://localhost:9000/health || exit 1 +LABEL org.opencontainers.image.description="Medusa Commerce Platform" + +ENTRYPOINT [] +CMD ["./start.sh"] diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000000..8c8dc99f79 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,51 @@ +ARG NODE_VERSION=20 +ARG NODE_ENV=production + +FROM node:${NODE_VERSION}-alpine + +ARG UID=1000 +ARG GID=1000 + +WORKDIR /server + +# Install dependencies for native modules +RUN apk add --no-cache python3 make g++ git + +# Enable Corepack +RUN corepack enable && corepack prepare yarn@3.2.1 --activate + +# Create non-root user (remove default node user if it conflicts) +RUN deluser --remove-home node 2>/dev/null || true && \ + addgroup -g ${GID} medusa && \ + adduser -D -u ${UID} -G medusa medusa && \ + chown medusa:medusa /server + +# Switch to non-root user +USER medusa + +# Copy dependency files first for better caching +COPY --chown=medusa:medusa package.json yarn.lock .yarnrc.yml ./ +COPY --chown=medusa:medusa .yarn .yarn +COPY --chown=medusa:medusa packages packages + +# Install all dependencies (including devDependencies) +RUN yarn install + +# Copy remaining files +COPY --chown=medusa:medusa . . + +# Build packages +RUN yarn build + +# Build draft-order plugin admin extensions +RUN cd packages/plugins/draft-order && npx medusa plugin:build + +EXPOSE 9000 5173 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \ + CMD wget -q --spider http://localhost:9000/health || exit 1 + +LABEL org.opencontainers.image.description="Medusa Commerce Platform (Dev)" + +ENTRYPOINT [] +CMD ["./start.sh"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..f9edf80a74 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,110 @@ +services: + # PostgreSQL Database + postgres: + image: postgres:15-alpine + restart: unless-stopped + environment: + POSTGRES_DB: medusa-store + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - "5432:5432" + volumes: + - ${GLOBAL_DATA_FOLDER:-/Data}/${SERVICE_NAME_OVERRIDE:-my-medusa-store}/postgres_data:/var/lib/postgresql/data + networks: + - default + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d medusa-store"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + # Redis + redis: + image: redis:7-alpine + restart: unless-stopped + ports: + - "6379:6379" + networks: + - default + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + medusa: + build: + context: . + dockerfile: Dockerfile.dev + args: + UID: ${UID:-1000} + GID: ${GID:-1000} + user: "${UID:-1000}:${GID:-1000}" + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + ports: + - "9000:9000" + environment: + - NODE_ENV=development + - DATABASE_URL=postgres://postgres:postgres@postgres:5432/medusa-store + - REDIS_URL=redis://redis:6379 + env_file: + - .env + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost:9000/health"] + interval: 30s + timeout: 10s + start_period: 120s + retries: 3 + develop: + watch: + - action: sync + path: ./packages + target: /server/packages + - action: sync + path: ./medusa-config.js + target: /server/medusa-config.js + - action: sync + path: ./start.sh + target: /server/start.sh + - action: rebuild + path: package.json + - action: rebuild + path: yarn.lock + networks: + - default + - traefik_default + labels: + com.centurylinklabs.watchtower.enable: "true" + traefik.docker.network: traefik_default + traefik.enable: true + traefik.http.routers.my-medusa-store.entrypoints: websecure + traefik.http.routers.my-medusa-store.rule: Host(`${SERVICE_NAME_OVERRIDE:-my-medusa-store}.${DOMAIN_NAME:-local}`) + traefik.http.routers.my-medusa-store.service: my-medusa-store + traefik.http.routers.my-medusa-store.tls: true + traefik.http.routers.my-medusa-store.tls.certresolver: letsencrypt-cloudflare-dns-challenge + traefik.http.services.my-medusa-store.loadbalancer.server.port: 9000 + local.yacht.port.9000: WebUI + traefik.http.routers.my-medusa-store-admin.entrypoints: websecure + traefik.http.routers.my-medusa-store-admin.rule: Host(`${SERVICE_NAME_OVERRIDE:-my-medusa-store}-admin.${DOMAIN_NAME:-local}`) + traefik.http.routers.my-medusa-store-admin.service: my-medusa-store-admin + traefik.http.routers.my-medusa-store-admin.tls: true + traefik.http.routers.my-medusa-store-admin.tls.certresolver: letsencrypt-cloudflare-dns-challenge + traefik.http.services.my-medusa-store-admin.loadbalancer.server.port: 5173 + local.yacht.port.5173: WebUI + +volumes: + postgres_data: + +networks: + default: + driver: bridge + traefik_default: + external: true diff --git a/medusa-config.js b/medusa-config.js new file mode 100644 index 0000000000..d4edcc81d3 --- /dev/null +++ b/medusa-config.js @@ -0,0 +1,63 @@ +const { defineConfig, Modules } = require("@medusajs/framework/utils") + +module.exports = defineConfig({ + admin: { + vite: () => { + return { + server: { + allowedHosts: [ + ".nett00n.org", + "reka.nett00n.org", + "http://reka.nett00n.org:9000", + ], + } + } + }, + }, + + projectConfig: { + databaseUrl: process.env.DATABASE_URL, + databaseDriverOptions: { + connection: { + ssl: false, + }, + }, + redisUrl: process.env.REDIS_URL, + http: { + jwtSecret: process.env.JWT_SECRET || "supersecret", + cookieSecret: process.env.COOKIE_SECRET || "supersecret", + }, + }, + modules: { + [Modules.FILE]: { + resolve: "@medusajs/file", + options: { + providers: [ + { + resolve: "@medusajs/file-local", + id: "local", + options: { + upload_dir: "uploads", + backend_url: "http://localhost:9000", + }, + }, + ], + }, + }, + [Modules.NOTIFICATION]: { + resolve: "@medusajs/notification", + options: { + providers: [ + { + resolve: "@medusajs/notification-local", + id: "local", + options: { + name: "Local Notification Provider", + channels: ["feed"], + }, + }, + ], + }, + }, + }, +}) diff --git a/package.json b/package.json index 52bbbde34b..bfbb58fec7 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@atomico/rollup-plugin-sizes": "^1.1.4", "@aws-sdk/client-dynamodb": "^3.218.0", "@faker-js/faker": "^9.2.0", + "@medusajs/medusa": "workspace:^", "@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-replace": "^5.0.2", "@storybook/addon-essentials": "^8.3.5", diff --git a/start.sh b/start.sh new file mode 100755 index 0000000000..117e553e8f --- /dev/null +++ b/start.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env sh +set -eu + +# Colors (disabled if not a terminal) +if [ -t 1 ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + BLUE='\033[0;34m' + NC='\033[0m' +else + RED='' GREEN='' YELLOW='' BLUE='' NC='' +fi + +timestamp() { + date -u +%Y-%m-%dT%H:%M:%SZ +} + +log_info() { + printf "💙 ${BLUE}[%s] [INFO]${NC} %s\n" "$(timestamp)" "$1" +} + +log_success() { + printf "💚 ${GREEN}[%s] [SUCCESS]${NC} %s\n" "$(timestamp)" "$1" +} + +log_error() { + printf "❤ ${RED}[%s] [ERROR]${NC} %s\n" "$(timestamp)" "$1" >&2 +} + +log_warn() { + printf "💛 ${YELLOW}[%s] [WARN]${NC} %s\n" "$(timestamp)" "$1" +} + +cleanup() { + log_warn "Received shutdown signal, cleaning up..." + # Kill child processes + jobs -p | xargs kill 2>/dev/null || true + exit 130 +} + +trap cleanup INT TERM + +# Pre-flight checks +log_info "Starting Medusa development environment..." + +if find . -maxdepth 1 -mindepth 1 ! -uid "$UID" -print -quit | grep -q .; then + echo "ERROR: files not owned by UID=$UID exist in current directory. If running in DEVELOPER_MODE, fill UID and GID of your user in .env file and rebuild" >&2 + find . -maxdepth 1 -mindepth 1 ! -uid "$UID" -ls >&2 + exit 1 +fi + +if find . -maxdepth 1 -mindepth 1 ! -gid "$GID" -print -quit | grep -q .; then + echo "ERROR: files not owned by GID=$GID exist in current directory. If running in DEVELOPER_MODE, fill UID and GID of your user in .env file and rebuild" >&2 + find . -maxdepth 1 -mindepth 1 ! -gid "$GID" -ls >&2 + exit 1 +fi + +if [ ! -f "medusa-config.js" ]; then + log_error "medusa-config.js not found. Are you in the Medusa project root?" + exit 1 +fi + +if [ ! -d "node_modules" ]; then + log_error "node_modules/ not found. Run 'yarn install' first." + exit 1 +fi + +if [ -z "${DATABASE_URL:-}" ]; then + log_error "DATABASE_URL environment variable is not set" + exit 1 +fi + +# Database connectivity check (optional, can be skipped with --skip-db-check) +if [ "${1:-}" != "--skip-db-check" ]; then + log_info "Checking database connectivity..." + # Simple check using npx medusa (will fail if DB is unreachable) + if ! npx medusa db:check 2>/dev/null; then + log_warn "Database connectivity check failed (this is OK if db:check doesn't exist)" + else + log_success "Database is reachable" + fi +fi + +# Run migrations +log_info "Running database migrations..." +start_time=$(date +%s) + +if npx medusa db:migrate; then + migration_time=$(($(date +%s) - start_time)) + log_success "Migrations completed in ${migration_time}s" +else + log_error "Migration failed" + exit 1 +fi + +# Create admin user if credentials are provided +if [ -n "${ADMIN_LOGIN:-}" ] && [ -n "${ADMIN_PASSWORD:-}" ]; then + log_info "Creating admin user with email: ${ADMIN_LOGIN}..." + + if npx medusa user -e "${ADMIN_LOGIN}" -p "${ADMIN_PASSWORD}"; then + log_success "Admin user created successfully" + else + log_warn "Admin user creation failed (user may already exist)" + fi +elif [ -n "${ADMIN_LOGIN:-}" ] || [ -n "${ADMIN_PASSWORD:-}" ]; then + log_warn "Both ADMIN_LOGIN and ADMIN_PASSWORD must be set to create admin user" +fi + +# Start development server +log_info "Starting Medusa development server..." +log_info "Press Ctrl+C to stop" + +npx medusa develop diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..19d4ac2e6f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": {} + } +}