diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f216285 --- /dev/null +++ b/Makefile @@ -0,0 +1,550 @@ +# ExcaliDash Makefile +# Comprehensive development, testing, and release automation + +.PHONY: help install dev build test test-frontend test-backend test-e2e test-e2e-docker \ + lint lint-frontend lint-backend clean docker-build docker-run docker-down docker-logs \ + release pre-release version-bump changelog db-migrate db-reset + +# Colors +GREEN := \033[0;32m +YELLOW := \033[1;33m +BLUE := \033[0;34m +RED := \033[0;31m +NC := \033[0m + +# Configuration +DOCKER_USERNAME := zimengxiong +IMAGE_NAME := excalidash +VERSION := $(shell cat VERSION 2>/dev/null || echo "0.0.0") + +# Default target +.DEFAULT_GOAL := help + +#=============================================================================== +# HELP +#=============================================================================== + +help: ## Show this help message + @echo "" + @echo "$(GREEN)ExcaliDash Makefile$(NC)" + @echo "$(GREEN)==================$(NC)" + @echo "" + @echo "$(YELLOW)Usage:$(NC) make [target]" + @echo "" + @echo "$(BLUE)Development:$(NC)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(install|dev|build|lint|clean)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "" + @echo "$(BLUE)Testing:$(NC)" + @grep -E '^test[-a-zA-Z0-9_]*:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "" + @echo "$(BLUE)Docker:$(NC)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(docker)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "" + @echo "$(BLUE)Release:$(NC)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(release|version|changelog)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "" + @echo "$(BLUE)Database:$(NC)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(db-)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "" + @echo "$(YELLOW)Current version:$(NC) $(VERSION)" + @echo "" + +#=============================================================================== +# DEVELOPMENT +#=============================================================================== + +install: ## Install all dependencies (frontend, backend, e2e) + @echo "$(YELLOW)Installing frontend dependencies...$(NC)" + cd frontend && npm install + @echo "$(YELLOW)Installing backend dependencies...$(NC)" + cd backend && npm install + @echo "$(YELLOW)Installing e2e dependencies...$(NC)" + cd e2e && npm install + @echo "$(GREEN)All dependencies installed!$(NC)" + +dev: ## Start development servers (frontend + backend) + @echo "$(YELLOW)Starting development servers...$(NC)" + @echo "$(BLUE)Backend will run on port 8000, Frontend on port 5173$(NC)" + @trap 'kill 0' INT; \ + (cd backend && npm run dev) & \ + (cd frontend && npm run dev) & \ + wait + +dev-frontend: ## Start frontend dev server only + cd frontend && npm run dev + +dev-backend: ## Start backend dev server only + cd backend && npm run dev + +build: ## Build frontend and backend for production + @echo "$(YELLOW)Building frontend...$(NC)" + cd frontend && npm run build + @echo "$(GREEN)Build complete!$(NC)" + +lint: lint-frontend lint-backend ## Run linters for frontend and backend + +lint-frontend: ## Run frontend linter + @echo "$(YELLOW)Linting frontend...$(NC)" + cd frontend && npm run lint + +lint-backend: ## Run backend linter (if available) + @echo "$(YELLOW)Backend linting not configured$(NC)" + +clean: ## Clean build artifacts and node_modules + @echo "$(YELLOW)Cleaning build artifacts...$(NC)" + rm -rf frontend/dist + rm -rf frontend/node_modules/.vite + @echo "$(GREEN)Clean complete!$(NC)" + +clean-all: clean ## Clean everything including node_modules + @echo "$(RED)Removing all node_modules...$(NC)" + rm -rf frontend/node_modules + rm -rf backend/node_modules + rm -rf e2e/node_modules + @echo "$(GREEN)Full clean complete!$(NC)" + +#=============================================================================== +# TESTING +#=============================================================================== + +test: test-frontend test-backend ## Run all tests (frontend + backend unit tests) + @echo "$(GREEN)All unit tests passed!$(NC)" + +test-all: test test-e2e ## Run ALL tests (unit + e2e) + @echo "$(GREEN)All tests passed!$(NC)" + +test-frontend: ## Run frontend unit tests + @echo "$(YELLOW)Running frontend tests...$(NC)" + cd frontend && npm test + +test-backend: ## Run backend unit tests + @echo "$(YELLOW)Running backend tests...$(NC)" + cd backend && npm test + +test-coverage: ## Run all unit tests with coverage + @echo "$(YELLOW)Running tests with coverage...$(NC)" + cd frontend && npm run test:coverage + cd backend && npm run test:coverage + +test-e2e: ## Run e2e tests (starts servers automatically) + @echo "$(YELLOW)Running e2e tests...$(NC)" + cd e2e && ./run-e2e.sh + +test-e2e-headed: ## Run e2e tests with visible browser + @echo "$(YELLOW)Running e2e tests (headed)...$(NC)" + cd e2e && ./run-e2e.sh --headed + +test-e2e-docker: ## Run e2e tests in Docker containers + @echo "$(YELLOW)Running e2e tests in Docker...$(NC)" + cd e2e && ./run-e2e.sh --docker + +test-watch: ## Run tests in watch mode + @trap 'kill 0' INT; \ + (cd frontend && npm run test:watch) & \ + (cd backend && npm run test:watch) & \ + wait + +#=============================================================================== +# DOCKER +#=============================================================================== + +docker-build: ## Build Docker images locally + @echo "$(YELLOW)Building Docker images...$(NC)" + docker-compose build + @echo "$(GREEN)Docker images built!$(NC)" + +docker-run: ## Start Docker containers (docker-compose up) + @echo "$(YELLOW)Starting Docker containers...$(NC)" + docker-compose up + +docker-up: docker-run ## Alias for docker-run + +docker-run-detached: ## Start Docker containers in background + @echo "$(YELLOW)Starting Docker containers (detached)...$(NC)" + docker-compose up -d + @echo "$(GREEN)Containers started! Access at http://localhost:6767$(NC)" + +docker-down: ## Stop and remove Docker containers + @echo "$(YELLOW)Stopping Docker containers...$(NC)" + docker-compose down + @echo "$(GREEN)Containers stopped!$(NC)" + +docker-down-volumes: ## Stop containers and remove volumes + @echo "$(RED)Stopping containers and removing volumes...$(NC)" + docker-compose down -v + +docker-logs: ## Show Docker container logs + docker-compose logs -f + +docker-ps: ## Show running Docker containers + docker-compose ps + +docker-restart: docker-down docker-run ## Restart Docker containers + +docker-rebuild: docker-down docker-build docker-run ## Rebuild and restart containers + +#=============================================================================== +# VERSION MANAGEMENT +#=============================================================================== + +version: ## Show current version + @echo "$(YELLOW)Current version:$(NC) $(VERSION)" + +version-bump: ## Interactive version bump + @echo "$(YELLOW)Current version:$(NC) $(VERSION)" + @echo "" + @echo "$(BLUE)Select version bump type:$(NC)" + @echo " 1) patch ($(VERSION) -> $$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}'))" + @echo " 2) minor ($(VERSION) -> $$(echo $(VERSION) | awk -F. '{print $$1"."$$2+1".0"}'))" + @echo " 3) major ($(VERSION) -> $$(echo $(VERSION) | awk -F. '{print $$1+1".0.0"}'))" + @echo " 4) custom" + @echo "" + @read -p "Enter choice [1-4]: " choice; \ + case $$choice in \ + 1) NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}') ;; \ + 2) NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2+1".0"}') ;; \ + 3) NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1+1".0.0"}') ;; \ + 4) read -p "Enter new version: " NEW_VERSION ;; \ + *) echo "$(RED)Invalid choice$(NC)"; exit 1 ;; \ + esac; \ + echo "$(YELLOW)Bumping version to $$NEW_VERSION...$(NC)"; \ + echo "$$NEW_VERSION" > VERSION; \ + sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json 2>/dev/null || \ + sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json; \ + sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json 2>/dev/null || \ + sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json; \ + echo "$(GREEN)Version bumped to $$NEW_VERSION$(NC)" + +#=============================================================================== +# RELEASE +#=============================================================================== + +changelog: ## Edit release notes (RELEASE.md) + @echo "$(YELLOW)Opening RELEASE.md for editing...$(NC)" + @if [ -z "$$EDITOR" ]; then \ + echo "$(RED)No EDITOR set. Using vim.$(NC)"; \ + vim RELEASE.md; \ + else \ + $$EDITOR RELEASE.md; \ + fi + +release: ## Full release workflow (main branch only) + @echo "$(GREEN)===========================================$(NC)" + @echo "$(GREEN) ExcaliDash Release Workflow$(NC)" + @echo "$(GREEN)===========================================$(NC)" + @echo "" + @# Branch check + @CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ + if [ "$$CURRENT_BRANCH" != "main" ]; then \ + echo "$(RED)ERROR: Releases must be made from 'main' branch!$(NC)"; \ + echo "$(RED)Current branch: $$CURRENT_BRANCH$(NC)"; \ + echo "$(YELLOW)Please switch to main and try again.$(NC)"; \ + exit 1; \ + fi + @echo "$(GREEN)✓ On main branch$(NC)" + @echo "" + @# Pull latest + @echo "$(YELLOW)Pulling latest changes...$(NC)" + @git pull origin main + @echo "$(GREEN)✓ Up to date with remote$(NC)" + @echo "" + @# Show current status + @echo "$(YELLOW)Current status:$(NC)" + @git status --short || true + @echo "" + @# Run tests + @echo "$(YELLOW)Running tests...$(NC)" + @$(MAKE) test + @echo "$(GREEN)✓ All tests passed$(NC)" + @echo "" + @# Version bump - inline with clear options + @CURRENT=$$(cat VERSION); \ + PATCH=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2"."$$3+1}'); \ + MINOR=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2+1".0"}'); \ + MAJOR=$$(echo $$CURRENT | awk -F. '{print $$1+1".0.0"}'); \ + echo "$(YELLOW)Current version: $$CURRENT$(NC)"; \ + echo ""; \ + echo "$(BLUE)Select version bump:$(NC)"; \ + echo " 1) patch → $$PATCH"; \ + echo " 2) minor → $$MINOR"; \ + echo " 3) major → $$MAJOR"; \ + echo " 4) custom"; \ + echo " 5) skip (keep $$CURRENT)"; \ + echo ""; \ + read -p "Enter choice [1-5]: " choice; \ + case $$choice in \ + 1) NEW_VERSION=$$PATCH ;; \ + 2) NEW_VERSION=$$MINOR ;; \ + 3) NEW_VERSION=$$MAJOR ;; \ + 4) read -p "Enter new version: " NEW_VERSION ;; \ + 5) NEW_VERSION=$$CURRENT ;; \ + *) echo "$(RED)Invalid choice, using current$(NC)"; NEW_VERSION=$$CURRENT ;; \ + esac; \ + if [ "$$NEW_VERSION" != "$$CURRENT" ]; then \ + echo "$(YELLOW)Bumping version to $$NEW_VERSION...$(NC)"; \ + echo "$$NEW_VERSION" > VERSION; \ + sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json 2>/dev/null || \ + sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json; \ + sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json 2>/dev/null || \ + sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json; \ + echo "$(GREEN)✓ Version bumped to $$NEW_VERSION$(NC)"; \ + else \ + echo "$(YELLOW)Keeping version $$CURRENT$(NC)"; \ + fi + @echo "" + @# Release notes + @echo "$(YELLOW)Release notes (RELEASE.md):$(NC)" + @read -p "Edit RELEASE.md now? [Y/n]: " edit; \ + if [ "$$edit" != "n" ] && [ "$$edit" != "N" ]; then \ + $(MAKE) changelog; \ + fi + @echo "" + @# Show summary before commit + @NEW_VERSION=$$(cat VERSION); \ + echo "$(BLUE)===========================================$(NC)"; \ + echo "$(BLUE)Release Summary$(NC)"; \ + echo "$(BLUE)===========================================$(NC)"; \ + echo " Version: v$$NEW_VERSION"; \ + echo " Branch: main"; \ + echo " Tag: v$$NEW_VERSION"; \ + echo ""; \ + echo "$(YELLOW)Changes to be committed:$(NC)"; \ + git status --short; \ + echo "" + @read -p "$(YELLOW)Proceed with release? [y/N]: $(NC)" confirm; \ + if [ "$$confirm" != "y" ] && [ "$$confirm" != "Y" ]; then \ + echo "$(RED)Release aborted.$(NC)"; \ + exit 1; \ + fi + @echo "" + @# Commit changes + @NEW_VERSION=$$(cat VERSION); \ + echo "$(YELLOW)Committing release...$(NC)"; \ + git add -A; \ + git commit -m "chore: release v$$NEW_VERSION" || echo "$(YELLOW)Nothing to commit$(NC)" + @echo "$(GREEN)✓ Changes committed$(NC)" + @echo "" + @# Push to remote + @echo "$(YELLOW)Pushing to remote...$(NC)" + @git push origin main + @echo "$(GREEN)✓ Pushed to origin/main$(NC)" + @echo "" + @# Create git tag + @NEW_VERSION=$$(cat VERSION); \ + echo "$(YELLOW)Creating tag v$$NEW_VERSION...$(NC)"; \ + git tag -a "v$$NEW_VERSION" -m "Release v$$NEW_VERSION"; \ + git push origin "v$$NEW_VERSION" + @echo "$(GREEN)✓ Tag v$$NEW_VERSION created and pushed$(NC)" + @echo "" + @# Create GitHub release + @NEW_VERSION=$$(cat VERSION); \ + echo "$(YELLOW)Creating GitHub release...$(NC)"; \ + if command -v gh &> /dev/null; then \ + gh release create "v$$NEW_VERSION" \ + --title "ExcaliDash v$$NEW_VERSION" \ + --notes-file RELEASE.md; \ + echo "$(GREEN)✓ GitHub release created$(NC)"; \ + else \ + echo "$(RED)gh CLI not installed!$(NC)"; \ + echo "$(YELLOW)Install with: brew install gh$(NC)"; \ + echo "$(YELLOW)Then run: gh auth login$(NC)"; \ + exit 1; \ + fi + @echo "" + @# Build and push Docker images + @echo "$(YELLOW)Building and pushing Docker images...$(NC)" + @./publish-docker.sh + @echo "" + @echo "$(GREEN)===========================================$(NC)" + @echo "$(GREEN) Release Complete!$(NC)" + @echo "$(GREEN)===========================================$(NC)" + @NEW_VERSION=$$(cat VERSION); \ + echo ""; \ + echo "$(GREEN)✓ Version: v$$NEW_VERSION$(NC)"; \ + echo "$(GREEN)✓ Git tag pushed$(NC)"; \ + echo "$(GREEN)✓ GitHub release created$(NC)"; \ + echo "$(GREEN)✓ Docker images published$(NC)" + +pre-release: ## Pre-release workflow (pre-release branch only) + @echo "$(BLUE)===========================================$(NC)" + @echo "$(BLUE) ExcaliDash Pre-Release Workflow$(NC)" + @echo "$(BLUE)===========================================$(NC)" + @echo "" + @# Branch check + @CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ + if [ "$$CURRENT_BRANCH" != "pre-release" ]; then \ + echo "$(RED)ERROR: Pre-releases must be made from 'pre-release' branch!$(NC)"; \ + echo "$(RED)Current branch: $$CURRENT_BRANCH$(NC)"; \ + echo "$(YELLOW)Please switch to pre-release and try again.$(NC)"; \ + exit 1; \ + fi + @echo "$(GREEN)✓ On pre-release branch$(NC)" + @echo "" + @# Pull latest + @echo "$(YELLOW)Pulling latest changes...$(NC)" + @git pull origin pre-release + @echo "$(GREEN)✓ Up to date with remote$(NC)" + @echo "" + @# Show current status + @echo "$(YELLOW)Current status:$(NC)" + @git status --short || true + @echo "" + @# Run tests + @echo "$(YELLOW)Running tests...$(NC)" + @$(MAKE) test + @echo "$(GREEN)✓ All tests passed$(NC)" + @echo "" + @# Version bump - inline with clear options + @CURRENT=$$(cat VERSION); \ + PATCH=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2"."$$3+1}'); \ + MINOR=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2+1".0"}'); \ + MAJOR=$$(echo $$CURRENT | awk -F. '{print $$1+1".0.0"}'); \ + echo "$(YELLOW)Current version: $$CURRENT$(NC)"; \ + echo ""; \ + echo "$(BLUE)Select version bump:$(NC)"; \ + echo " 1) patch → $$PATCH-dev"; \ + echo " 2) minor → $$MINOR-dev"; \ + echo " 3) major → $$MAJOR-dev"; \ + echo " 4) custom"; \ + echo " 5) skip (keep $$CURRENT-dev)"; \ + echo ""; \ + read -p "Enter choice [1-5]: " choice; \ + case $$choice in \ + 1) NEW_VERSION=$$PATCH ;; \ + 2) NEW_VERSION=$$MINOR ;; \ + 3) NEW_VERSION=$$MAJOR ;; \ + 4) read -p "Enter new version (without -dev suffix): " NEW_VERSION ;; \ + 5) NEW_VERSION=$$CURRENT ;; \ + *) echo "$(RED)Invalid choice, using current$(NC)"; NEW_VERSION=$$CURRENT ;; \ + esac; \ + if [ "$$NEW_VERSION" != "$$CURRENT" ]; then \ + echo "$(YELLOW)Bumping version to $$NEW_VERSION...$(NC)"; \ + echo "$$NEW_VERSION" > VERSION; \ + sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json 2>/dev/null || \ + sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json; \ + sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json 2>/dev/null || \ + sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json; \ + echo "$(GREEN)✓ Version bumped to $$NEW_VERSION$(NC)"; \ + else \ + echo "$(YELLOW)Keeping version $$CURRENT$(NC)"; \ + fi + @echo "" + @# Release notes + @echo "$(YELLOW)Release notes (RELEASE.md):$(NC)" + @read -p "Edit RELEASE.md now? [Y/n]: " edit; \ + if [ "$$edit" != "n" ] && [ "$$edit" != "N" ]; then \ + $(MAKE) changelog; \ + fi + @echo "" + @# Show summary before commit + @NEW_VERSION=$$(cat VERSION); \ + echo "$(BLUE)===========================================$(NC)"; \ + echo "$(BLUE)Pre-Release Summary$(NC)"; \ + echo "$(BLUE)===========================================$(NC)"; \ + echo " Version: v$$NEW_VERSION-dev"; \ + echo " Branch: pre-release"; \ + echo " Tag: v$$NEW_VERSION-dev (pre-release)"; \ + echo ""; \ + echo "$(YELLOW)Changes to be committed:$(NC)"; \ + git status --short; \ + echo "" + @read -p "$(YELLOW)Proceed with pre-release? [y/N]: $(NC)" confirm; \ + if [ "$$confirm" != "y" ] && [ "$$confirm" != "Y" ]; then \ + echo "$(RED)Pre-release aborted.$(NC)"; \ + exit 1; \ + fi + @echo "" + @# Commit changes + @NEW_VERSION=$$(cat VERSION); \ + echo "$(YELLOW)Committing pre-release...$(NC)"; \ + git add -A; \ + git commit -m "chore: pre-release v$$NEW_VERSION-dev" || echo "$(YELLOW)Nothing to commit$(NC)" + @echo "$(GREEN)✓ Changes committed$(NC)" + @echo "" + @# Push to remote + @echo "$(YELLOW)Pushing to remote...$(NC)" + @git push origin pre-release + @echo "$(GREEN)✓ Pushed to origin/pre-release$(NC)" + @echo "" + @# Create git tag + @NEW_VERSION=$$(cat VERSION); \ + PRE_TAG="v$$NEW_VERSION-dev"; \ + echo "$(YELLOW)Creating tag $$PRE_TAG...$(NC)"; \ + git tag -a "$$PRE_TAG" -m "Pre-release $$PRE_TAG"; \ + git push origin "$$PRE_TAG" + @echo "$(GREEN)✓ Tag $$PRE_TAG created and pushed$(NC)" + @echo "" + @# Create GitHub pre-release + @NEW_VERSION=$$(cat VERSION); \ + PRE_TAG="v$$NEW_VERSION-dev"; \ + echo "$(YELLOW)Creating GitHub pre-release...$(NC)"; \ + if command -v gh &> /dev/null; then \ + gh release create "$$PRE_TAG" \ + --title "ExcaliDash $$PRE_TAG (Pre-release)" \ + --notes-file RELEASE.md \ + --prerelease; \ + echo "$(GREEN)✓ GitHub pre-release created$(NC)"; \ + else \ + echo "$(RED)gh CLI not installed!$(NC)"; \ + echo "$(YELLOW)Install with: brew install gh$(NC)"; \ + echo "$(YELLOW)Then run: gh auth login$(NC)"; \ + exit 1; \ + fi + @echo "" + @# Build and push Docker images + @echo "$(YELLOW)Building and pushing Docker images...$(NC)" + @./publish-docker-prerelease.sh + @echo "" + @echo "$(BLUE)===========================================$(NC)" + @echo "$(GREEN) Pre-Release Complete!$(NC)" + @echo "$(BLUE)===========================================$(NC)" + @NEW_VERSION=$$(cat VERSION); \ + echo ""; \ + echo "$(GREEN)✓ Version: v$$NEW_VERSION-dev$(NC)"; \ + echo "$(GREEN)✓ Git tag pushed$(NC)"; \ + echo "$(GREEN)✓ GitHub pre-release created$(NC)"; \ + echo "$(GREEN)✓ Docker images published$(NC)" + +release-docker: ## Build and push release Docker images + ./publish-docker.sh + +pre-release-docker: ## Build and push pre-release Docker images + ./publish-docker-prerelease.sh + +#=============================================================================== +# DATABASE +#=============================================================================== + +db-migrate: ## Run database migrations + @echo "$(YELLOW)Running database migrations...$(NC)" + cd backend && npx prisma migrate dev + @echo "$(GREEN)Migrations complete!$(NC)" + +db-generate: ## Generate Prisma client + @echo "$(YELLOW)Generating Prisma client...$(NC)" + cd backend && npx prisma generate + @echo "$(GREEN)Client generated!$(NC)" + +db-reset: ## Reset database (WARNING: destroys all data) + @echo "$(RED)WARNING: This will destroy all data!$(NC)" + @read -p "Are you sure? [y/N]: " confirm; \ + if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ + cd backend && npx prisma migrate reset --force; \ + echo "$(GREEN)Database reset complete!$(NC)"; \ + else \ + echo "$(YELLOW)Cancelled$(NC)"; \ + fi + +db-studio: ## Open Prisma Studio (database GUI) + @echo "$(YELLOW)Opening Prisma Studio...$(NC)" + cd backend && npx prisma studio + +#=============================================================================== +# QUICK ALIASES +#=============================================================================== + +up: docker-run ## Alias: Start Docker containers +down: docker-down ## Alias: Stop Docker containers +logs: docker-logs ## Alias: Show Docker logs +t: test ## Alias: Run unit tests +ta: test-all ## Alias: Run all tests diff --git a/RELEASE.md b/RELEASE.md index 04bf419..707df09 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,30 +1,29 @@ -# ExcaliDash v0.1.5 +CSRF Protection (8a78b2b) -Date: 2025-11-23 + - Implemented comprehensive CSRF (Cross-Site Request Forgery) protection for enhanced security + - Added new backend/src/security.ts module for security utilities + - Frontend API layer now handles CSRF tokens automatically + - Added integration tests for CSRF validation -Compatibility: v0.1.x (Backward Compatible) + Upload Progress Indicator (8f9b9b4) -# Security + - Added a visual upload progress bar when users upload files + - New UploadContext for managing upload state across components + - New UploadStatus component displaying real-time upload progress + - Save status indicator when navigating back from the editor + - Improved error handling and recovery for failed uploads -- RCE: implemented strict Zod schema validation and input sanitization on file uploads; added path traversal guards to file handling logic + Bug Fixes -- XSS: used DOMPurify for HTML sanitization; blocked execution-capable SVG attributes and enforces CSP headers. + - Fixed broken e2e tests (cae8f3c) + - Replaced deprecated substr() with substring() + - Fixed stale state issues in error handling + - Fixed missing useEffect dependencies + - Fixed CSS class conflicts in progress bar styling + - Added error recovery for save state in Editor -- DoS: moved CPU-intensive operations to worker threads to prevent event loop blocking; request rate limiting (1,000 req/15 min per IP) and streaming for large files + Infrastructure -# Infras & Deployment - -- non-root execution (uid 1001) in containers -- migrated to multi-stage Docker builds - -# Database - -- migrated to better-sqlite3, converted all DB interactions to non-blocking async operations and offloaded integrity checks to worker threads. - -- implemented SQLite magic header validation; added automatic backup triggers preceding data import - -- input validation logic - -# Frontend - -- updated Settings UI to show version + - Updated docker-compose configurations with new environment variables + - E2E test suite improvements and reliability fixes + - Added Kubernetes deployment note in README diff --git a/VERSION b/VERSION index 84aa3a7..0c62199 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.8 \ No newline at end of file +0.2.1 diff --git a/backend/package.json b/backend/package.json index 8077adf..63b14ce 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "backend", - "version": "0.1.8", + "version": "0.2.1", "description": "", "main": "index.js", "scripts": { @@ -42,4 +42,4 @@ "typescript": "^5.9.3", "vitest": "^4.0.15" } -} \ No newline at end of file +} diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 6394181..2121f33 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -6,8 +6,8 @@ services: - DATABASE_URL=file:/app/prisma/dev.db - PORT=8000 - NODE_ENV=production - # Required for horizontal scaling (k8s): must be the same across all instances - - CSRF_SECRET=${CSRF_SECRET} + # Required for horizontal scaling (k8s): uncomment and set to same value on all instances + # - CSRF_SECRET=${CSRF_SECRET} volumes: - backend-data:/app/prisma networks: diff --git a/docker-compose.yml b/docker-compose.yml index 7a42374..d2d928a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,8 +8,8 @@ services: - DATABASE_URL=file:/app/prisma/dev.db - PORT=8000 - NODE_ENV=production - # Required for horizontal scaling (k8s): must be the same across all instances - - CSRF_SECRET=${CSRF_SECRET} + # Required for horizontal scaling (k8s): uncomment and set to same value on all instances + # - CSRF_SECRET=${CSRF_SECRET} volumes: - backend-data:/app/prisma networks: diff --git a/frontend/package.json b/frontend/package.json index 120e61d..5220db4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "0.1.8", + "version": "0.2.1", "type": "module", "scripts": { "dev": "vite",