diff --git a/.github/workflows/practika.yml b/.github/workflows/practika.yml new file mode 100644 index 0000000..c384f02 --- /dev/null +++ b/.github/workflows/practika.yml @@ -0,0 +1,65 @@ +name: Build and Publish + +on: + push: + branches: [ "practika-develop" ] + +jobs: + build-server: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Server + uses: docker/build-push-action@v4 + with: + context: ./server + push: true + tags: ${{ secrets.DOCKER_USERNAME }}/eduplatform-server:latest + + build-web: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Web + uses: docker/build-push-action@v4 + with: + context: ./frontend + file: frontend/packages/web/Dockerfile + push: true + tags: ${{ secrets.DOCKER_USERNAME }}/eduplatform-web:latest + + build-admin: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Admin + uses: docker/build-push-action@v4 + with: + context: ./frontend + file: frontend/packages/admin/Dockerfile + push: true + tags: ${{ secrets.DOCKER_USERNAME }}/eduplatform-admin:latest \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1d8fb57 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +version: '3.8' + +services: + db: + image: postgres:15-alpine + container_name: edu_postgres + environment: + POSTGRES_USER: myuser + POSTGRES_PASSWORD: mypassword + POSTGRES_DB: mydb + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"] + interval: 5s + timeout: 5s + retries: 5 + restart: always + + server: + build: ./server + container_name: edu_server + ports: + - "3000:3000" + environment: + DATABASE_URL: "postgresql://myuser:mypassword@db:5432/mydb?schema=public" + PORT: 3000 + JWT_SECRET: "super-secret" + depends_on: + db: + condition: service_healthy + command: > + sh -c "npx prisma migrate deploy && node dist/main.js" + restart: always + + web: + build: + context: ./frontend + dockerfile: packages/web/Dockerfile + container_name: edu_web + ports: + - "8080:80" + depends_on: + - server + restart: always + + admin: + build: + context: ./frontend + dockerfile: packages/admin/Dockerfile + container_name: edu_admin + ports: + - "8081:80" + depends_on: + - server + restart: always + +volumes: + postgres_data: \ No newline at end of file diff --git a/frontend/packages/admin/Dockerfile b/frontend/packages/admin/Dockerfile new file mode 100644 index 0000000..ecd0492 --- /dev/null +++ b/frontend/packages/admin/Dockerfile @@ -0,0 +1,28 @@ +FROM node:20-alpine AS builder +RUN corepack enable && corepack prepare pnpm@latest --activate +WORKDIR /app + +COPY package.json ./ + +RUN echo "packages:" > pnpm-workspace.yaml && \ + echo " - 'packages/*'" >> pnpm-workspace.yaml + +COPY package.json ./ + +RUN echo "packages:" > pnpm-workspace.yaml && \ + echo " - 'packages/*'" >> pnpm-workspace.yaml + +RUN echo "link-workspace-packages=true" > .npmrc && \ + echo "prefer-workspace-packages=true" >> .npmrc && \ + echo "shamefully-hoist=true" >> .npmrc + +COPY packages ./packages + +RUN pnpm install + +RUN pnpm --filter admin build + +FROM nginx:alpine +COPY --from=builder /app/packages/admin/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/packages/admin/package.json b/frontend/packages/admin/package.json index 2c374fb..ab52046 100644 --- a/frontend/packages/admin/package.json +++ b/frontend/packages/admin/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vite build", "preview": "vite preview", "lint": "eslint ." }, diff --git a/frontend/packages/web/Dockerfile b/frontend/packages/web/Dockerfile new file mode 100644 index 0000000..7067573 --- /dev/null +++ b/frontend/packages/web/Dockerfile @@ -0,0 +1,25 @@ +FROM node:20-alpine AS builder +RUN corepack enable && corepack prepare pnpm@latest --activate +WORKDIR /app + +COPY package.json ./ + +RUN echo "packages:" > pnpm-workspace.yaml && \ + echo " - 'packages/*'" >> pnpm-workspace.yaml + +RUN echo "link-workspace-packages=true" > .npmrc && \ + echo "prefer-workspace-packages=true" >> .npmrc && \ + echo "shamefully-hoist=true" >> .npmrc + +COPY packages ./packages + +RUN ls -la packages/ui/package.json + +RUN pnpm install + +RUN pnpm --filter web build + +FROM nginx:alpine +COPY --from=builder /app/packages/web/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/packages/web/package.json b/frontend/packages/web/package.json index 40bc3f9..e1aa36e 100644 --- a/frontend/packages/web/package.json +++ b/frontend/packages/web/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vite build", "preview": "vite preview", "lint": "eslint ." }, diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 4226b78..ff31f5f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: pnpm: specifier: ^10.20.0 version: 10.21.0 + xlsx: + specifier: ^0.18.5 + version: 0.18.5 devDependencies: prettier: specifier: ^3.2.4 @@ -18,6 +21,27 @@ importers: packages: + adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + + cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + pnpm@10.21.0: resolution: {integrity: sha512-2jM3Jn5AD909R5psaAeaxtsB2MpPZ1cgg+cid1p5Z4inqZVmE3SeAA+sINQktZT3p5Gl9OLhNYHF75R/JpaKQA==} engines: {node: '>=18.12'} @@ -28,8 +52,56 @@ packages: engines: {node: '>=14'} hasBin: true + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + snapshots: + adler-32@1.3.1: {} + + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + + codepage@1.15.0: {} + + crc-32@1.2.2: {} + + frac@1.1.2: {} + pnpm@10.21.0: {} prettier@3.6.2: {} + + ssf@0.11.2: + dependencies: + frac: 1.1.2 + + wmf@1.0.2: {} + + word@0.3.0: {} + + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 diff --git a/server/Dockerfile b/server/Dockerfile index a5d4094..1b269dd 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,37 +1,36 @@ FROM node:20-alpine AS base -# Install dependencies only when needed FROM base AS deps -RUN apk add --no-cache libc6-compat +RUN apk add --no-cache libc6-compat openssl WORKDIR /app COPY package.json package-lock.json* ./ RUN npm ci -# Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . - -# Generate Prisma Client RUN npx prisma generate RUN npm run build -# Production image, copy all the files and run nest FROM base AS runner WORKDIR /app ENV NODE_ENV production +RUN apk add --no-cache libc6-compat openssl + RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nestjs -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/prisma ./prisma -COPY --from=builder /app/package.json ./package.json +COPY --from=builder --chown=nestjs:nodejs /app/node_modules ./node_modules +COPY --from=builder --chown=nestjs:nodejs /app/dist ./dist +COPY --from=builder --chown=nestjs:nodejs /app/prisma ./prisma +COPY --from=builder --chown=nestjs:nodejs /app/package.json ./package.json + +RUN chown -R nestjs:nodejs /app USER nestjs @@ -39,8 +38,4 @@ EXPOSE 3000 ENV PORT 3000 -CMD ["node", "dist/main.js"] - - - - +CMD ["node", "dist/main.js"] \ No newline at end of file