{"id":31675036,"url":"https://github.com/meleshyn/comments-spa","last_synced_at":"2026-04-07T21:31:43.497Z","repository":{"id":316493888,"uuid":"1057919003","full_name":"meleshyn/comments-spa","owner":"meleshyn","description":"Join engaging conversations with powerful commenting system. Post comments, create infinite nested replies, attach images and files, and enjoy rich text editing","archived":false,"fork":false,"pushed_at":"2025-10-03T14:37:48.000Z","size":273,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-11T23:56:02.337Z","etag":null,"topics":["comments","docker","docker-compose","drizzle-orm","monorepo","nestjs","react","turborepo","typescript","vite"],"latest_commit_sha":null,"homepage":"https://comments.meleshyn.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/meleshyn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-16T11:36:01.000Z","updated_at":"2025-10-10T06:21:52.000Z","dependencies_parsed_at":"2025-09-25T00:19:16.142Z","dependency_job_id":"5f3e2264-9e83-4a1d-b217-8b0f1e981462","html_url":"https://github.com/meleshyn/comments-spa","commit_stats":null,"previous_names":["meleshyn/comments-spa"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/meleshyn/comments-spa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meleshyn%2Fcomments-spa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meleshyn%2Fcomments-spa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meleshyn%2Fcomments-spa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meleshyn%2Fcomments-spa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/meleshyn","download_url":"https://codeload.github.com/meleshyn/comments-spa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meleshyn%2Fcomments-spa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31530641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"ssl_error","status_checked_at":"2026-04-07T16:28:06.951Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["comments","docker","docker-compose","drizzle-orm","monorepo","nestjs","react","turborepo","typescript","vite"],"created_at":"2025-10-08T04:59:19.794Z","updated_at":"2026-04-07T21:31:43.469Z","avatar_url":"https://github.com/meleshyn.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Comments SPA\n\nA feature-rich, high-performance Single-Page Application for nested commenting with file attachments, built using modern web technologies and best practices.\n\n![Turborepo](https://img.shields.io/badge/Turborepo-2C3E50?style=for-the-badge\u0026logo=turborepo\u0026logoColor=white)\n![NestJS](https://img.shields.io/badge/NestJS-CC3366?style=for-the-badge\u0026logo=nestjs\u0026logoColor=white)\n![React](https://img.shields.io/badge/React-007ACC?style=for-the-badge\u0026logo=react\u0026logoColor=white)\n![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge\u0026logo=typescript\u0026logoColor=white)\n![Docker](https://img.shields.io/badge/Docker-2C3E50?style=for-the-badge\u0026logo=docker\u0026logoColor=white)\n\n## ✨ Features\n\n### 💬 Advanced Commenting System\n\n- **Infinite Nesting**: Create threaded discussions with unlimited depth\n- **Rich Text Editor**: TipTap-powered editor with formatting support (`\u003ca\u003e`, `\u003ccode\u003e`, `\u003ci\u003e`, `\u003cstrong\u003e`)\n- **Smart Sorting**: Sort comments by username, email, or creation date (ascending/descending)\n- **Pagination**: Efficient cursor-based pagination with 25 comments per page\n- **Lazy Loading**: On-demand loading of nested replies for optimal performance\n\n### 📎 File Attachments\n\n- **Image Support**: Upload JPG, GIF, PNG images (automatically resized to 320x240px)\n- **Text Files**: Upload .txt files up to 100KB\n- **Instant Preview**: Real-time preview of selected files before submission\n- **Cloud Storage**: Files stored securely in Google Cloud Storage\n- **Lightbox Viewer**: Full-screen viewing experience for images and text files\n\n### 🔒 Security \u0026 Performance\n\n- **CAPTCHA Protection**: Google reCAPTCHA integration to prevent spam\n- **XSS Prevention**: Content sanitization with DOMPurify\n- **SQL Injection Protection**: Parameterized queries with Drizzle ORM\n- **Input Validation**: Comprehensive Zod schema validation\n- **Optimistic Updates**: Instant UI feedback with TanStack Query\n- **Skeleton Loading**: Progressive loading states for smooth UX\n\n### 🎨 Modern UI/UX\n\n- **Material Design 3**: Consistent design system with dark theme optimization\n- **Responsive Design**: Mobile-first approach with touch-friendly interactions\n- **Accessibility**: ARIA labels, keyboard navigation, and screen reader support\n- **Visual Hierarchy**: Clear indentation and borders for nested conversations\n\n## 🏗️ Architecture\n\nThis project is a **Turborepo** monorepo containing:\n\n```\ncomments-spa/\n├── apps/\n│   ├── api/          # NestJS backend with Drizzle ORM\n│   └── web/          # React frontend with Vite\n├── packages/\n│   ├── schemas/      # Shared Zod validation schemas\n│   └── tsconfig/     # Shared TypeScript configurations\n└── docker-compose.yaml # Production deployment\n```\n\n### Backend (NestJS)\n\n- **Framework**: NestJS with modular architecture\n- **Database**: PostgreSQL with Drizzle ORM\n- **File Storage**: Google Cloud Storage\n- **Validation**: Zod schemas with shared validation logic\n- **Security**: Helmet, CORS, and rate limiting\n\n### Frontend (React)\n\n- **Build Tool**: Vite for fast development and optimized builds\n- **UI Components**: Shadcn/ui with Tailwind CSS\n- **State Management**: TanStack Query for server state\n- **Rich Text**: TipTap editor with custom extensions\n- **Forms**: React Hook Form with Zod validation\n- **File Handling**: Multipart form uploads with preview\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n- **Node.js** 18+ and **pnpm**\n- **Docker** and **Docker Compose**\n- **Google Cloud Storage** account (for file uploads)\n\n### Local Development\n\n1. **Install Dependencies**\n\n   ```bash\n   pnpm install\n   ```\n\n2. **Start Database**\n\n   ```bash\n   docker run --name comments_db \\\n     -e POSTGRES_USER=postgres \\\n     -e POSTGRES_PASSWORD=postgres \\\n     -e POSTGRES_DB=comments_spa \\\n     -p 5432:5432 -d postgres:17-alpine\n   ```\n\n3. **Configure Environment**\n\n   ```bash\n   # Copy environment files\n   cp apps/api/env.example apps/api/.env\n   cp apps/web/env.example apps/web/.env\n   ```\n\n4. **Configure Google Cloud Storage** (Optional for file uploads)\n\n   ```bash\n   # For file upload functionality, set up GCS credentials:\n   # 1. Create a service account in Google Cloud Console\n   # 2. Download the JSON key file\n   # 3. Copy the credentials template\n   cp apps/api/gcs-credentials.example.json apps/api/gcs-credentials.json\n   # 4. Replace the placeholder values in gcs-credentials.json with your actual credentials\n   # 5. For detailed setup instructions, see:\n   #    https://cloud.google.com/docs/authentication/getting-started\n   ```\n\n   \u003e **Note**: File uploads are optional. The commenting system works without GCS configuration, but image and text file attachments require proper GCS setup.\n\n5. **Run Database Migrations**\n\n   ```bash\n   pnpm --filter api db:migrate\n   ```\n\n6. **Start Development Servers**\n   ```bash\n   pnpm dev\n   ```\n\n## 📡 API Endpoints\n\n### Comments\n\n- `GET /comments` - Fetch paginated root comments with sorting\n- `GET /comments/:id/replies` - Fetch replies to a specific comment\n- `POST /comments` - Create a new comment with optional file attachments\n\n### Query Parameters\n\n- `limit` (1-100, default: 25)\n- `cursor` (UUID for pagination)\n- `sortBy` (`userName`, `email`, `createdAt`)\n- `sortOrder` (`asc`, `desc`)\n\n## 🐳 Production Deployment\n\n1. **Configure Environment**\n\n   ```bash\n   # Copy and configure production environment\n   cp env.example .env\n   ```\n\n   \u003e **Note**: The `gcs-credentials.json` file is automatically created from the `GCS_KEY_FILE_CONTENT` environment variable during the container startup process via the `entrypoint.sh` script. This approach keeps sensitive credentials secure while maintaining deployment flexibility.\n\n2. **Deploy with Docker Compose**\n   ```bash\n   docker-compose up --build -d\n   ```\n\nThe application will be available at:\n\n- **Frontend**: http://localhost\n- **Backend API**: http://localhost:3000\n\n## 📊 Performance Features\n\n### Database Optimization\n\n- **Cursor-based Pagination**: Consistent performance regardless of dataset size\n- **N+1 Query Prevention**: Efficient SQL queries with subquery optimization\n- **Indexing Strategy**: Optimized indexes for sorting and filtering operations\n\n### Frontend Performance\n\n- **Code Splitting**: Lazy-loaded components and routes\n- **Caching Strategy**: Intelligent TanStack Query caching with stale-time management\n- **Optimistic Updates**: Instant UI feedback for better perceived performance\n- **Skeleton Loading**: Progressive content loading with placeholder states\n\n## 🔒 Security Features\n\n### Input Validation \u0026 Sanitization\n\n- **Zod Schemas**: Runtime type validation for all inputs\n- **DOMPurify**: XSS protection for user-generated content\n- **SQL Injection Prevention**: Parameterized queries with Drizzle ORM\n\n### File Upload Security\n\n- **File Type Validation**: Strict MIME type checking on both client and server\n- **Size Limits**: Configurable file size restrictions\n- **Secure Storage**: Private cloud storage with access controls\n\n### Authentication \u0026 Authorization\n\n- **CAPTCHA Integration**: Google reCAPTCHA for spam prevention\n- **CORS Configuration**: Secure cross-origin resource sharing\n- **Helmet Security Headers**: Comprehensive HTTP security headers\n\n## 📝 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🆘 Support\n\n- **Issues**: Bug reports and feature requests via GitHub Issues\n- **Discussions**: Community discussions and Q\u0026A on GitHub Discussions\n\n---\n\n**Built with ❤️ using modern web technologies and best practices**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeleshyn%2Fcomments-spa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeleshyn%2Fcomments-spa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeleshyn%2Fcomments-spa/lists"}