diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..dacef15dac --- /dev/null +++ b/.dockerignore @@ -0,0 +1,42 @@ +# Node modules (will be installed in container) +node_modules +*/node_modules +**/node_modules + +# Build outputs +dist +build +coverage +*.tsbuildinfo + +# Development files +.git +.github +.vscode +*.log +# Note: We need yarn.lock and package-lock.json for dependency resolution +# *.lock + +# Docker +*.dockerignore +Dockerfile* +docker-compose* +.docker + +# Docker volumes +docker_data/ +docker_volumes/ + +# Development +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Cypress +cypress/videos +cypress/screenshots + +# Misc +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000..b4ef6d10b6 --- /dev/null +++ b/.env.example @@ -0,0 +1,31 @@ +# Environment Variables for Docker Compose +# Copy this file to .env and modify as needed + +# Authentication Type (SNAuth or IdentityServer) +AUTH_TYPE=SNAuth + +# Node Environment +NODE_ENV=production + +# Ports +SENSENET_CLIENT_PORT=8080 +SENSENET_DEV_PORT=3000 + +# Development Settings (for hot reload) +CHOKIDAR_USEPOLLING=true +WATCHPACK_POLLING=true + +# Database Settings (if using database service) +# DB_PASSWORD=YourPassword123! +# DB_NAME=SenseNet +# DB_USER=sa + +# Redis Settings (if using redis service) +# REDIS_PASSWORD= + +# Traefik Settings (if using traefik service) +# TRAEFIK_DOMAIN=sensenet.local + +# Backend API URL (if connecting to external backend) +# REACT_APP_SERVICE_URL=https://dev.demo.sensenet.com +# REACT_APP_IDENTITY_SERVER_URL=https://is.demo.sensenet.com \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 623ff317fb..c55783cead 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,7 +12,7 @@ module.exports = { ], parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint', 'react', 'cypress', 'jsdoc', 'import', 'react-hooks'], - env: { browser: true, node: true, es6: true, jest: true, 'cypress/globals': true }, + env: { browser: true, node: true, es6: true, 'cypress/globals': true }, parserOptions: { ecmaVersion: 6, sourceType: 'module', @@ -36,6 +36,9 @@ module.exports = { }, rules: { 'react/prop-types': 0, + 'no-unused-vars': 'off', + 'import/export': 0, + '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-types': 'off', '@typescript-eslint/no-empty-function': 'off', @@ -45,6 +48,7 @@ module.exports = { '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/array-type': ['error', { default: 'array-simple', readonly: 'array-simple' }], 'require-jsdoc': 1, + 'cypress/unsafe-to-chain-command': 'off', 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks 'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies 'import/default': 0, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b642aede3a..e33ce6f9df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache + - uses: actions/cache@v4 with: path: ~/.cache/yarn key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} @@ -46,15 +46,9 @@ jobs: - name: build run: yarn build - - name: build examples - run: yarn build:examples - - name: build snapp run: yarn snapp build env: RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }} - - name: test - run: NODE_OPTIONS='--max-old-space-size=4096' yarn test --coverage --logHeapUsage - - uses: codecov/codecov-action@v1 diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index c8a43d2218..a55f4f6be7 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v4 # Updated from v1 to v4 + - uses: actions/cache@v4 with: path: ~/.cache/yarn key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} @@ -36,35 +36,3 @@ jobs: env: NETLIFY_SITE_ID: 94fb346c-b540-40f7-aaaf-21eee2a9c891 NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - - deploy-storybook: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions/cache@v4 # Updated from v1 to v4 - with: - path: ~/.cache/yarn - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: install - run: yarn install - env: - CYPRESS_INSTALL_BINARY: 0 - - - name: build - run: yarn build - - - name: build storybook - run: yarn storybook build-storybook - - - name: wake up deploy notifier - run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - - - name: Publish - run: npx netlify-cli@v2.41.0 deploy --dir=./examples/sn-react-component-docs/storybook-static --message ${{ github.event.pull_request.number }} - env: - NETLIFY_SITE_ID: 1747b330-27d8-4ddd-bf74-39469c257010 - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000000..4ae75c7563 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,68 @@ +name: Docker Image CI + +on: + push: + branches: + - 'develop' + - 'main' + - 'feature/docker-containerization' + - 'feature/sn-auth-package-extraimprovements' + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: + - 'develop' + - 'main' + - 'feature/sn-auth-package-extraimprovements' + +jobs: + build: + runs-on: ubuntu-latest + # Skip draft PRs + if: github.event.pull_request.draft == false || github.event_name == 'push' + + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Check if Dockerfile exists + id: check_dockerfile + run: | + if [ -f "Dockerfile" ]; then + echo "dockerfile_exists=true" >> $GITHUB_OUTPUT + else + echo "dockerfile_exists=false" >> $GITHUB_OUTPUT + echo "⚠️ No Dockerfile found, skipping Docker build" + fi + + - name: Set up Docker metadata + if: steps.check_dockerfile.outputs.dockerfile_exists == 'true' + id: meta + uses: docker/metadata-action@v5 + with: + images: sensenetcsp/sn-client + tags: | + # Clean branch name (e.g., feature-docker-containerization) + type=ref,event=branch + # Branch name with SHA (e.g., feature-docker-containerization-abc1234) + type=ref,event=branch,suffix=-{{sha}} + # Latest tag for main branch + type=raw,value=latest,enable={{is_default_branch}} + # PR number for pull requests + type=ref,event=pr + + - name: Login to DockerHub + if: steps.check_dockerfile.outputs.dockerfile_exists == 'true' && github.event_name == 'push' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + if: steps.check_dockerfile.outputs.dockerfile_exists == 'true' + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name == 'push' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 6a86cc155e..288a352a61 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,8 @@ jspm_packages/ # Misc .DS_Store +pipe\[0] +# Environment variable files +.env +.env.local +.env.*.local diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000000..0590481343 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,129 @@ +# Docker Setup for SenseNet Client + +This guide explains how to run the SenseNet client application using Docker. + +## 🚀 Quick Start + +### Development (with hot reload) +```bash +docker-compose -f docker-compose.dev.yml up -d +``` +- **URL**: http://localhost:8080 +- **Hot reload**: ✅ File changes are instantly reflected +- **Use case**: Active development + +### Production +```bash +docker-compose -f docker-compose.prod.yml up -d +``` +- **URL**: http://localhost:8080 +- **Hot reload**: ❌ Static built files +- **Use case**: Testing production builds, deployment + +## 📁 Files Overview + +| File | Purpose | +|------|---------| +| `Dockerfile` | Single Docker image for both dev and prod | +| `docker-compose.dev.yml` | Development setup with volume mounts | +| `docker-compose.prod.yml` | Production setup without volume mounts | +| `.dockerignore` | Excludes unnecessary files from build context | + +## 🔧 How It Works + +### Development Mode +- **Volume mounting**: Your local code is mounted into the container +- **File watching**: Changes trigger automatic rebuilds +- **Command**: `yarn snapp start` (webpack dev server with hot reload) + +### Production Mode +- **Built files**: Uses pre-built static files inside the container +- **No volumes**: Container is self-contained +- **Command**: `yarn snapp start` (same command, but runs webpack dev server on built files) + +## 🛠️ Common Commands + +```bash +# Start development +docker-compose -f docker-compose.dev.yml up -d + +# Stop development +docker-compose -f docker-compose.dev.yml down + +# Rebuild and start (after dependency changes) +docker-compose -f docker-compose.dev.yml up --build -d + +# View logs +docker-compose -f docker-compose.dev.yml logs -f + +# Start production +docker-compose -f docker-compose.prod.yml up -d +``` + +## 🐳 Docker Images + +Automatic builds are available on DockerHub: + +```bash +# Latest development build +docker pull sensenetcsp/sn-client:feature-docker-containerization + +# Specific commit +docker pull sensenetcsp/sn-client:feature-docker-containerization-abc1234 + +# Production (when merged to main) +docker pull sensenetcsp/sn-client:latest +``` + +## ⚙️ Configuration + +### Environment Variables +Both compose files support these environment variables: + +- `NODE_ENV`: `development` or `production` +- `AUTH_TYPE`: `SNAuth` or `IdentityServer` +- `CHOKIDAR_USEPOLLING`: `true` (dev only, for file watching) +- `WATCHPACK_POLLING`: `true` (dev only, for webpack) + +### Port Configuration +- **Default**: Port 8080 for both dev and prod +- **Customizable**: Change the host port in docker-compose files + +## 🔍 Troubleshooting + +### Container won't start +```bash +# Check logs +docker-compose -f docker-compose.dev.yml logs + +# Rebuild from scratch +docker-compose -f docker-compose.dev.yml down +docker-compose -f docker-compose.dev.yml up --build +``` + +### Hot reload not working +- Ensure you're using the dev compose file +- Restart the container if file watching stops working + +### Port already in use +```bash +# Change the port in docker-compose file +ports: + - "3000:8080" # Use port 3000 instead of 8080 +``` + +## 📦 Build Process + +The Docker build process: +1. **Copy source code** (excluding files in `.dockerignore`) +2. **Install dependencies** with `yarn install` +3. **Build packages** with `yarn build` +4. **Start application** with `yarn snapp start` + +## 🚀 CI/CD + +GitHub Actions automatically builds and pushes Docker images when: +- Code is pushed to `feature/docker-containerization` +- Pull requests target `develop`, `main`, or `feature/sn-auth-package-extraimprovements` + +Images are tagged based on branch names and commit SHAs for easy identification. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..d2c04299d4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Dockerfile for SenseNet client +FROM node:20-alpine + +# Install serve for static file serving +RUN yarn global add serve + +# Set working directory +WORKDIR /app + +# Copy everything (dockerignore excludes unwanted files) +COPY . . + +# Install dependencies +RUN yarn install + +# Build packages (required for the app to work) +RUN yarn build + +# Build the app bundle in production mode (avoids runtime webpack rebuild) +RUN NODE_ENV=production yarn snapp build + +# Expose port +EXPOSE 8080 + +# Health check (start-period is short since static server starts instantly) +HEALTHCHECK --interval=30s --timeout=3s --start-period=15s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 + +# Serve pre-built static files (fast startup, SPA mode with -s flag) +CMD ["serve", "-s", "apps/sensenet/build", "-l", "8080"] diff --git a/apps/sensenet/README.md b/apps/sensenet/README.md index ea765a613f..34ec0099ee 100644 --- a/apps/sensenet/README.md +++ b/apps/sensenet/README.md @@ -59,3 +59,67 @@ If you start typing a Content query term (that starts with a '+' sign), the term ## ℹ Version info (Coming soon...) + +# sensenet Admin UI + +React-based UI for sensenet. This application provides a rich UI for managing your sensenet content repository. It was designed to take advantage of the modern web technologies - which means we built it for evergreen browsers (Edge, Chrome, Firefox). If you need legacy browser support (e.g. IE11) please use the [old admin UI](https://github.com/SenseNet/sensenet/tree/master/src/nuget/snadmin/install-webpages) instead. + +## Authentication Configuration + +The application supports two authentication methods: + +- **SNAuth**: sensenet's JWT-based authentication +- **IdentityServer**: OIDC-based authentication with Identity Server + +You can specify which authentication method to use during the build process. This is a build-time configuration, meaning the application will be built to use only one authentication method. + +### Building with specific authentication method + +To build the application with SNAuth (default): + +```bash +yarn build:snauth +# or npm run build:snauth +``` + +To build the application with Identity Server authentication: + +```bash +yarn build:idserver +# or npm run build:idserver +``` + +### Development with specific authentication method + +To run the development server with SNAuth: + +```bash +yarn start:snauth +# or npm run start:snauth +``` + +To run the development server with Identity Server authentication: + +```bash +yarn start:idserver +# or npm run start:idserver +``` + +If you don't specify an authentication method, the application will default to using SNAuth. + +## Development + +To run the application locally: + +```bash +yarn install +yarn start +``` + +Navigate to http://localhost:8080 in your browser. + +To build the application: + +```bash +yarn build +``` diff --git a/apps/sensenet/index.html b/apps/sensenet/index.html index 4f32ba2e91..bc7f6ac4c8 100644 --- a/apps/sensenet/index.html +++ b/apps/sensenet/index.html @@ -1,42 +1,45 @@ -
- - - -