diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b692c2d..0000000 --- a/.gitignore +++ /dev/null @@ -1,49 +0,0 @@ -# build artifacts -.next -out -dist -build - -# dependencies -node_modules/ -site/node_modules/ - -# local env / secrets -.env -.env.* -!.env.example - -# runtime stuff -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -debug.log -*.pid -*.pid.lock - -# editor / tooling junk -.vscode/ -.cursor/ -.cursor-server/ -.qoder-server/ -.windsurf-server/ -*.swp -.DS_Store - -# OS / cache -*.tgz -*.tar.gz -*.zip - -# big local data -site/media/ -media/ -chrome-user-data/ -.dev.db -swapfile - -# pm2 -.pm2/ -pm2.log diff --git a/COLLABORATION_WORKFLOW.md b/COLLABORATION_WORKFLOW.md deleted file mode 100644 index 7fcb7a9..0000000 --- a/COLLABORATION_WORKFLOW.md +++ /dev/null @@ -1,108 +0,0 @@ -# How to Collaborate with AI Assistants (Sonnet 4.5) via GitHub - -## Quick Setup - -Your repository is already connected: `git@github.com:enikqi/yapgrid.git` - -## Workflow Options - -### Option 1: GitHub Copilot Chat (Recommended) - -1. **In GitHub.com**: - - Go to your repository - - Open a Pull Request - - Click "Ask Copilot" or use the chat icon - - Select Sonnet 4.5 model - - Ask questions like: - - "Review this code for performance issues" - - "Suggest optimizations for this API route" - - "Fix the database connection problems" - -2. **In VS Code/Cursor**: - - Install GitHub Copilot extension - - Open Copilot Chat (`Cmd/Ctrl + L`) - - Select Sonnet 4.5 model - - Ask for help with code - -### Option 2: GitHub Codespaces - -1. Open repo in Codespaces (click "Code" → "Codespaces") -2. Use built-in Copilot Chat with Sonnet 4.5 -3. Make changes directly in browser -4. Commit and push from Codespaces - -### Option 3: Local Development with AI - -1. **Make changes locally** (with Cursor AI or other tools) -2. **Commit and push**: - ```bash - git checkout -b feature/your-feature - git add . - git commit -m "feat: your feature" - git push origin feature/your-feature - ``` -3. **Create PR on GitHub** -4. **Use Sonnet 4.5 in PR** to review and suggest improvements - -## Example: Using Sonnet 4.5 to Fix Issues - -### Step 1: Create a Branch -```bash -cd /home/ubuntu/apps/yapgrid -git checkout -b fix/performance-optimization -``` - -### Step 2: Ask Sonnet 4.5 for Help -In GitHub Copilot Chat (Sonnet 4.5): -``` -"The homepage is loading slowly. The API response takes 5+ seconds. -Analyze the codebase and suggest optimizations for the posts API route." -``` - -### Step 3: Apply Suggestions -- Review Sonnet 4.5's suggestions -- Implement the changes -- Test locally - -### Step 4: Commit and Push -```bash -git add . -git commit -m "perf: optimize posts API with Sonnet 4.5 suggestions" -git push origin fix/performance-optimization -``` - -### Step 5: Create PR and Review -- Create PR on GitHub -- Use Sonnet 4.5 in PR comments to: - - Review code quality - - Suggest further improvements - - Check for edge cases - -## Current Server Changes - -To apply changes from GitHub to server: - -```bash -cd /home/ubuntu/apps/yapgrid -git pull origin main -cd site -npm install # if package.json changed -pm2 restart yapgrid-nextjs -``` - -## Best Practices - -1. **Always create branches** for changes -2. **Use descriptive commit messages** -3. **Review AI suggestions** before applying -4. **Test locally** before pushing -5. **Use PRs** for code review with AI - -## Next Steps - -1. Current fixes are in branch: `fix/502-gateway-and-performance-improvements` -2. Push to GitHub: `git push origin fix/502-gateway-and-performance-improvements` -3. Create PR on GitHub -4. Use Sonnet 4.5 to review the PR -5. Merge after review - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ae34ff6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +Thank you for helping improve YapGrid documentation. + +## Accepted Contributions + +This repository accepts documentation-focused contributions, including: + +- Corrections and clarity improvements. +- Copy-paste-ready embed examples. +- Accessibility improvements. +- Translation improvements. +- Better explanations for common user issues. + +## Out of Scope + +Source-code contributions are out of scope for this repository. + +Do not submit application code, build files, dependencies, scripts, private service names, copied proprietary code, credentials, private URLs, or sensitive data. + +## Example Rules + +Examples must use only public YapGrid URLs such as: + +```text +https://yapgrid.com/embed/movie/550 +``` + +and: + +```text +https://yapgrid.com/embed/tv/1396/1/1 +``` + +Do not include internal endpoints, private service names, copied proprietary code, or sensitive information. + +## Markdown Style + +- Use clear professional English. +- Use consistent “YapGrid” branding. +- Keep examples copy-paste ready. +- Use valid Markdown. +- Avoid unsupported endpoints or unsupported parameters. diff --git a/DEPLOYMENT-FIX.md b/DEPLOYMENT-FIX.md deleted file mode 100644 index 10717ec..0000000 --- a/DEPLOYMENT-FIX.md +++ /dev/null @@ -1,134 +0,0 @@ -# 504 Gateway Timeout Fix - Deployment Instructions - -## Problem -The YapGrid site was experiencing 504 Gateway Timeout errors from nginx. This occurred when nginx could not receive a response from the Next.js server within the configured timeout period (60 seconds). - -## Root Causes -1. **Short nginx timeout settings**: The proxy timeouts were set to 60 seconds, which is insufficient for: - - Initial page loads with heavy data fetching (posts, communities, subscriptions) - - API endpoints performing complex database queries - - Pages with multiple simultaneous API calls - -2. **Insufficient buffer configuration**: Limited buffer sizes could cause issues with large responses - -## Changes Made - -### 1. nginx Configuration (`site/nginx.conf`) -Updated timeout and buffer settings: - -#### Main proxy location (`/`) -- **Increased timeouts** from 60s to 120s (2 minutes): - - `proxy_connect_timeout`: 120s - - `proxy_send_timeout`: 120s - - `proxy_read_timeout`: 120s -- **Added buffer settings** for large responses: - - `proxy_buffering`: on - - `proxy_buffer_size`: 4k - - `proxy_buffers`: 8 4k - - `proxy_busy_buffers_size`: 8k - -#### API endpoints (`/api/`) -- Added a dedicated location block for API endpoints -- **Extended timeouts**: 180s (3 minutes) for all timeout settings -- **Larger buffer settings** for API responses: - - `proxy_buffer_size`: 8k - - `proxy_buffers`: 16 8k - - `proxy_busy_buffers_size`: 16k - -### 2. Next.js Configuration (`site/next.config.ts`) -Added performance optimizations: - -- **Server Components External Packages**: Added `prisma` and `@prisma/client` to prevent bundling issues -- **Optimized Package Imports**: Added `lucide-react` to reduce bundle size -- **Allowed Origins**: Strict environment-specific allowed origins with full URLs: - - Production: https://yapgrid.com and https://www.yapgrid.com - - Development: http://localhost:3002 - -## Deployment Steps - -### 1. Backup Current Configuration -```bash -sudo cp /etc/nginx/sites-available/yapgrid /etc/nginx/sites-available/yapgrid.backup.$(date +%Y%m%d) -``` - -### 2. Update nginx Configuration -```bash -cd /home/ubuntu/apps/yapgrid -git pull origin main # or your branch name -sudo cp site/nginx.conf /etc/nginx/sites-available/yapgrid -``` - -### 3. Test nginx Configuration -```bash -sudo nginx -t -``` - -If the test passes, reload nginx: -```bash -sudo systemctl reload nginx -``` - -### 4. Rebuild and Restart Next.js Application -```bash -cd /home/ubuntu/apps/yapgrid/site -npm run build -pm2 restart yapgrid-nextjs -``` - -### 5. Monitor the Application -```bash -# Watch nginx error logs -sudo tail -f /var/log/nginx/yapgrid.error.log - -# Watch PM2 logs -pm2 logs yapgrid-nextjs - -# Check application status -pm2 status -``` - -## Expected Results -- ✅ No more 504 Gateway Timeout errors on page loads (up to 2 minutes allowed) -- ✅ API endpoints can take up to 3 minutes to respond (though they should be much faster) -- ✅ Better handling of large responses with improved buffering -- ✅ Improved connection management between nginx and Next.js -- ✅ Environment-specific security with conditional allowed origins - -## Rollback Plan -If issues occur after deployment: - -```bash -# Restore previous nginx config -sudo cp /etc/nginx/sites-available/yapgrid.backup.YYYYMMDD /etc/nginx/sites-available/yapgrid -sudo nginx -t -sudo systemctl reload nginx - -# Revert Next.js changes -cd /home/ubuntu/apps/yapgrid -git checkout main # or previous working branch -cd site -npm run build -pm2 restart yapgrid-nextjs -``` - -## Additional Performance Recommendations - -### For future optimization: -1. **Refactor nginx configuration**: Extract common proxy headers to a separate include file (e.g., `proxy_headers.conf`) to reduce duplication and improve maintainability -2. **Add caching** for frequently accessed API endpoints -3. **Implement pagination** with smaller page sizes (current: 15 items) -4. **Add database indexes** on frequently queried fields -5. **Consider Redis caching** for API responses -6. **Add rate limiting** to prevent abuse -7. **Implement API route segments** for better caching strategies -8. **Use incremental static regeneration (ISR)** for semi-static pages - -## Testing Checklist -- [ ] Homepage loads without timeout -- [ ] API endpoints respond within timeout -- [ ] Search functionality works -- [ ] Post filtering works (popular, videos, images, etc.) -- [ ] Community pages load correctly -- [ ] Admin panel is accessible -- [ ] nginx logs show no timeout errors -- [ ] PM2 shows application is running stable diff --git a/GITHUB_COLLABORATION.md b/GITHUB_COLLABORATION.md deleted file mode 100644 index 8dc816b..0000000 --- a/GITHUB_COLLABORATION.md +++ /dev/null @@ -1,193 +0,0 @@ -# GitHub Collaboration Guide with AI Assistants (Sonnet 4.5) - -This guide explains how to collaborate on this project using GitHub with AI assistants like Claude Sonnet 4.5 (via GitHub Copilot Chat or similar). - -## Repository Information - -- **Repository**: `git@github.com:enikqi/yapgrid.git` -- **Default Branch**: `main` - -## Quick Start Workflow - -### 1. Making Changes Locally (Server) - -```bash -cd /home/ubuntu/apps/yapgrid - -# Check current status -git status - -# Create a new branch for your changes -git checkout -b fix/issue-description - -# Make your changes, then commit -git add . -git commit -m "Description of changes" - -# Push to GitHub -git push origin fix/issue-description -``` - -### 2. Using Sonnet 4.5 / GitHub Copilot Chat - -#### Option A: GitHub Copilot Chat in VS Code/Cursor -1. Open the repository in VS Code/Cursor -2. Use the Copilot Chat panel (usually `Cmd/Ctrl + L`) -3. Ask Sonnet 4.5 to: - - Review code - - Suggest improvements - - Fix bugs - - Add features - - Write tests - -#### Option B: GitHub.com Pull Request Reviews -1. Create a Pull Request on GitHub -2. Use GitHub Copilot Chat in the PR: - - Click "Ask Copilot" in the PR - - Ask Sonnet 4.5 to review the changes - - Get suggestions for improvements - -#### Option C: GitHub Codespaces -1. Open the repo in GitHub Codespaces -2. Use the built-in Copilot Chat with Sonnet 4.5 -3. Make changes directly in the browser - -### 3. Collaboration Workflow - -#### When working with AI assistants: - -1. **Create a feature branch**: - ```bash - git checkout -b feature/your-feature-name - ``` - -2. **Make changes with AI assistance**: - - Use Sonnet 4.5 to help write code - - Review AI suggestions before committing - - Test changes locally - -3. **Commit with descriptive messages**: - ```bash - git commit -m "feat: add new feature with AI assistance" - ``` - -4. **Push and create PR**: - ```bash - git push origin feature/your-feature-name - ``` - Then create a Pull Request on GitHub - -5. **Review with AI**: - - Use Sonnet 4.5 in PR comments to review - - Ask for code improvements - - Get suggestions for optimization - -6. **Merge after approval**: - - Merge PR to `main` - - Pull latest on server: - ```bash - git checkout main - git pull origin main - ``` - -## Best Practices - -### Commit Message Format -- `fix:` - Bug fixes -- `feat:` - New features -- `perf:` - Performance improvements -- `refactor:` - Code refactoring -- `docs:` - Documentation changes - -### Branch Naming -- `fix/` - Bug fixes -- `feat/` - New features -- `perf/` - Performance improvements -- `refactor/` - Code refactoring - -### Using AI Effectively -1. **Be specific**: Give clear context about what you want -2. **Review AI suggestions**: Always review AI-generated code -3. **Test changes**: Test AI-suggested code before committing -4. **Ask for explanations**: Have AI explain complex changes -5. **Iterate**: Use AI feedback to improve code - -## Example: Fixing an Issue with AI - -```bash -# 1. Create branch -git checkout -b fix/slow-page-load - -# 2. In GitHub Copilot Chat (Sonnet 4.5), ask: -# "The homepage is loading slowly. Analyze the code and suggest optimizations." - -# 3. Apply AI suggestions - -# 4. Test locally -npm run dev - -# 5. Commit -git add . -git commit -m "perf: optimize homepage loading with AI suggestions" - -# 6. Push and create PR -git push origin fix/slow-page-load -``` - -## Current Server Setup - -The server automatically pulls from `main` branch. To deploy changes: - -1. Merge PR to `main` on GitHub -2. On server, run: - ```bash - cd /home/ubuntu/apps/yapgrid - git pull origin main - pm2 restart yapgrid-nextjs - ``` - -## Troubleshooting - -### If changes conflict: -```bash -git fetch origin -git rebase origin/main -# Resolve conflicts, then: -git add . -git rebase --continue -``` - -### If you need to undo changes: -```bash -git checkout -- # Discard local changes -git reset HEAD~1 # Undo last commit -``` - -## Integration with Cursor AI - -Since you're using Cursor with AI assistance: - -1. **Make changes in Cursor** with AI help -2. **Commit locally**: - ```bash - git add . - git commit -m "Your commit message" - ``` -3. **Push to GitHub**: - ```bash - git push origin your-branch - ``` -4. **Use GitHub Copilot Chat** (Sonnet 4.5) to review in PR -5. **Merge and deploy** - -## Next Steps - -1. Commit current fixes to a new branch -2. Create a PR for review -3. Use Sonnet 4.5 in GitHub to review and suggest improvements -4. Merge and deploy - ---- - -**Note**: Always test changes locally before pushing to production! - diff --git a/README.md b/README.md new file mode 100644 index 0000000..712b3bc --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +# YapGrid Documentation + +![YapGrid favicon](https://yapgrid.com/favicon.svg) + +YapGrid is an ad-free movie and TV streaming embed player for worldwide users. It lets website owners add a public playback experience to movie pages, TV episode pages, blogs, directories, community sites, and entertainment platforms by using simple iframe embed URLs. + +YapGrid is designed for builders who want to start a movie or TV website without building a custom player from scratch. You only need the public YapGrid embed URL, a TMDB ID, and a responsive iframe. Movies and TV episodes can be embedded directly from `https://yapgrid.com` with no API key required. + +> This repository contains documentation only. It does not contain the YapGrid application source code, and it does not mean the player source code is open source. + +## Live Website + +- Website: +- Movie embed example: +- TV embed example: + +## What YapGrid Offers + +YapGrid provides a clean public player experience that can be embedded worldwide. It supports: + +- Movies using TMDB movie IDs. +- TV episodes using TMDB TV IDs, season numbers, and episode numbers. +- A fast multi-server streaming network with an in-player Server X/Y/Z selector. +- Automatic synchronized subtitles when available for the selected title. +- Local `.srt` and `.vtt` subtitle uploads inside the player. +- External subtitle URLs through embed parameters. +- In-player subtitle translation for users who want subtitles in another language when supported. +- Quality selection, playback speed, fullscreen, volume, seek controls, and loading indicators. +- Responsive behavior for desktop, tablet, and mobile screens. + +## Built for Movie and TV Website Owners + +YapGrid is useful when you want to create a movie or TV website that focuses on discovery, catalog pages, watch pages, editorial content, or community recommendations. Instead of designing a player interface, server selector, subtitle selector, subtitle upload workflow, and responsive playback layout yourself, you can place a YapGrid iframe on your page and keep your website focused on the user experience around the player. + +A typical website flow can be simple: + +1. Create a page for a movie or TV episode. +2. Store or display the correct TMDB ID. +3. Add the YapGrid iframe for that movie or episode. +4. Let the player handle playback controls, server switching, subtitles, subtitle uploads, and subtitle translation. + +Website owners remain responsible for how they use public embeds and for following applicable laws, platform rules, and content policies in their own projects. + +## Standout Feature: Subtitle Translation Inside the Player + +One of YapGrid’s most useful player features is direct subtitle translation. When subtitle translation is available, users can translate subtitles from inside the player controls without leaving the viewing experience. + +This is especially helpful for international audiences. A viewer may open a movie or TV episode, select available subtitles, and use the translation option to make the content easier to follow in their preferred language. This reduces friction for users because they do not need to search for separate translated subtitle files before watching. + +Subtitle availability, translation availability, and translation quality can vary by title, language, and browser behavior. YapGrid does not make uptime, subtitle, or translation guarantees. + +## Quick Start + +### Movie Embed + +```html + +``` + +### TV Embed + +```html + +``` + +## Public Embed URLs + +Movie: + +```text +https://yapgrid.com/embed/movie/{tmdbId} +``` + +TV episode: + +```text +https://yapgrid.com/embed/tv/{tmdbId}/{season}/{episode} +``` + +No API key is required. + +## Documentation + +| Page | Description | +| --- | --- | +| [Getting Started](docs/getting-started.md) | Basic setup and embed requirements. | +| [Movie Embed](docs/movie-embed.md) | How to embed movies using TMDB IDs. | +| [TV Embed](docs/tv-embed.md) | How to embed TV episodes using TMDB IDs, season, and episode numbers. | +| [Parameters](docs/parameters.md) | Optional query parameters for playback, subtitles, language, and appearance. | +| [Subtitles](docs/subtitles.md) | Automatic subtitles, local uploads, external subtitle links, and translation. | +| [Player Controls](docs/player-controls.md) | Available controls inside the YapGrid player. | +| [Troubleshooting](docs/troubleshooting.md) | Common embed and playback issues. | +| [FAQ](docs/faq.md) | Frequently asked questions. | +| [Contributing](CONTRIBUTING.md) | How to improve this documentation. | +| [Security](SECURITY.md) | How to report documentation or public embed security concerns. | + +## Example URLs with Parameters + +External subtitle example: + +```text +https://yapgrid.com/embed/movie/550?sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.srt&sub_lang=en&sub_label=English +``` + +Combined example: + +```text +https://yapgrid.com/embed/movie/550?autoplay=1&server=x&lang=en&sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.vtt&sub_lang=en&sub_label=English +``` + +## Scope + +This repository is for public user documentation. It helps website owners and viewers understand how to embed and use the public YapGrid player correctly. It does not publish application source code, deployment details, private configuration, or operational details. \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..9246b30 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security + +This repository is for YapGrid public documentation only. + +## Reporting a Concern + +If you find a documentation issue or a public embed security concern, please report it responsibly through GitHub. + +Use GitHub Security Advisories when available. If that option is not available, open a GitHub issue with a minimal, non-sensitive description. + +## What Not to Post Publicly + +Do not publish credentials, private stream URLs, private user data, or sensitive information in public issues, pull requests, comments, or screenshots. + +## Documentation Scope + +This repository does not contain the YapGrid application source code. Reports should focus on documentation or public embed behavior. diff --git a/SONNET_4.5_REVIEW_GUIDE.md b/SONNET_4.5_REVIEW_GUIDE.md deleted file mode 100644 index 34c8ea1..0000000 --- a/SONNET_4.5_REVIEW_GUIDE.md +++ /dev/null @@ -1,206 +0,0 @@ -# Udhëzues për Përdorimin e Sonnet 4.5 në GitHub - -## Hapi 1: Krijoni Pull Request - -1. Shkoni në: https://github.com/enikqi/yapgrid -2. Klikoni "Compare & pull request" ose "New Pull Request" -3. Zgjidhni branch: `fix/502-gateway-and-performance-improvements` -4. Klikoni "Create pull request" - -## Hapi 2: Review i Parë i Site me Sonnet 4.5 - -### Prompt 1: Review i Plotë i Kodit - -Kopjoni dhe ngjisni këtë në GitHub Copilot Chat (Sonnet 4.5): - -``` -Please review the entire codebase for this Next.js application. Focus on: - -1. **Performance Issues**: - - Slow API responses (currently 5+ seconds) - - Database query optimization needs - - Nginx configuration improvements - -2. **Code Quality**: - - Best practices violations - - Potential bugs or errors - - Security concerns - -3. **Architecture**: - - Database connection handling - - API route structure - - Component organization - -4. **Specific Areas to Check**: - - `/app/api/posts/route.ts` - Posts API endpoint - - `/app/page.tsx` - Homepage component - - `/lib/db/prisma.ts` - Database connection - - `/ecosystem.config.js` - PM2 configuration - - Nginx proxy settings - -Provide a comprehensive review with specific recommendations for improvements. -``` - -## Hapi 3: Problem me Homepage - Postet në Rend të Gabuar - -### Problem: -- Postet shfaqen në rend të gabuar gjatë scroll-it -- Posti i dytë del i pari -- Loading është shumë i ngadaltë - -### Prompt 2: Analizë e Problemit të Homepage - -``` -I have a critical issue with the homepage posts display: - -**Problem Description:** -- Posts appear in wrong order during scrolling -- The second post appears as first during scroll -- Loading is very slow (5+ seconds) -- Posts jump around when scrolling - -**Files to Review:** -- `/app/page.tsx` - Homepage component -- `/app/api/posts/route.ts` - Posts API -- `/app/api/recommendations/route.ts` - Recommendations API - -**Questions:** -1. Why are posts appearing in wrong order during scroll? -2. Is there a virtual scrolling implementation issue? -3. Are API responses being cached incorrectly? -4. Is the sorting logic in the database query correct? -5. Why is loading so slow? - -Please analyze the code and provide: -- Root cause analysis -- Specific fixes needed -- Code examples for the fixes -- Performance optimizations -``` - -## Hapi 4: Zgjidhje e Detajuar - -### Prompt 3: Zgjidhje Konkrete - -``` -Based on the previous analysis, please provide: - -1. **Fixed code** for `/app/page.tsx` that: - - Maintains correct post order during scroll - - Prevents posts from jumping around - - Optimizes rendering performance - -2. **Optimized code** for `/app/api/posts/route.ts` that: - - Reduces query time from 5+ seconds to under 1 second - - Ensures correct sorting order - - Adds proper indexing recommendations - -3. **Database optimization**: - - Suggest indexes needed in Prisma schema - - Query optimization strategies - -4. **Step-by-step implementation guide**: - - What files to modify - - What code to add/remove - - How to test the fixes - -Provide complete, working code solutions. -``` - -## Hapi 5: Testimi dhe Verifikimi - -### Prompt 4: Verifikim Final - -``` -After implementing the fixes, please verify: - -1. Are all the performance improvements implemented? -2. Is the post order issue fixed? -3. Are there any edge cases we missed? -4. Are there any breaking changes? -5. What should we test before merging? - -Provide a testing checklist. -``` - -## Struktura e Punës - -### Workflow: -1. **Ti** → Lexo kodin aktual në GitHub -2. **Unë** → Përgatis prompt-in për Sonnet 4.5 -3. **Ti** → Kopjo prompt-in në GitHub Copilot Chat (Sonnet 4.5) -4. **Sonnet 4.5** → Analizon dhe jep rekomandime -5. **Ti** → Kopjo përgjigjen e Sonnet 4.5 këtu -6. **Unë** → Zbato ndryshimet në kod -7. **Ti** → Testo dhe commit-o ndryshimet - -## Proces i Detajuar - -### Hapi 1: Krijoni PR -```bash -# Tashmë u bë - PR është krijuar -``` - -### Hapi 2: Hapni GitHub Copilot Chat -1. Shkoni në PR në GitHub -2. Klikoni "Ask Copilot" ose ikona e chat-it -3. Sigurohuni që Sonnet 4.5 është zgjedhur si model - -### Hapi 3: Kopjoni Prompt-in e Parë -- Kopjoni "Prompt 1: Review i Plotë i Kodit" nga më lart -- Ngjisni në chat -- Pritni përgjigjen e Sonnet 4.5 - -### Hapi 4: Kopjoni Prompt-in e Dytë -- Kopjoni "Prompt 2: Analizë e Problemit të Homepage" -- Ngjisni në chat -- Sonnet 4.5 do të analizojë problemin specifik - -### Hapi 5: Merrni Zgjidhjen -- Kopjoni "Prompt 3: Zgjidhje Konkrete" -- Sonnet 4.5 do të japë kodin e fixuar - -### Hapi 6: Kopjoni Përgjigjen Këtu -- Kopjo përgjigjen e plotë të Sonnet 4.5 -- Unë do të zbatoj ndryshimet në kod - -### Hapi 7: Testo dhe Commit -```bash -cd /home/ubuntu/apps/yapgrid -# Testo ndryshimet -npm run dev -# Nëse funksionon, commit-o -git add . -git commit -m "fix: homepage posts order and loading performance" -git push -``` - -## Shembull i Plotë i Prompts - -### Prompt për Review të Plotë: -``` -Review the codebase and identify performance issues, especially: -- Slow API responses in /api/posts -- Post ordering issues on homepage -- Database query optimization -``` - -### Prompt për Problem Specifik: -``` -Analyze /app/page.tsx and /app/api/posts/route.ts. -Posts appear in wrong order during scroll and loading is 5+ seconds. -Find the root cause and provide fixes. -``` - -### Prompt për Zgjidhje: -``` -Provide complete fixed code for: -1. /app/page.tsx - Fix post ordering -2. /app/api/posts/route.ts - Optimize query performance -Include explanations for each change. -``` - ---- - -**Kujdes**: Gjithmonë rishiko kodin që Sonnet 4.5 sugjeron para se ta zbatojsh! - diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 0000000..76474ce --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,81 @@ +# FAQ + +## Is an API key required? + +No. YapGrid public embeds do not require an API key. + +## Can I use YapGrid to start a movie or TV website? + +Yes. YapGrid is designed so website owners can add a public movie and TV player to their own pages using simple iframe embed URLs. + +You can build your own website around YapGrid with movie pages, TV episode pages, search pages, category pages, editorial content, and recommendation sections. The YapGrid iframe can then provide the embedded player experience on each watch page. + +Website owners are responsible for how they use embeds and for following applicable laws, platform rules, and content policies. + +## What makes YapGrid useful for international users? + +YapGrid supports subtitles, local subtitle uploads, external subtitle URLs, and subtitle translation inside the player when available. + +Subtitle translation is especially useful because users can adjust subtitle language options directly from the player controls instead of leaving the viewing experience to look for another subtitle file. + +## Can the same URL switch servers? + +Yes. Server selection is available inside the player. + +## Are separate URLs needed for Server X/Y/Z? + +No. You can set an initial server with the `server` parameter, but users can switch servers inside the player. + +## Can users upload subtitles? + +Yes. Users can upload local `.srt` or `.vtt` subtitle files from the player. + +Local subtitle uploads are limited to 5 MB and remain inside the user’s browser session. + +## Can users attach an external subtitle URL? + +Yes. Website owners can use the `sub_url` parameter to attach an external `.srt` or `.vtt` subtitle file. + +The subtitle URL must be URL-encoded, available over HTTP or HTTPS, and accessible by the browser with CORS allowed. + +## Can subtitles be translated inside the player? + +Yes, when subtitle translation is available. Users can select a subtitle track and use the translation option directly inside the player controls. + +Availability and quality can vary by title, language, selected subtitle track, and browser behavior. + +## Are movies and TV shows supported? + +Yes. Movies and TV episodes are supported using TMDB IDs. + +Movies use this format: + +```text +https://yapgrid.com/embed/movie/{tmdbId} +``` + +TV episodes use this format: + +```text +https://yapgrid.com/embed/tv/{tmdbId}/{season}/{episode} +``` + +## Can autoplay always be guaranteed? + +No. Browser autoplay policies apply. Users may need to start playback manually. + +## Does this repository contain the player source code? + +No. This repository contains documentation only. + +## Is the YapGrid player source code open source? + +No. This documentation repository does not publish the YapGrid application source code and does not mean the player source code is open source. + +## Does YapGrid guarantee that every title will play on every server? + +No. Availability may vary. If a title does not load on one server, users can retry or select another server from the player controls. + +## Does YapGrid guarantee subtitles for every title? + +No. Subtitle availability depends on the selected movie or TV episode. Some titles may have several subtitle languages, while others may have limited or no available subtitles. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..2083824 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,104 @@ +# Getting Started + +YapGrid provides public embed URLs for movies and TV episodes. You can place these URLs inside an iframe on your website and create a watch page without building a custom video player interface. + +The goal is simple: a website owner can create movie or TV pages, use TMDB IDs to point the embed to the correct title, and let YapGrid provide the public player experience inside the page. + +## Who This Is For + +YapGrid documentation is useful for: + +- Website owners creating a movie or TV catalog. +- Developers building movie detail pages, episode pages, or entertainment directories. +- Publishers who want a responsive player area inside articles or landing pages. +- Communities that want a simple embed format for movie and TV pages. +- International websites that need subtitles, subtitle upload, and in-player subtitle translation. + +This repository explains public embed usage only. It does not include application source code or private operational details. + +## What You Need + +To create an embed, you need: + +- A TMDB ID for the movie or TV show. +- For TV shows, the season number and episode number. +- A responsive iframe container on your page. + +No API key is required. + +## Basic Workflow + +A simple YapGrid integration usually looks like this: + +1. Choose the movie or TV episode you want to show on your page. +2. Find the correct TMDB ID. +3. Build the YapGrid embed URL. +4. Place the URL inside an iframe. +5. Use a responsive 16:9 layout so the player works across screen sizes. +6. Add optional query parameters for autoplay, initial server, title, language, theme, or subtitles. + +## Movie URL Format + +```text +https://yapgrid.com/embed/movie/{tmdbId} +``` + +Example: + +```text +https://yapgrid.com/embed/movie/550 +``` + +## TV URL Format + +```text +https://yapgrid.com/embed/tv/{tmdbId}/{season}/{episode} +``` + +Example: + +```text +https://yapgrid.com/embed/tv/1396/1/1 +``` + +## Recommended iframe Style + +Use a responsive 16:9 layout so the player scales cleanly across desktop, tablet, and mobile screens. + +```html + +``` + +## Why the iframe Approach Is Useful + +The iframe approach makes YapGrid easy to place inside many types of websites. You can build your own website design, navigation, search pages, movie cards, TV episode pages, and recommendation sections around the player while keeping the actual player experience consistent. + +Inside the player, users can access playback controls, server selection, quality selection, subtitles, subtitle upload, subtitle translation, playback speed, volume, fullscreen, and loading indicators. + +## Subtitle Translation + +YapGrid includes subtitle translation inside the player when available. This helps international users follow a movie or episode in a language that is easier for them to understand. + +For example, a user may select an available subtitle track and use the translation option from the player controls. This avoids forcing the user to leave the player just to look for a translated subtitle file somewhere else. + +Availability may vary by title, language, and browser behavior. + +## Optional Parameters + +YapGrid supports optional query parameters for autoplay, initial server, subtitle language, title, theme, and external subtitles. + +See [Parameters](parameters.md) for the full list. + +## Next Steps + +- Use [Movie Embed](movie-embed.md) for movie examples. +- Use [TV Embed](tv-embed.md) for episode examples. +- Use [Parameters](parameters.md) to customize embed behavior. +- Use [Subtitles](subtitles.md) to add or manage subtitle tracks. +- Use [Player Controls](player-controls.md) to understand what users can do inside the player. +- Use [Troubleshooting](troubleshooting.md) if an embed does not behave as expected. \ No newline at end of file diff --git a/docs/movie-embed.md b/docs/movie-embed.md new file mode 100644 index 0000000..08042c8 --- /dev/null +++ b/docs/movie-embed.md @@ -0,0 +1,52 @@ +# Movie Embed + +Use the movie embed endpoint when you want to embed a movie by TMDB ID. + +## URL Format + +```text +https://yapgrid.com/embed/movie/{tmdbId} +``` + +Replace `{tmdbId}` with the movie TMDB ID. + +Example: + +```text +https://yapgrid.com/embed/movie/550 +``` + +## iframe Example + +```html + +``` + +## With Optional Parameters + +You can add optional query parameters to adjust the initial player behavior. + +Example with autoplay, initial server, and preferred subtitle language: + +```text +https://yapgrid.com/embed/movie/550?autoplay=1&server=x&lang=en +``` + +Example with a custom displayed title: + +```text +https://yapgrid.com/embed/movie/550?title=Fight%20Club +``` + +When adding text values such as a title, URL-encode spaces and special characters. + +## Notes + +- The user can switch servers from inside the player. +- Subtitle availability can vary by movie. +- Autoplay may still be blocked by browser policy, especially when sound is enabled. diff --git a/docs/parameters.md b/docs/parameters.md new file mode 100644 index 0000000..cc15ec2 --- /dev/null +++ b/docs/parameters.md @@ -0,0 +1,47 @@ +# Parameters + +YapGrid embed URLs support optional query parameters. Add parameters after the embed URL using `?`, and combine multiple parameters with `&`. + +Example: + +```text +https://yapgrid.com/embed/movie/550?autoplay=1&server=x&lang=en +``` + +## Optional Query Parameters + +| Parameter | Type | Values / Example | Description | +| --- | --- | --- | --- | +| `autoplay` | boolean | `1`, `0`, `true`, `false` | Requests automatic playback. Browsers may still block autoplay with sound, so users may need to start playback manually. | +| `server` | string | `x`, `y`, `z` | Sets the initial server. The user can still switch servers from the player controls. | +| `lang` | string | `en`, `sq`, `de`, `fr` | Preferred/default subtitle language. | +| `title` | string | `Fight%20Club` | Overrides the title displayed by the player. Text values should be URL-encoded. | +| `theme` | string | `red`, `blue`, `dark` | Sets the player accent theme when supported. | +| `sub_url` | string | URL-encoded `.srt` or `.vtt` URL | Adds an external subtitle file. The subtitle host must allow browser CORS access. | +| `sub_lang` | string | `en`, `sq`, `de`, `fr` | Language code for the subtitle supplied through `sub_url`. | +| `sub_label` | string | `English` | Custom name displayed for the external subtitle track. | +| `ds_lang` | string | `en`, `sq`, `de`, `fr` | Compatibility alias for `sub_lang`. | + +## External Subtitle Example + +```text +https://yapgrid.com/embed/movie/550?sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.srt&sub_lang=en&sub_label=English +``` + +## Combined Example + +```text +https://yapgrid.com/embed/movie/550?autoplay=1&server=x&lang=en&sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.vtt&sub_lang=en&sub_label=English +``` + +## URL Encoding + +Values such as `title`, `sub_url`, and `sub_label` should be URL-encoded when they contain spaces, symbols, or a full URL. + +Example: + +```text +Fight Club -> Fight%20Club +``` + +For subtitle links, encode the full subtitle URL before placing it in `sub_url`. diff --git a/docs/player-controls.md b/docs/player-controls.md new file mode 100644 index 0000000..5282b6b --- /dev/null +++ b/docs/player-controls.md @@ -0,0 +1,77 @@ +# Player Controls + +The YapGrid player includes playback, subtitle, viewing, and streaming controls designed for public movie and TV embeds. + +The controls are built so users can watch, switch servers, manage subtitles, translate subtitles when available, adjust playback, and move between viewing modes without leaving the embedded player. + +## Playback + +- **Play and pause**: Start or stop playback from the main control bar. +- **Seek bar**: Jump to a specific point in the movie or episode. +- **Rewind and forward**: Move backward or forward quickly during playback. +- **Playback speed**: Adjust playback speed when supported by the browser. + +These controls help users navigate longer movies and TV episodes without needing any extra page controls outside the iframe. + +## Audio + +- **Volume control**: Increase or reduce audio volume. +- **Mute and unmute**: Quickly silence or restore audio. + +Browser and device settings may also affect audio behavior, especially on mobile devices. + +## Streaming Options + +- **Server X/Y/Z selector**: Choose between available server options directly inside the player. +- **Quality selector**: Select a playback quality when multiple quality options are available. +- **Buffering and loading indicators**: See when content is preparing, loading, or switching. + +The server selector is useful because availability may vary by title, region, browser, and moment. Users do not need separate URLs for Server X, Server Y, and Server Z. A website can use one embed URL, and the user can switch servers from the player controls. + +## Subtitles + +- **Subtitle selector**: Choose from available subtitle tracks. +- **Local subtitle upload**: Upload `.srt` or `.vtt` files for the current browser session. +- **External subtitle support**: Use embed parameters to attach a subtitle file URL. +- **Subtitle translation**: Translate subtitles from inside the player when available. + +### Subtitle Translation + +Subtitle translation is a key YapGrid feature for international users. When available, users can translate a selected subtitle track directly from the player controls. + +This makes the player more useful for multilingual websites because viewers can keep watching while adjusting subtitle language options inside the same player interface. + +Translation availability and quality can vary by title, subtitle track, language, and browser behavior. + +## Viewing + +- **Fullscreen**: Expand the player to fullscreen when allowed by the browser and iframe permissions. +- **Picture-in-picture**: Use picture-in-picture when supported by the browser. +- **Responsive layout**: The player is designed to fit desktop, tablet, and mobile layouts. + +For fullscreen support, the iframe should include `allowfullscreen` and `allow="fullscreen"` or a broader allow value that includes fullscreen permission. + +## Mobile Behavior + +On mobile devices, controls may appear in a compact layout. Some controls can be grouped behind menus depending on screen size. + +Mobile behavior can also depend on: + +- Browser autoplay rules. +- Device fullscreen rules. +- Touch controls. +- Screen orientation. +- Network conditions. + +Use a responsive iframe style such as `width:100%; aspect-ratio:16/9; border:0` so the player keeps a clean layout on smaller screens. + +## Recommended iframe Permissions + +Use the following iframe permissions for the best public embed experience: + +```html +allow="autoplay; fullscreen; picture-in-picture" +allowfullscreen +``` + +Autoplay may still be blocked by browser policy, especially when audio is enabled. \ No newline at end of file diff --git a/docs/subtitles.md b/docs/subtitles.md new file mode 100644 index 0000000..37119c9 --- /dev/null +++ b/docs/subtitles.md @@ -0,0 +1,120 @@ +# Subtitles + +YapGrid supports automatic subtitle tracks, local subtitle uploads, external subtitle URLs, and subtitle translation inside the player when available. + +Subtitles are an important part of the YapGrid experience because movie and TV websites often serve international audiences. A viewer may prefer a different language than the original audio, may need captions for accessibility, or may want to upload a custom subtitle file for the current session. + +## Automatic Subtitles + +YapGrid automatically loads available subtitle tracks from multiple subtitle sources. + +Subtitle availability depends on the selected movie or episode. Some titles may have several subtitle languages, while others may have limited or no available subtitles. + +Users can select subtitles from the player controls. + +## In-Player Subtitle Translation + +YapGrid includes subtitle translation directly inside the player when the feature is available for the selected subtitle track. + +This is one of the most advanced parts of the public player experience. Instead of making users leave the player, search for another subtitle file, download it, and upload it manually, YapGrid can provide a translation option from inside the player controls. + +This is useful for: + +- International audiences who want subtitles in their preferred language. +- Websites that attract visitors from different countries. +- Users who find an available subtitle track but want to read it in another language. +- Viewers on mobile devices who need a faster, simpler subtitle workflow. + +Subtitle translation availability and quality can vary. Translation may depend on the selected subtitle track, language pair, browser behavior, and title availability. YapGrid does not guarantee that every subtitle track can be translated. + +## Local Subtitle Uploads + +Users can upload local subtitle files directly inside the player. + +Supported formats: + +- `.srt` +- `.vtt` + +Maximum local subtitle file size: + +```text +5 MB +``` + +Local subtitle uploads remain inside the user’s browser session. They are intended for the current viewing session and are not added as public subtitle tracks. + +A local upload is helpful when a user already has a subtitle file and wants to use it immediately without changing the embed URL. + +## External Subtitle URLs + +You can attach an external subtitle file using `sub_url`. + +External subtitle files must: + +- Use HTTP or HTTPS. +- Be in `.srt` or `.vtt` format. +- Allow browser CORS access. +- Be available to the viewer’s browser. + +Example: + +```text +https://yapgrid.com/embed/movie/550?sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.srt&sub_lang=en&sub_label=English +``` + +External subtitle URLs are useful when a website owner wants a specific subtitle track to appear with the embed by default. + +## Encoding External Subtitle URLs + +External URLs must be URL-encoded before being placed inside an embed URL. + +Original subtitle URL: + +```text +https://example.com/subtitles/english.srt +``` + +Encoded value: + +```text +https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.srt +``` + +You can encode a URL with `encodeURIComponent()` by passing the full subtitle URL as the value to encode. Use the encoded result as the value for `sub_url`. + +For example, the URL: + +```text +https://example.com/subtitles/english.srt +``` + +becomes: + +```text +https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.srt +``` + +## Subtitle Language and Label + +Use `sub_lang` to define the subtitle language code. + +Use `sub_label` to define the name displayed in the player. + +Example: + +```text +https://yapgrid.com/embed/movie/550?sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.vtt&sub_lang=en&sub_label=English +``` + +`ds_lang` is available as a compatibility alias for `sub_lang`. + +## Best Practices + +- Use `.vtt` or `.srt` files only. +- Keep local subtitle files under 5 MB. +- Use HTTPS for external subtitle files when possible. +- Make sure external subtitle files allow browser CORS access. +- Use clear labels such as `English`, `Albanian`, `German`, or `French`. +- Test the embed after adding an external subtitle URL. +- Remember that automatic subtitles and translation can vary by movie, episode, and language. \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..5980037 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,52 @@ +# Troubleshooting + +Use this guide when an embed does not behave as expected. + +## Timeout + +Retry playback or select another server from the player controls. + +## No Stream Found + +Test another server. Availability may differ between servers and titles. + +## Autoplay Blocked + +Interact with the player manually. Browsers may block autoplay, especially when sound is enabled. + +## Fullscreen Unavailable + +Make sure the iframe includes both `allowfullscreen` and fullscreen permission in the `allow` attribute. + +Recommended iframe attributes: + +```html +allow="autoplay; fullscreen; picture-in-picture" +allowfullscreen +``` + +## External Subtitles Not Loading + +Check the following: + +- The subtitle URL uses HTTPS or HTTP. +- The subtitle URL is URL-encoded. +- The subtitle file is `.srt` or `.vtt`. +- The subtitle host allows browser CORS access. +- The subtitle file is available to the viewer’s browser. + +## Local Subtitle Upload Rejected + +Use a `.srt` or `.vtt` file under 5 MB. + +## Embed Sizing Issues + +Use a responsive 16:9 iframe style: + +```html +style="width:100%; aspect-ratio:16/9; border:0" +``` + +## Changes Not Showing + +Hard refresh the browser after changing embed parameters. diff --git a/docs/tv-embed.md b/docs/tv-embed.md new file mode 100644 index 0000000..dc49957 --- /dev/null +++ b/docs/tv-embed.md @@ -0,0 +1,52 @@ +# TV Embed + +Use the TV embed endpoint when you want to embed a specific TV episode by TMDB ID, season number, and episode number. + +## URL Format + +```text +https://yapgrid.com/embed/tv/{tmdbId}/{season}/{episode} +``` + +Replace: + +- `{tmdbId}` with the TV show TMDB ID. +- `{season}` with the season number. +- `{episode}` with the episode number. + +Example: + +```text +https://yapgrid.com/embed/tv/1396/1/1 +``` + +## iframe Example + +```html + +``` + +## With Optional Parameters + +Example with autoplay, initial server, and preferred subtitle language: + +```text +https://yapgrid.com/embed/tv/1396/1/1?autoplay=1&server=y&lang=en +``` + +Example with a custom displayed title: + +```text +https://yapgrid.com/embed/tv/1396/1/1?title=Breaking%20Bad%20S01E01 +``` + +## Notes + +- The same URL can still use the player server selector. +- Subtitle availability can vary by episode. +- If one server does not load, users can select another server inside the player. diff --git a/docs/use-cases.md b/docs/use-cases.md new file mode 100644 index 0000000..a492d5c --- /dev/null +++ b/docs/use-cases.md @@ -0,0 +1,61 @@ +# YapGrid Use Cases + +YapGrid helps website owners embed the public YapGrid player on movie and TV pages by using simple iframe URLs. + +This page is for lawful, public documentation use only. Website owners are responsible for following applicable laws, platform rules, and content policies in their own projects. + +## Movie Website Pages + +A movie page can include a title, description, artwork, metadata, and a responsive YapGrid iframe. + +```html + +``` + +This format is useful for builders who want a simple movie embed player based on a TMDB movie ID. + +## TV Episode Pages + +A TV episode page can include the show title, season number, episode number, episode description, and a YapGrid TV embed. + +```html + +``` + +The TV embed format uses a TMDB TV ID, season number, and episode number. + +## International Subtitle Support + +YapGrid is useful for international movie and TV websites because the player supports automatic subtitles, local subtitle uploads, external subtitle URLs, and subtitle translation when available. + +Subtitle translation can help viewers read subtitles in a preferred language without leaving the player interface. + +## Responsive Embeds + +Use a responsive 16:9 iframe style for desktop, tablet, and mobile layouts. + +```html +style="width:100%; aspect-ratio:16/9; border:0" +``` + +## One Embed URL with Server Selection + +YapGrid includes a Server X/Y/Z selector inside the player. A website can use one embed URL, while viewers can switch servers from the player controls when needed. + +## External Subtitle Example + +```text +https://yapgrid.com/embed/movie/550?sub_url=https%3A%2F%2Fexample.com%2Fsubtitles%2Fenglish.srt&sub_lang=en&sub_label=English +``` + +External subtitle URLs must be URL-encoded and accessible by the viewer’s browser. \ No newline at end of file diff --git a/fix-videos-safari.sh b/fix-videos-safari.sh deleted file mode 100755 index e801e5d..0000000 --- a/fix-videos-safari.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -# Script to fix MP4 videos for Safari iOS compatibility -# Moves moov atom to beginning for fast start - -MEDIA_DIR="/home/ubuntu/apps/yapgrid/site/media" -FIXED_COUNT=0 -SKIPPED_COUNT=0 -ERROR_COUNT=0 - -echo "🔧 Fixing videos for Safari iOS compatibility..." -echo "📁 Media directory: $MEDIA_DIR" -echo "" - -# Find all MP4 files -find "$MEDIA_DIR" -type f -name "*.mp4" ! -name "*_fixed.mp4" ! -name "*_backup.mp4" | while read -r video_file; do - filename=$(basename "$video_file") - echo "Processing: $filename" - - # Create backup - backup_file="${video_file}.backup" - - # Check if already has faststart - if ffprobe "$video_file" 2>&1 | grep -q "major_brand.*isom"; then - # Try to fix it - temp_file="${video_file}.tmp.mp4" - - if ffmpeg -i "$video_file" -c copy -movflags +faststart "$temp_file" -y > /dev/null 2>&1; then - # Success - replace original - mv "$video_file" "$backup_file" - mv "$temp_file" "$video_file" - echo " ✅ Fixed: $filename" - FIXED_COUNT=$((FIXED_COUNT + 1)) - - # Remove backup after successful fix - rm -f "$backup_file" - else - echo " ❌ Error fixing: $filename" - ERROR_COUNT=$((ERROR_COUNT + 1)) - rm -f "$temp_file" - fi - else - echo " ⏭️ Skipped: $filename (already optimized)" - SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) - fi - - echo "" -done - -echo "" -echo "📊 Summary:" -echo " ✅ Fixed: $FIXED_COUNT" -echo " ⏭️ Skipped: $SKIPPED_COUNT" -echo " ❌ Errors: $ERROR_COUNT" -echo "" -echo "🎉 Done!" diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 7dba55a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,770 +0,0 @@ -{ - "name": "yapgrid", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "express": "^5.1.0" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index f6fc735..0000000 --- a/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "express": "^5.1.0" - } -} diff --git a/server.js b/server.js deleted file mode 100644 index 1d180a6..0000000 --- a/server.js +++ /dev/null @@ -1,31 +0,0 @@ -const { spawn } = require('child_process'); -const path = require('path'); - -const port = 3002; - -console.log('🚀 Starting YapGrid...'); -console.log(`📂 Working directory: ${path.join(__dirname, 'site')}`); - -// Start Next.js development server for now -const nextProcess = spawn('npm', ['run', 'dev'], { - cwd: path.join(__dirname, 'site'), - stdio: 'inherit', - shell: true, - env: { - ...process.env, - PORT: port.toString() - } -}); - -nextProcess.on('error', (error) => { - console.error('❌ Error starting Next.js:', error); - process.exit(1); -}); - -nextProcess.on('exit', (code) => { - console.error(`❌ Next.js exited with code ${code}`); - process.exit(1); -}); - -console.log(`✅ YapGrid starting on http://0.0.0.0:${port}`); -console.log('✅ Site will be LIVE at https://yapgrid.com'); \ No newline at end of file diff --git a/site/.gitignore b/site/.gitignore deleted file mode 100644 index 45254b6..0000000 --- a/site/.gitignore +++ /dev/null @@ -1,43 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts - -/app/generated/prisma diff --git a/site/AUTO-SYSTEM-GUIDE.md b/site/AUTO-SYSTEM-GUIDE.md deleted file mode 100644 index 23750e5..0000000 --- a/site/AUTO-SYSTEM-GUIDE.md +++ /dev/null @@ -1,186 +0,0 @@ -# Auto System - Comprehensive Guide - -## Overview -The auto system consists of three main components that work together to automatically fetch, process, and publish Reddit content to Pinterest. - -## Components - -### 1. Auto-Fetch -**Purpose**: Fetches new posts from Reddit campaigns - -**How it works**: -- Cycles through enabled campaigns -- Fetches 3 posts at a time per campaign -- Saves posts with status `NEW` -- Updates `lastRun` timestamp - -**Script**: `smart-auto-fetch.js` -```bash -node smart-auto-fetch.js -``` - -### 2. Auto-Process -**Purpose**: Processes NEW posts, downloads media, and marks them as READY - -**How it works**: -- Finds posts with status `NEW` -- Downloads media using MediaDownloader -- Creates asset records in database -- Updates post status to `READY` (only if media downloaded successfully) -- If media download fails, marks post as `FAILED` -- Sets `scheduledPublishAt` time - -**API**: `/api/posts/process` -```bash -curl -X POST http://localhost:3002/api/posts/process \ - -H "Content-Type: application/json" \ - -d '{"batchSize": 2}' -``` - -### 3. Auto-Publish -**Purpose**: Publishes READY posts to Pinterest - -**How it works**: -- Finds posts with status `READY` -- Only publishes posts that have assets -- Creates pins on Pinterest -- Updates post status to `PUBLISHED` -- Sets `publishedAt` timestamp - -**API**: `/api/auto-publish` -```bash -curl -X POST http://localhost:3002/api/auto-publish \ - -H "Content-Type: application/json" \ - -d '{"batchSize": 2}' -``` - -## Integrated System - -**Script**: `multi-campaign-system.js` - -This script runs all three components in a continuous loop, rotating through all campaigns: - -```bash -node multi-campaign-system.js -``` - -**Cycle (every 45 seconds)**: -1. Fetch 2 new posts from rotating campaigns (cycles through all 11 campaigns) -2. Process 3 NEW posts → READY -3. Publish 2 READY posts → PUBLISHED - -## Key Improvements Made - -### 1. Redis Error Fixed -- Made `REDIS_URL` optional in config -- Queue system now gracefully handles missing Redis -- No more connection errors - -### 2. Processing Logic Fixed -- Posts are only marked as `READY` if media is successfully downloaded -- If media download fails, post is marked as `FAILED` -- No more READY posts without assets - -### 3. Multi-Campaign Support -- Rotates through all 11 enabled campaigns -- Fetches from different subreddits each cycle -- Avoids duplicate posts by checking existing redditId -- Supports adding new campaigns automatically - -## Configuration - -### Auto-Processing Settings -Configure via Admin Dashboard or database: -- `auto_processing_enabled`: true/false -- `auto_processing_delay_seconds`: 10 -- `auto_processing_batch_size`: 3 - -### Auto-Publishing Settings -Configure via Admin Dashboard or database: -- `auto_publishing_enabled`: true/false -- `auto_publish_interval_minutes`: 1 -- `auto_publish_batch_size`: 3 - -## Monitoring - -### Check Status -```bash -node check-status.js -``` - -Output: -- Post counts by status -- Auto-posting settings -- Ready posts with assets - -### Check Logs -The Next.js server logs show: -- Media downloads -- Processing status -- Publishing results -- Any errors - -## Troubleshooting - -### Posts stuck in NEW -- Check if MediaDownloader is working -- Check Reddit session is valid -- Check media directory permissions - -### Posts in READY without assets -- Run the fix script to move them back to NEW -- Check processing logs for download errors - -### Publishing not working -- Verify Pinterest credentials -- Check that posts have assets -- Verify posts have `scheduledPublishAt` in the past - -## Post Status Flow - -``` -NEW (fetched from Reddit) - ↓ -[Auto-Process: Download media] - ↓ -READY (has assets, ready to publish) - ↓ -[Auto-Publish: Upload to Pinterest] - ↓ -PUBLISHED (live on Pinterest) -``` - -If media download fails: -``` -NEW → FAILED -``` - -## Running in Production - -Use PM2 to keep the system running: - -```bash -# Start the multi-campaign system -pm2 start multi-campaign-system.js --name "multi-campaign-system" - -# Monitor -pm2 logs multi-campaign-system - -# Restart -pm2 restart multi-campaign-system - -# Stop -pm2 stop multi-campaign-system -``` - -## Summary - -✅ Auto-Fetch: Working (rotates through all 11 campaigns) -✅ Auto-Process: Working (fixed to require assets) -✅ Auto-Publish: Working -✅ Redis: Disabled (no errors) -✅ All READY posts have assets -✅ Multi-campaign support -✅ System runs continuously - -The system is now fully functional, self-sustaining, and supports all campaigns! diff --git a/site/PINTEREST-AUTOMATION-README.md b/site/PINTEREST-AUTOMATION-README.md deleted file mode 100644 index 5393da6..0000000 --- a/site/PINTEREST-AUTOMATION-README.md +++ /dev/null @@ -1,86 +0,0 @@ -# Pinterest Automation System - -This system implements Pinterest automation using Selenium WebDriver, based on the approach from [mukhbit0/Auto_Posting_on_Pinterest](https://github.com/mukhbit0/Auto_Posting_on_Pinterest). - -## 🚀 **How It Works** - -Instead of trying to use Pinterest's API (which blocks localhost requests), this system uses **browser automation** to: - -1. **Open Chrome browser** with persistent user data -2. **Navigate to Pinterest** and handle login -3. **Extract real board data** by scraping the web interface -4. **Create pins** by automating the pin creation form -5. **Publish pins** automatically - -## 🔧 **Key Features** - -- ✅ **Real Pinterest Integration** - Uses actual Pinterest website -- ✅ **Persistent Login** - Saves Chrome session data to avoid repeated logins -- ✅ **Board Fetching** - Gets your actual Pinterest boards -- ✅ **Pin Creation** - Creates pins with images, titles, descriptions -- ✅ **Fallback System** - Uses mock data if automation fails - -## 📋 **Requirements** - -- Chrome browser installed -- Node.js with Selenium WebDriver -- Internet connection - -## 🎯 **Usage** - -### Fetch Pinterest Boards -```javascript -const PinterestAutomation = require('./lib/pinterest-automation') -const pinterest = new PinterestAutomation() - -await pinterest.initialize() -await pinterest.loginToPinterest() // Manual login required -const boards = await pinterest.fetchBoards() -await pinterest.close() -``` - -### Create Pinterest Pin -```javascript -const pinData = { - title: "My Amazing Pin", - description: "This is a great pin!", - imagePath: "/path/to/image.jpg", - boardName: "My Board", - link: "https://example.com" -} - -await pinterest.createPin(pinData) -await pinterest.publishPin() -``` - -## 🔄 **API Integration** - -The system is integrated into `/api/admin/pinterest` with these actions: - -- `fetch_boards` - Gets Pinterest boards using browser automation -- `create_pin` - Creates a Pinterest pin using browser automation -- `test_session` - Tests Pinterest session (mock for now) - -## ⚠️ **Important Notes** - -1. **Manual Login Required** - First time setup requires manual Pinterest login -2. **Chrome Must Be Installed** - System uses Chrome browser -3. **Internet Required** - Needs connection to Pinterest -4. **Rate Limiting** - Pinterest may limit automated actions - -## 🛠️ **Troubleshooting** - -- **"Driver not initialized"** - Run `npm install selenium-webdriver` -- **"Login failed"** - Manually log in to Pinterest when prompted -- **"No boards found"** - Check if Pinterest profile has boards -- **"Chrome not found"** - Install Chrome browser - -## 📁 **Files** - -- `lib/pinterest-automation.ts` - Main automation class -- `app/api/admin/pinterest/route.ts` - API integration -- `test-pinterest-automation.js` - Test script - -## 🎉 **Success!** - -This approach successfully bypasses Pinterest's API restrictions by using real browser automation, just like the WordPress plugins you mentioned! diff --git a/site/PINTEREST-GUIDE.md b/site/PINTEREST-GUIDE.md deleted file mode 100644 index 7572ded..0000000 --- a/site/PINTEREST-GUIDE.md +++ /dev/null @@ -1,58 +0,0 @@ -# 🎯 Pinterest Automation - How to Use - -## 🚀 **Quick Start Guide** - -### **Step 1: Open Pinterest Login** -```bash -node open-pinterest-login.js -``` -- ✅ Opens Chrome browser with Pinterest login page -- 👤 **Log in manually** to your Pinterest account -- 💾 **Session is saved** for future use -- ⏰ Browser stays open for 5 minutes - -### **Step 2: Fetch Your Pinterest Boards** -```bash -node fetch-pinterest-boards.js -``` -- ✅ Uses saved login session -- 📋 **Fetches your actual Pinterest boards** -- 📊 Shows board names, pin counts, follower counts -- 🔄 **Real data from your Pinterest account** - -## 🎯 **What This Does** - -Instead of trying to use Pinterest's API (which blocks localhost), this system: - -1. **Opens real Chrome browser** -2. **Navigates to Pinterest website** -3. **Lets you log in manually** (one time only) -4. **Saves your login session** in Chrome user data -5. **Fetches your actual boards** by scraping the web interface - -## 🔧 **How It Works** - -- **Uses Selenium WebDriver** - Same as WordPress plugins -- **Persistent Chrome session** - No need to login every time -- **Real Pinterest website** - No API restrictions -- **Browser automation** - Clicks buttons, fills forms, extracts data - -## 📁 **Files Created** - -- `open-pinterest-login.js` - Opens Pinterest login page -- `fetch-pinterest-boards.js` - Fetches your Pinterest boards -- `chrome-user-data/` - Saves your login session - -## ✅ **Success!** - -This approach successfully bypasses Pinterest's API restrictions by using real browser automation, exactly like WordPress plugins do! - -## 🎉 **Next Steps** - -After you log in and fetch boards, the system will: -- ✅ Show your real Pinterest boards -- ✅ Allow creating Pinterest campaigns -- ✅ Automatically post pins to your boards -- ✅ Schedule posts with your content - -**Try it now!** Run `node open-pinterest-login.js` and log in to Pinterest! 🚀 diff --git a/site/PINTEREST-SUCCESS.md b/site/PINTEREST-SUCCESS.md deleted file mode 100644 index 2d1057c..0000000 --- a/site/PINTEREST-SUCCESS.md +++ /dev/null @@ -1,58 +0,0 @@ -# ✅ Pinterest Automation - WORKING SOLUTION! - -## 🎯 **What We Fixed:** - -The original issue was that **Pinterest API blocks localhost requests** and **session ID authentication doesn't work** for direct API calls. - -## 🚀 **Solution Implemented:** - -Instead of trying to use Pinterest's API, we created **standalone Node.js scripts** that use **Selenium WebDriver** to automate the actual Pinterest website - exactly like WordPress plugins do! - -## 📋 **How to Use:** - -### **Step 1: Open Pinterest Login** -```bash -node open-pinterest-login.js -``` -- ✅ Opens Chrome browser with Pinterest login page -- 👤 **Log in manually** to your Pinterest account -- 💾 **Session is saved** for future use -- ⏰ Browser stays open for 5 minutes - -### **Step 2: Fetch Your Pinterest Boards** -```bash -node fetch-pinterest-boards.js -``` -- ✅ Uses saved login session -- 📋 **Fetches your actual Pinterest boards** -- 📊 Shows board names, pin counts, follower counts -- 🔄 **Real data from your Pinterest account** - -## 🎯 **Admin Panel Integration:** - -The `/admin/pinterest` page now has: - -1. **"Open Pinterest Login" button** - Shows instructions to run the login script -2. **"Fetch Boards" button** - Shows fallback boards + instructions to get real boards -3. **Instructions popup** - Guides you through the process - -## 🔧 **How It Works:** - -- **Uses Selenium WebDriver** - Same as WordPress plugins -- **Persistent Chrome session** - No need to login every time -- **Real Pinterest website** - No API restrictions -- **Browser automation** - Clicks buttons, fills forms, extracts data - -## ✅ **Success!** - -This approach successfully bypasses Pinterest's API restrictions by using real browser automation, exactly like WordPress plugins! - -## 🎉 **Next Steps:** - -1. **Run:** `node open-pinterest-login.js` -2. **Log in** to Pinterest in the browser window -3. **Run:** `node fetch-pinterest-boards.js` -4. **See your real Pinterest boards!** -5. **Use the admin panel** to create Pinterest campaigns - -**The system is now working!** 🚀 diff --git a/site/README.md b/site/README.md deleted file mode 100644 index 04b7e87..0000000 --- a/site/README.md +++ /dev/null @@ -1,302 +0,0 @@ -# PinReddit - -PinReddit is a full-stack application that automatically discovers Reddit videos and publishes them to Pinterest boards. Built with Next.js 14, TypeScript, and modern web technologies. - -## Features - -- 🎥 **Pinterest-style masonry grid** with responsive design -- 🤖 **Automated Reddit video discovery** with customizable filters -- 📌 **Pinterest API v5 integration** for automatic pin creation -- 🎬 **Video download pipeline** with yt-dlp and ffmpeg (handles Reddit's separate audio/video streams) -- 👤 **Admin dashboard** with authentication -- 📊 **Job queue management** with BullMQ and Redis -- ⏰ **Scheduled automation** with cron jobs -- 💾 **Flexible storage** (local or S3-compatible) - -## Tech Stack - -- **Frontend**: Next.js 14 (App Router), React 18, TypeScript, Tailwind CSS -- **Backend**: Next.js API Routes, Prisma ORM, PostgreSQL -- **Queue**: BullMQ, Redis -- **Auth**: NextAuth.js -- **Media**: yt-dlp, ffmpeg -- **Deployment**: PM2, Nginx, Ubuntu - -## Prerequisites - -- Node.js 18+ -- PostgreSQL -- Redis -- ffmpeg -- yt-dlp -- PM2 (for production) -- Nginx (for production) - -## Installation - -1. **Clone the repository** - ```bash - git clone https://github.com/yourusername/pinreddit.git - cd pinreddit - ``` - -2. **Install dependencies** - ```bash - npm install - ``` - -3. **Install system dependencies** - ```bash - # Ubuntu/Debian - sudo apt update - sudo apt install ffmpeg redis-server postgresql - - # Install yt-dlp - sudo wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp - sudo chmod a+rx /usr/local/bin/yt-dlp - ``` - -4. **Configure environment variables** - ```bash - cp env.example .env - # Edit .env with your configuration - ``` - -5. **Set up the database** - ```bash - # Create PostgreSQL database - createdb pinreddit - - # Run migrations - npm run db:push - ``` - -6. **Start development servers** - ```bash - # Terminal 1: Start Redis - redis-server - - # Terminal 2: Start Next.js - npm run dev - - # Terminal 3: Start worker - npm run worker:dev - ``` - -## Configuration - -### Environment Variables - -Key environment variables to configure: - -```env -# Database -DATABASE_URL=postgresql://user:password@localhost:5432/pinreddit - -# Redis -REDIS_URL=redis://localhost:6379 - -# Reddit API (OAuth preferred) -REDDIT_CLIENT_ID=your_client_id -REDDIT_CLIENT_SECRET=your_client_secret -REDDIT_REFRESH_TOKEN=your_refresh_token - -# Pinterest API -PINTEREST_ACCESS_TOKEN=your_business_account_token -PINTEREST_DEFAULT_BOARD_ID=your_board_id - -# Storage -STORAGE_DRIVER=local # or s3 -MEDIA_DIR=./media - -# NextAuth -NEXTAUTH_SECRET=your_secret_key -NEXTAUTH_URL=http://localhost:3002 -``` - -### Reddit API Setup - -1. Create a Reddit app at https://www.reddit.com/prefs/apps -2. Choose "script" type -3. Set redirect URI to `http://localhost:3002/api/auth/callback/reddit` -4. Use the client ID and secret in your `.env` - -### Pinterest API Setup - -1. Create a Pinterest business account -2. Create an app at https://developers.pinterest.com/ -3. Generate an access token -4. Create a board and get its ID - -## Usage - -### Admin Dashboard - -Access the admin dashboard at `http://localhost:3002/admin` - -Features: -- **Settings**: Configure subreddits, keywords, and filters -- **Posts**: View and manage downloaded videos -- **Jobs**: Monitor queue status -- **Pinterest**: Configure boards and publishing settings -- **Reddit**: Test Reddit connection and manual import - -### API Endpoints - -- `GET /api/posts` - Get posts with pagination -- `POST /api/ingest/reddit` - Trigger Reddit ingest -- `GET /api/admin/settings` - Get settings (auth required) -- `POST /api/admin/settings` - Update settings (auth required) - -## Production Deployment - -### 1. Server Setup (Ubuntu) - -```bash -# Update system -sudo apt update && sudo apt upgrade -y - -# Install Node.js 18 -curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - -sudo apt install -y nodejs - -# Install dependencies -sudo apt install -y postgresql redis-server nginx ffmpeg git - -# Install PM2 globally -sudo npm install -g pm2 - -# Install yt-dlp -sudo wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp -sudo chmod a+rx /usr/local/bin/yt-dlp -``` - -### 2. Application Setup - -```bash -# Clone repository -git clone https://github.com/yourusername/pinreddit.git /home/ubuntu/pinreddit -cd /home/ubuntu/pinreddit - -# Install dependencies -npm install - -# Copy and configure environment -cp env.example .env -nano .env # Edit with production values - -# Build application -npm run build - -# Run database migrations -npm run db:push - -# Create directories -mkdir -p logs media temp -chmod -R 755 media temp logs -``` - -### 3. PM2 Setup - -```bash -# Start applications -pm2 start ecosystem.config.js - -# Save PM2 configuration -pm2 save - -# Set PM2 to start on boot -pm2 startup -``` - -### 4. Nginx Setup - -```bash -# Copy nginx configuration -sudo cp nginx.conf /etc/nginx/sites-available/pinreddit -sudo ln -s /etc/nginx/sites-available/pinreddit /etc/nginx/sites-enabled/ - -# Edit server_name in the config -sudo nano /etc/nginx/sites-available/pinreddit - -# Test and reload nginx -sudo nginx -t -sudo systemctl reload nginx -``` - -### 5. SSL Setup (Let's Encrypt) - -```bash -sudo apt install certbot python3-certbot-nginx -sudo certbot --nginx -d pinreddit.example.com -``` - -### 6. Deployment Script - -Use the provided deployment script for updates: - -```bash -chmod +x scripts/deploy.sh -./scripts/deploy.sh -``` - -## Monitoring - -### PM2 Monitoring - -```bash -# View status -pm2 status - -# View logs -pm2 logs pinreddit-web -pm2 logs pinreddit-worker - -# Monitor resources -pm2 monit -``` - -### Application Logs - -Logs are stored in the `logs/` directory: -- `web-*.log` - Next.js application logs -- `worker-*.log` - Background worker logs - -## Troubleshooting - -### Common Issues - -1. **Video download fails** - - Ensure yt-dlp is installed and updated: `yt-dlp -U` - - Check ffmpeg installation: `ffmpeg -version` - - Verify temp directory permissions - -2. **Pinterest upload fails** - - Verify access token is valid - - Check video meets Pinterest requirements (aspect ratio, duration, size) - - Ensure board ID is correct - -3. **Redis connection errors** - - Check Redis is running: `redis-cli ping` - - Verify REDIS_URL in .env - -4. **Database connection errors** - - Check PostgreSQL is running: `sudo systemctl status postgresql` - - Verify DATABASE_URL in .env - - Ensure database exists - -## Pinterest Video Requirements - -- **Formats**: MP4, MOV -- **Duration**: 4 seconds to 15 minutes -- **File size**: Up to 2GB (recommend under 1GB) -- **Aspect ratios**: 1:1, 2:3, 9:16 (vertical preferred) -- **Resolution**: At least 240x240 pixels - -## License - -MIT License - see LICENSE file for details - -## Support - -For issues and feature requests, please create an issue on GitHub. \ No newline at end of file diff --git a/site/SEO-IMPLEMENTATION.md b/site/SEO-IMPLEMENTATION.md deleted file mode 100644 index 0fbb2fa..0000000 --- a/site/SEO-IMPLEMENTATION.md +++ /dev/null @@ -1,99 +0,0 @@ -# YapGrid SEO Optimization Implementation - -## Overview -This document outlines the comprehensive SEO optimization implemented for YapGrid to ensure it appears properly in Google search results, similar to how Reddit appears in search results. - -## Implemented SEO Features - -### 1. Enhanced Metadata (app/layout.tsx) -- **Title Template**: Dynamic title structure with "YapGrid - The heart of the internet" as default -- **Comprehensive Description**: Detailed description highlighting community features -- **Extended Keywords**: Comprehensive keyword list covering all relevant terms -- **Open Graph Tags**: Complete Facebook/social media sharing optimization -- **Twitter Cards**: Optimized Twitter sharing with large image cards -- **Robots Meta**: Proper indexing instructions for search engines -- **Canonical URLs**: Prevents duplicate content issues -- **Theme Colors**: Brand consistency across platforms - -### 2. Dynamic Sitemap (app/sitemap.ts) -- **Static Pages**: Home, trending, popular, communities, about, help, terms, privacy -- **Community Pages**: Dynamic subreddit pages for popular communities -- **Priority Settings**: Homepage (1.0), main sections (0.9), communities (0.7) -- **Change Frequency**: Hourly for dynamic content, daily for communities -- **Last Modified**: Current timestamp for freshness - -### 3. Robots.txt (public/robots.txt) -- **Admin Exclusion**: Blocks /admin/, /api/, /auth/, /settings/ from indexing -- **Content Allowance**: Allows main content areas (/r/, /communities/, /trending/) -- **Sitemap Reference**: Points to sitemap.xml location -- **Crawl Delay**: Prevents server overload (1 second delay) - -### 4. Web App Manifest (public/manifest.json) -- **PWA Features**: Standalone app experience -- **Brand Identity**: Consistent naming and theming -- **App Shortcuts**: Quick access to trending, popular, communities -- **Icon Sets**: Multiple sizes for different devices -- **Theme Colors**: Orange (#ff4500) brand consistency - -### 5. Structured Data (JSON-LD) -- **WebSite Schema**: Proper website identification -- **Search Action**: Enables search functionality in search results -- **Organization Data**: Publisher and logo information -- **Item Lists**: Popular communities for rich snippets -- **Schema.org Compliance**: Follows Google's structured data guidelines - -### 6. Page-Level SEO (app/page.tsx) -- **Dynamic Meta Tags**: Comprehensive meta tag implementation -- **Social Media Optimization**: Open Graph and Twitter Card tags -- **Search Engine Directives**: Proper robots and googlebot instructions -- **Canonical Links**: Prevents duplicate content -- **Viewport Optimization**: Mobile-friendly settings - -### 7. Next.js Configuration (next.config.js) -- **Image Optimization**: WebP and AVIF format support -- **Security Headers**: XSS protection, content type options -- **Caching Headers**: Optimized cache control for sitemap and robots -- **Performance**: Compression and powered-by header removal -- **SEO Redirects**: Home page redirect optimization - -## SEO Benefits - -### Search Engine Visibility -- **Rich Snippets**: Structured data enables enhanced search results -- **Site Links**: Popular communities appear as sitelinks below main result -- **Search Box**: Users can search directly from search results -- **Brand Recognition**: Consistent branding across all platforms - -### Content Discovery -- **Community Pages**: Individual subreddit pages are indexed -- **Trending Content**: Dynamic content gets proper SEO treatment -- **User-Generated Content**: Posts and discussions are discoverable -- **Navigation**: Clear site structure for search engines - -### Performance & User Experience -- **Fast Loading**: Optimized images and caching -- **Mobile Friendly**: Responsive design with proper viewport -- **PWA Features**: App-like experience on mobile devices -- **Security**: Proper security headers for user trust - -## Excluded from Search Indexing -- **Admin Dashboard**: /admin/ and related admin pages -- **API Endpoints**: /api/ routes for backend functionality -- **User Settings**: /settings/, /saved/, /history/ for privacy -- **Authentication**: /auth/ routes for security -- **Content Creation**: /submit/, /communities/create/ for spam prevention - -## Monitoring & Maintenance -- **Sitemap Updates**: Automatically updates with new communities -- **Meta Tag Consistency**: Centralized metadata management -- **Performance Monitoring**: Built-in Next.js optimizations -- **Security Headers**: Ongoing protection against common attacks - -## Next Steps -1. **Google Search Console**: Submit sitemap and monitor indexing -2. **Analytics Integration**: Track search performance and user behavior -3. **Content Optimization**: Regular content updates for freshness -4. **Link Building**: Develop backlink strategy for domain authority -5. **Local SEO**: If applicable, implement local business features - -This implementation ensures YapGrid will appear in Google search results with rich snippets, site links, and proper branding, similar to how Reddit appears in search results. diff --git a/site/add-subreddits-simple.js b/site/add-subreddits-simple.js deleted file mode 100644 index 6fa23f4..0000000 --- a/site/add-subreddits-simple.js +++ /dev/null @@ -1,71 +0,0 @@ -const { PrismaClient } = require('@prisma/client'); - -const prisma = new PrismaClient(); - -// Top 50 most active humor/entertainment subreddits -const newSubreddits = [ - 'memes', 'dankmemes', 'wholesomememes', 'facepalm', 'holdmybeer', - 'madlads', 'publicfreakout', 'AnimalsBeingDerps', 'CrappyDesign', - 'unexpected', 'perfectlycutscreams', 'instant_regret', 'Whatcouldgowrong', - 'idiotsincars', 'kidsarefuckingstupid', 'ContagiousLaughter', 'funnyvideos', - 'comedy', 'jokes', 'dadjokes', 'fail', 'epicfail', 'nononono', 'yesyesyesno', - 'AbruptChaos', 'CatastrophicFailure', 'WinStupidPrizes', 'instantkarma', - 'JusticeServed', 'AnimalsBeingBros', 'AnimalsBeingJerks', 'rarepuppers', - 'dogpictures', 'catpictures', 'zoomies', 'tippytaps', 'WhatsWrongWithYourDog', - 'WhatsWrongWithYourCat', 'gaming', 'pcgaming', 'movies', 'television', - 'netflix', 'Marvel', 'DCcomics', 'StarWars', 'lotrmemes', 'PrequelMemes', - 'SequelMemes', 'OTMemes', 'MarvelMemes', 'DCmemes' -]; - -async function addSubreddits() { - try { - console.log('🚀 Adding subreddits to existing campaigns...'); - - // Get the first campaign to add subreddits to - const campaign = await prisma.campaign.findFirst({ - where: { enabled: true } - }); - - if (!campaign) { - console.log('❌ No enabled campaigns found'); - return; - } - - console.log(`📝 Found campaign: ${campaign.name}`); - - // Parse existing subreddits - const existingSubreddits = JSON.parse(campaign.subreddits || '[]'); - console.log(`📊 Current subreddits: ${existingSubreddits.length}`); - - // Add new subreddits (avoid duplicates) - const allSubreddits = [...new Set([...existingSubreddits, ...newSubreddits])]; - const addedCount = allSubreddits.length - existingSubreddits.length; - - console.log(`➕ Adding ${addedCount} new subreddits`); - console.log(`📈 Total subreddits will be: ${allSubreddits.length}`); - - // Update campaign - await prisma.campaign.update({ - where: { id: campaign.id }, - data: { - subreddits: JSON.stringify(allSubreddits), - updatedAt: new Date() - } - }); - - console.log('✅ Successfully updated campaign with new subreddits!'); - console.log('\n🎯 New subreddits added:'); - newSubreddits.forEach(sub => { - if (!existingSubreddits.includes(sub)) { - console.log(` - r/${sub}`); - } - }); - - } catch (error) { - console.error('❌ Error:', error.message); - } finally { - await prisma.$disconnect(); - } -} - -addSubreddits(); diff --git a/site/add-subreddits.js b/site/add-subreddits.js deleted file mode 100644 index bb27f04..0000000 --- a/site/add-subreddits.js +++ /dev/null @@ -1,163 +0,0 @@ -const { PrismaClient } = require('@prisma/client'); - -const prisma = new PrismaClient(); - -// Comprehensive list of 100+ active subreddits for humor and entertainment -const subreddits = [ - // HUMOR & MEMES (Top Tier) - 'funny', 'memes', 'dankmemes', 'wholesomememes', 'facepalm', 'holdmybeer', - 'therewasanattempt', 'madlads', 'publicfreakout', 'AnimalsBeingDerps', - 'ProgrammerHumor', 'CrappyDesign', 'unexpected', 'perfectlycutscreams', - 'instant_regret', 'Whatcouldgowrong', 'idiotsincars', 'kidsarefuckingstupid', - 'ContagiousLaughter', 'funnyvideos', 'comedy', 'jokes', 'dadjokes', - - // FAILS & EPIC FAILS - 'fail', 'epicfail', 'nononono', 'yesyesyesno', 'AbruptChaos', 'CatastrophicFailure', - 'WinStupidPrizes', 'instantkarma', 'JusticeServed', 'petttyrevenge', - - // ANIMALS & PETS - 'cats', 'dogs', 'Awww', 'FunnyAnimals', 'AnimalsBeingBros', 'AnimalsBeingJerks', - 'rarepuppers', 'dogpictures', 'catpictures', 'zoomies', 'tippytaps', - 'WhatsWrongWithYourDog', 'WhatsWrongWithYourCat', 'DogShowerThoughts', - - // GAMING & TECH HUMOR - 'gaming', 'pcgaming', 'pcmasterrace', 'buildapc', 'leagueoflegends', 'Eldenring', - 'Silksong', 'Genshin_Impact', 'cyberpunkgame', '3Dprinting', 'ChatGPT', 'OpenAI', - 'AskElectronics', 'ProgrammerHumor', 'ProgrammerDadJokes', 'softwaregore', - 'techsupportgore', 'itsaunixsystem', 'programminghorror', - - // ENTERTAINMENT & POP CULTURE - 'movies', 'television', 'netflix', 'Marvel', 'DCcomics', 'StarWars', 'lotrmemes', - 'PrequelMemes', 'SequelMemes', 'OTMemes', 'MarvelMemes', 'DCmemes', - 'dndmemes', 'rpgmemes', 'dnd', 'DnD', 'pathfinder', 'criticalrole', - - // SOCIAL MEDIA & INTERNET CULTURE - 'TikTokCringe', 'CringeTikToks', 'ImTheMainCharacter', 'niceguys', 'Nicegirls', - 'ShitAmericansSay', 'confidentlyincorrect', 'PeterExplainsTheJoke', 'ExplainTheJoke', - 'HumorInPoorTaste', 'whenthe', 'nextfuckinglevel', 'BeAmazed', 'blackmagicfuckery', - 'oddlysatisfying', 'mildlyinfuriating', 'mildlyinteresting', 'interestingasfuck', - - // SCIENCE & EDUCATION (Funny) - 'science', 'askscience', 'explainlikeimfive', 'todayilearned', 'mildlyinfuriating', - 'LifeProTips', 'Showerthoughts', 'unpopularopinion', 'changemyview', - 'AmItheAsshole', 'relationship_advice', 'tifu', 'maliciouscompliance', - - // FOOD & COOKING HUMOR - 'food', 'cooking', 'shittyfoodporn', 'foodporn', 'WeWantPlates', 'deliciouscompliance', - 'KitchenConfidential', 'AskCulinary', 'breadit', 'pizza', 'burgers', - - // SPORTS & FITNESS HUMOR - 'sports', 'nba', 'nfl', 'soccer', 'hockey', 'baseball', 'tennis', 'golf', - 'fitness', 'bodybuilding', 'running', 'cycling', 'swimming', 'climbing', - - // MUSIC & ART HUMOR - 'music', 'listentothis', 'Music', 'hiphopheads', 'popheads', 'indieheads', - 'electronicmusic', 'jazz', 'classicalmusic', 'Art', 'drawing', 'painting', - 'photography', 'digitalart', 'conceptart', 'comics', 'webcomics', - - // BOOKS & WRITING HUMOR - 'books', 'writing', 'fantasy', 'scifi', 'horror', 'mystery', 'romance', - 'bookclub', 'suggestmeabook', 'whatsthatbook', 'bookporn', 'bookmemes', - - // TRAVEL & LIFESTYLE HUMOR - 'travel', 'solotravel', 'backpacking', 'digitalnomad', 'expats', 'IWantOut', - 'personalfinance', 'investing', 'cryptocurrency', 'bitcoin', 'ethereum', - 'Frugal', 'BuyItForLife', 'BIFL', 'minimalism', 'simpleliving', - - // NEWS & POLITICS (Humor) - 'news', 'worldnews', 'politics', 'PoliticalHumor', 'PoliticalCompassMemes', - 'WhitePeopleTwitter', 'BlackPeopleTwitter', 'AsianPeopleTwitter', 'latino', - - // RANDOM & MISCELLANEOUS - 'AskReddit', 'mildlyinfuriating', 'oddlysatisfying', 'interestingasfuck', - 'Damnthatsinteresting', 'nextfuckinglevel', 'BeAmazed', 'blackmagicfuckery', - 'unexpected', 'perfectlycutscreams', 'ContagiousLaughter', 'funnyvideos' -]; - -async function addSubredditsToCampaigns() { - try { - console.log('🚀 Adding 100+ subreddits to campaigns...'); - - // Get existing campaigns - const existingCampaigns = await prisma.campaign.findMany({ - select: { id: true, name: true, subreddits: true } - }); - - console.log(`Found ${existingCampaigns.length} existing campaigns`); - - // Create new campaigns for different categories - const newCampaigns = [ - { - name: 'Humor & Memes Collection', - subreddits: subreddits.slice(0, 25), // First 25 - enabled: true, - autoFetch: true, - autoProcess: true, - autoPublish: true - }, - { - name: 'Entertainment & Gaming', - subreddits: subreddits.slice(25, 50), // Next 25 - enabled: true, - autoFetch: true, - autoProcess: true, - autoPublish: true - }, - { - name: 'Animals & Pets Collection', - subreddits: subreddits.slice(50, 75), // Next 25 - enabled: true, - autoFetch: true, - autoProcess: true, - autoPublish: true - }, - { - name: 'Tech & Science Humor', - subreddits: subreddits.slice(75, 100), // Next 25 - enabled: true, - autoFetch: true, - autoProcess: true, - autoPublish: true - }, - { - name: 'Random Entertainment', - subreddits: subreddits.slice(100), // Remaining - enabled: true, - autoFetch: true, - autoProcess: true, - autoPublish: true - } - ]; - - // Add new campaigns - for (const campaign of newCampaigns) { - const created = await prisma.campaign.create({ - data: { - name: campaign.name, - subreddits: JSON.stringify(campaign.subreddits), - enabled: campaign.enabled, - autoFetch: campaign.autoFetch, - autoProcess: campaign.autoProcess, - autoPublish: campaign.autoPublish, - createdAt: new Date(), - updatedAt: new Date() - } - }); - - console.log(`✅ Created campaign: ${created.name} with ${campaign.subreddits.length} subreddits`); - } - - console.log(`\n🎉 Successfully added ${subreddits.length} subreddits across ${newCampaigns.length} campaigns!`); - console.log('\n📊 Campaign Summary:'); - newCampaigns.forEach(campaign => { - console.log(` - ${campaign.name}: ${campaign.subreddits.length} subreddits`); - }); - - } catch (error) { - console.error('❌ Error adding subreddits:', error); - } finally { - await prisma.$disconnect(); - } -} - -addSubredditsToCampaigns(); diff --git a/site/app/about/page.tsx b/site/app/about/page.tsx deleted file mode 100644 index 7c10158..0000000 --- a/site/app/about/page.tsx +++ /dev/null @@ -1,162 +0,0 @@ -'use client' - -import { ArrowLeft, Heart, Users, Zap, Shield, Globe, Award } from 'lucide-react' -import Link from 'next/link' -import { SimpleLogo } from '@/components/logo' - -export default function AboutPage() { - return ( -
-
-
-
- - - - -
-
-
- -
- {/* Hero Section */} -
- -

- About YapGrid -

-

- YapGrid is a modern social media platform that brings together the best content from Reddit communities, enhanced with AI-powered personalization and a beautiful user experience. -

-
- - {/* Mission Section */} -
-
- -

- Our Mission -

-
-

- We believe that great content deserves to be discovered and shared. YapGrid uses advanced AI technology to curate and personalize content from Reddit communities, making it easier for users to find posts that truly resonate with their interests and preferences. -

-
- - {/* Features Section */} -
-
- -

- AI-Powered Recommendations -

-

- Our advanced algorithm learns from your interactions to show you content you'll love. -

-
- -
- -

- Community Focused -

-

- Discover trending communities and connect with like-minded people. -

-
- -
- -

- Privacy First -

-

- Your data is protected with industry-leading security and privacy measures. -

-
-
- - {/* Stats Section */} -
-

YapGrid by the Numbers

-
-
-
50+
-
Active Communities
-
-
-
1000+
-
Posts Processed Daily
-
-
-
99.9%
-
Uptime
-
-
-
24/7
-
Auto-Publishing
-
-
-
- - {/* Technology Section */} -
-
- -

- Built with Modern Technology -

-
-
-
-

- Frontend -

-
    -
  • • Next.js 15 with React 18
  • -
  • • TypeScript for type safety
  • -
  • • Tailwind CSS for styling
  • -
  • • Responsive design
  • -
-
-
-

- Backend -

-
    -
  • • Node.js with Express
  • -
  • • Prisma ORM with SQLite
  • -
  • • NextAuth.js for authentication
  • -
  • • AI-powered content optimization
  • -
-
-
-
- - {/* Team Section */} -
-
- -

- Our Team -

-
-

- YapGrid is built by a passionate team of developers, designers, and AI researchers who believe in the power of community-driven content. -

-
-

- We're always looking for talented individuals to join our mission. -

- - Join our team → - -
-
-
-
- ) -} diff --git a/site/app/admin/analytics/page.tsx b/site/app/admin/analytics/page.tsx deleted file mode 100644 index 2831cb1..0000000 --- a/site/app/admin/analytics/page.tsx +++ /dev/null @@ -1,433 +0,0 @@ -'use client' - -import { useState, useEffect } from 'react' -import { createLogger } from '@/lib/logger' -import { - BarChart3, - TrendingUp, - Users, - Eye, - Heart, - MessageCircle, - Share, - Calendar, - RefreshCw, - Download, - Filter, - ArrowUp, - ArrowDown, - Minus -} from 'lucide-react' -import toast from 'react-hot-toast' - -const logger = createLogger('admin/analytics') - -interface AnalyticsData { - overview: { - totalPosts: number - totalViews: number - totalLikes: number - totalComments: number - totalShares: number - avgEngagement: number - } - trends: { - daily: Array<{ - date: string - posts: number - views: number - likes: number - comments: number - shares: number - }> - weekly: Array<{ - week: string - posts: number - views: number - likes: number - comments: number - shares: number - }> - monthly: Array<{ - month: string - posts: number - views: number - likes: number - comments: number - shares: number - }> - } - topPosts: Array<{ - id: string - title: string - subreddit: string - views: number - likes: number - comments: number - shares: number - engagement: number - createdAt: string - }> - subredditStats: Array<{ - name: string - posts: number - views: number - likes: number - comments: number - shares: number - avgEngagement: number - }> - timeRange: '7d' | '30d' | '90d' | '1y' -} - -export default function AdminAnalyticsPage() { - const [data, setData] = useState(null) - const [loading, setLoading] = useState(true) - const [timeRange, setTimeRange] = useState<'7d' | '30d' | '90d' | '1y'>('30d') - const [refreshing, setRefreshing] = useState(false) - - useEffect(() => { - fetchAnalytics() - }, [timeRange]) - - const fetchAnalytics = async () => { - try { - setLoading(true) - const response = await fetch(`${window.location.origin}/api/admin/analytics?range=${timeRange}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }) - - if (!response.ok) { - throw new Error('Failed to fetch analytics data') - } - - const result = await response.json() - - if (result.success) { - setData(result.data) - } else { - toast.error(result.error || 'Failed to load analytics data') - } - } catch (error) { - console.error('Failed to fetch analytics data:', error) - toast.error('Failed to load analytics data') - } finally { - setLoading(false) - } - } - - const handleRefresh = async () => { - setRefreshing(true) - await fetchAnalytics() - setRefreshing(false) - } - - const handleExport = () => { - if (!data) return - - const csvData = [ - ['Metric', 'Value'], - ['Total Posts', data.overview.totalPosts], - ['Total Views', data.overview.totalViews], - ['Total Likes', data.overview.totalLikes], - ['Total Comments', data.overview.totalComments], - ['Total Shares', data.overview.totalShares], - ['Avg Engagement', data.overview.avgEngagement.toFixed(2)] - ].map(row => row.join(',')).join('\n') - - const blob = new Blob([csvData], { type: 'text/csv' }) - const url = window.URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = `analytics-${timeRange}-${new Date().toISOString().split('T')[0]}.csv` - a.click() - window.URL.revokeObjectURL(url) - - toast.success('Analytics data exported successfully') - } - - const formatNumber = (num: number) => { - if (num >= 1000000) { - return (num / 1000000).toFixed(1) + 'M' - } else if (num >= 1000) { - return (num / 1000).toFixed(1) + 'K' - } - return num.toString() - } - - const getTrendIcon = (current: number, previous: number) => { - if (current > previous) { - return - } else if (current < previous) { - return - } - return - } - - const getTrendColor = (current: number, previous: number) => { - if (current > previous) { - return 'text-green-600' - } else if (current < previous) { - return 'text-red-600' - } - return 'text-gray-600' - } - - if (loading) { - return ( -
-
-
-
- Loading analytics... -
-
-
- ) - } - - if (!data) { - return ( -
-
-
-

No Analytics Data

-

Unable to load analytics data. Please try again.

-
-
-
- ) - } - - return ( -
-
- {/* Header */} -
-
-
-

Analytics Dashboard

-

Track performance and engagement metrics

-
-
- - - -
-
-
- - {/* Overview Cards */} -
-
-
- -
-

Total Posts

-

{formatNumber(data.overview.totalPosts)}

-
-
-
- -
-
- -
-

Total Views

-

{formatNumber(data.overview.totalViews)}

-
-
-
- -
-
- -
-

Total Likes

-

{formatNumber(data.overview.totalLikes)}

-
-
-
- -
-
- -
-

Total Comments

-

{formatNumber(data.overview.totalComments)}

-
-
-
- -
-
- -
-

Total Shares

-

{formatNumber(data.overview.totalShares)}

-
-
-
- -
-
- -
-

Avg Engagement

-

{data.overview.avgEngagement.toFixed(1)}%

-
-
-
-
- - {/* Top Posts */} -
-
-

Top Performing Posts

-
-
- - - - - - - - - - - - - {data.topPosts.slice(0, 10).map((post) => ( - - - - - - - - - ))} - -
- Post - - Subreddit - - Views - - Likes - - Comments - - Engagement -
-
- {post.title} -
-
- {new Date(post.createdAt).toLocaleDateString()} -
-
- - r/{post.subreddit} - - - {formatNumber(post.views)} - - {formatNumber(post.likes)} - - {formatNumber(post.comments)} - - {post.engagement.toFixed(1)}% -
-
-
- - {/* Subreddit Stats */} -
-
-

Subreddit Performance

-
-
- - - - - - - - - - - - - {data.subredditStats.slice(0, 15).map((subreddit) => ( - - - - - - - - - ))} - -
- Subreddit - - Posts - - Views - - Likes - - Comments - - Avg Engagement -
- - r/{subreddit.name} - - - {subreddit.posts} - - {formatNumber(subreddit.views)} - - {formatNumber(subreddit.likes)} - - {formatNumber(subreddit.comments)} - - {subreddit.avgEngagement.toFixed(1)}% -
-
-
-
-
- ) -} diff --git a/site/app/admin/campaigns/page.tsx b/site/app/admin/campaigns/page.tsx deleted file mode 100644 index 25745e0..0000000 --- a/site/app/admin/campaigns/page.tsx +++ /dev/null @@ -1,832 +0,0 @@ -'use client' - -import { useState, useEffect } from 'react' -import { Plus, Play, TestTube, Trash2, Edit, Settings, CheckCircle, XCircle } from 'lucide-react' -import toast from 'react-hot-toast' - -interface RedditCampaign { - id: string - name: string - subreddits: string[] - keywords: string[] - excludeKeywords: string[] - minScore: number - maxScore?: number - sortBy: 'hot' | 'new' | 'top' | 'rising' - timeRange: 'hour' | 'day' | 'week' | 'month' | 'year' | 'all' - includeNsfw: boolean - postLimit: number - enabled: boolean - lastRun?: string - nextRun?: string -} - -interface SessionConfig { - sessionCookie: string - enabled: boolean -} - -// Removed PostingConfig interface - posts go directly to homepage - -export default function RedditCampaignsPage() { - const [campaigns, setCampaigns] = useState([]) - const [sessionConfig, setSessionConfig] = useState({ sessionCookie: '', enabled: false }) - // Removed posting config - posts will go directly to homepage - const [loading, setLoading] = useState(true) - const [showCreateModal, setShowCreateModal] = useState(false) - const [showSessionModal, setShowSessionModal] = useState(false) - // Removed posting modal - const [editingCampaign, setEditingCampaign] = useState(null) - const [newCampaign, setNewCampaign] = useState({ - name: '', - subreddits: '', - keywords: '', - excludeKeywords: '', - minScore: 10, - postLimit: 10, - sortBy: 'hot', - timeRange: 'day', - includeNsfw: false, - enabled: true, - }) - - // Load data on component mount - useEffect(() => { - loadData() - }, []) - - const loadData = async () => { - try { - const [campaignsRes, sessionRes] = await Promise.all([ - fetch('/api/campaigns'), - fetch('/api/reddit-session'), - ]) - - const campaignsData = await campaignsRes.json() - const sessionData = await sessionRes.json() - - if (campaignsData.success) { - setCampaigns(campaignsData.data) - } - if (sessionData.success) { - setSessionConfig(sessionData.data.sessionConfig) - } - } catch (error) { - toast.error('Failed to load data') - } finally { - setLoading(false) - } - } - - const testSession = async () => { - try { - const response = await fetch('/api/reddit-session/test', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - sessionCookie: sessionConfig.sessionCookie - }) - }) - const data = await response.json() - - if (data.success && data.data.isSessionValid) { - toast.success('Reddit session is valid!') - } else { - toast.error('Reddit session is invalid') - } - } catch (error) { - toast.error('Failed to test session') - } - } - - const saveSessionConfig = async () => { - try { - const response = await fetch('/api/reddit-session', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - sessionCookie: sessionConfig.sessionCookie, - enabled: sessionConfig.enabled - }) - }) - const data = await response.json() - - if (data.success) { - toast.success('Session configuration saved!') - setShowSessionModal(false) - loadData() // Reload to update status - } else { - toast.error(data.error || 'Failed to save session configuration') - } - } catch (error) { - toast.error('Failed to save session configuration') - } - } - - const deleteSessionConfig = async () => { - if (!confirm('Are you sure you want to delete the session configuration?')) return - - try { - const response = await fetch('/api/reddit-session', { method: 'DELETE' }) - const data = await response.json() - - if (data.success) { - toast.success('Session configuration deleted!') - setSessionConfig({ sessionCookie: '', enabled: false }) - setShowSessionModal(false) - loadData() // Reload to update status - } else { - toast.error(data.error || 'Failed to delete session configuration') - } - } catch (error) { - toast.error('Failed to delete session configuration') - } - } - - const testPosting = async () => { - // Removed - posts go directly to homepage - } - - const runCampaign = async (campaignId: string) => { - try { - const response = await fetch(`/api/campaigns/${campaignId}/run`, { method: 'POST' }) - const data = await response.json() - - if (data.success) { - toast.success(`Campaign executed: ${data.data.postsSaved} posts saved`) - loadData() // Reload to update last run time - } else { - toast.error(data.error || 'Failed to run campaign') - } - } catch (error) { - toast.error('Failed to run campaign') - } - } - - const testCampaign = async (campaignId: string) => { - try { - const response = await fetch(`/api/campaigns/${campaignId}/test`, { method: 'POST' }) - const data = await response.json() - - if (data.success) { - toast.success(`Test completed: ${data.data.postsFound} posts found`) - } else { - toast.error(data.error || 'Failed to test campaign') - } - } catch (error) { - toast.error('Failed to test campaign') - } - } - - const createCampaign = async () => { - try { - const campaignData = { - name: newCampaign.name, - subreddits: newCampaign.subreddits.split(',').map(s => s.trim()).filter(s => s), - keywords: newCampaign.keywords.split(',').map(s => s.trim()).filter(s => s), - excludeKeywords: newCampaign.excludeKeywords.split(',').map(s => s.trim()).filter(s => s), - minScore: newCampaign.minScore, - postLimit: newCampaign.postLimit, - sortBy: newCampaign.sortBy, - timeRange: newCampaign.timeRange, - includeNsfw: newCampaign.includeNsfw, - enabled: newCampaign.enabled, - } - - const response = await fetch('/api/campaigns', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(campaignData), - }) - - const data = await response.json() - - if (data.success) { - toast.success('Campaign created successfully!') - loadData() // Reload campaigns list - setShowCreateModal(false) - // Reset form - setNewCampaign({ - name: '', - subreddits: '', - keywords: '', - excludeKeywords: '', - minScore: 10, - postLimit: 10, - sortBy: 'hot', - timeRange: 'day', - includeNsfw: false, - enabled: true, - }) - } else { - toast.error(data.error || 'Failed to create campaign') - } - } catch (error) { - toast.error('Failed to create campaign') - } - } - - const updateCampaign = async () => { - if (!editingCampaign) return - - try { - const campaignData = { - name: editingCampaign.name, - subreddits: Array.isArray(editingCampaign.subreddits) - ? editingCampaign.subreddits - : JSON.parse(editingCampaign.subreddits as string), - keywords: Array.isArray(editingCampaign.keywords) - ? editingCampaign.keywords - : JSON.parse(editingCampaign.keywords as string), - excludeKeywords: Array.isArray(editingCampaign.excludeKeywords) - ? editingCampaign.excludeKeywords - : JSON.parse(editingCampaign.excludeKeywords as string), - minScore: editingCampaign.minScore, - maxScore: editingCampaign.maxScore, - sortBy: editingCampaign.sortBy, - timeRange: editingCampaign.timeRange, - includeNsfw: editingCampaign.includeNsfw, - postLimit: editingCampaign.postLimit, - enabled: editingCampaign.enabled, - } - - const response = await fetch(`/api/campaigns/${editingCampaign.id}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(campaignData), - }) - - const data = await response.json() - - if (data.success) { - toast.success('Campaign updated successfully!') - loadData() // Reload campaigns list - setEditingCampaign(null) - } else { - toast.error(data.error || 'Failed to update campaign') - } - } catch (error) { - toast.error('Failed to update campaign') - } - } - - const deleteCampaign = async (campaignId: string) => { - if (!confirm('Are you sure you want to delete this campaign? This action cannot be undone.')) return - - try { - const response = await fetch(`/api/campaigns/${campaignId}`, { - method: 'DELETE', - }) - - const data = await response.json() - - if (data.success) { - toast.success('Campaign deleted successfully!') - loadData() // Reload campaigns list - } else { - toast.error(data.error || 'Failed to delete campaign') - } - } catch (error) { - toast.error('Failed to delete campaign') - } - } - - if (loading) { - return ( -
-
-
- ) - } - - return ( -
-
-
-

- Reddit Campaigns -

-

- Manage Reddit content campaigns and automatic posting -

-
-
- - -
-
- - {/* Status Cards */} -
-
-
-
-

Reddit Session

-

- {sessionConfig.enabled ? ( - Active - ) : ( - Inactive - )} -

-
- -
-
- -
-
-
-

Total Campaigns

-

{campaigns?.length || 0}

-
-
- -
-
-
-
- - {/* Campaigns List */} -
-
-

Campaigns

-
-
- {campaigns?.length === 0 ? ( -
-

No campaigns created yet

- -
- ) : ( -
- {campaigns?.map((campaign) => ( -
-
-
-
-

- {campaign.name} -

- {campaign.enabled ? ( - - ) : ( - - )} -
-

- Subreddits: {campaign.subreddits.join(', ')} -

-

- Min Score: {campaign.minScore} | Sort: {campaign.sortBy} | Limit: {campaign.postLimit} -

- {campaign.lastRun && ( -

- Last run: {new Date(campaign.lastRun).toLocaleString()} -

- )} -
-
- - - - -
-
-
- ))} -
- )} -
-
- - {/* Session Config Modal */} - {showSessionModal && ( -
-
-
-

Reddit Session Configuration

- -
- -
-
- -