{"_id":"@duonghuyhieu/game-saver","_rev":"6-cb05d420f4402c82ad064ff84413d719","name":"@duonghuyhieu/game-saver","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.0":{"name":"@duonghuyhieu/game-saver","version":"1.0.0","keywords":["game","save","backup","cli","cloud"],"author":{"name":"Duong Huy Hieu"},"license":"MIT","_id":"@duonghuyhieu/game-saver@1.0.0","maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"bin":{"game-saver":"bin/cli.js"},"dist":{"shasum":"cdafbe019bf804d5efe12b70da07b0e169d45d74","tarball":"https://registry.npmjs.org/@duonghuyhieu/game-saver/-/game-saver-1.0.0.tgz","fileCount":11,"integrity":"sha512-s3W8XEwY/VTRxJdBkV/uBegu+8Umx6baii0nQypjnpK6RWjjNwKBf92FRnRwIJcQlUEjo/YDu/C5dy/9ouJb9w==","signatures":[{"sig":"MEQCIH6w10wUyPD6zJofq6VmZZjVJ7SES6paPv2Cf/rdsvLmAiAIavy805fuMgTGeRsr7D5Erh5LstgbZdRw4f31iZCKBQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33298},"type":"module","gitHead":"07ac25a27a1fe7de5dc88f7023fe02bdf288e844","private":false,"scripts":{"cli":"tsx ./cli/index.ts","dev":"next dev","lint":"eslint","build":"next build","start":"next start","build:cli":"tsc -p tsconfig.cli.json","prepublishOnly":"npm run build:cli"},"_npmUser":{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"},"repository":{"url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git","type":"git"},"_npmVersion":"11.6.2","description":"CLI tool to backup and sync game saves to cloud","directories":{},"_nodeVersion":"25.0.0","dependencies":{"ora":"^8.0.1","next":"16.0.1","axios":"^1.6.2","chalk":"^5.3.0","react":"19.2.0","dotenv":"^16.3.1","zustand":"^5.0.8","bcryptjs":"^3.0.3","inquirer":"^9.2.12","form-data":"^4.0.0","react-dom":"19.2.0","jsonwebtoken":"^9.0.2","lucide-react":"^0.552.0","@supabase/ssr":"^0.7.0","@supabase/supabase-js":"^2.79.0","@tanstack/react-query":"^5.90.6"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","eslint":"^9","typescript":"^5","@types/node":"^20","tailwindcss":"^4","@types/react":"^19","@types/bcryptjs":"^2.4.6","@types/inquirer":"^9.0.7","@types/react-dom":"^19","eslint-config-next":"16.0.1","@types/jsonwebtoken":"^9.0.10","@tailwindcss/postcss":"^4"},"_npmOperationalInternal":{"tmp":"tmp/game-saver_1.0.0_1762849672031_0.8546121226131387","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@duonghuyhieu/game-saver","version":"1.0.1","keywords":["game","save","backup","cli","cloud"],"author":{"name":"Duong Huy Hieu"},"license":"MIT","_id":"@duonghuyhieu/game-saver@1.0.1","maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"bin":{"game-saver":"bin/cli.js"},"dist":{"shasum":"64dd3b1fe050b93ae2603da15d7b1ef421a82ba5","tarball":"https://registry.npmjs.org/@duonghuyhieu/game-saver/-/game-saver-1.0.1.tgz","fileCount":11,"integrity":"sha512-HrLWA57Tn1fvZdjZ8uwakrmtQjTooXWPkvho7RXC/lqSYEsc1E52zFOatuc2OaXUa2x2zEDJvmgrUoU3XhlsNA==","signatures":[{"sig":"MEYCIQCuiwO2DyUC7TMkaLJgxG3s3eP/MQRiahmspoo914e4FwIhAOnBlNs50wdVJx+SkPEF4GzGLlvXFxWXh0u9eDyP0gX3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33344},"type":"module","gitHead":"0102a46c362fae0e4abe7f52d28db9e3615b7b65","private":false,"scripts":{"cli":"tsx ./cli/index.ts","dev":"next dev","lint":"eslint","build":"next build","start":"next start","build:cli":"tsc -p tsconfig.cli.json","prepublishOnly":"npm run build:cli"},"_npmUser":{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"},"repository":{"url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git","type":"git"},"_npmVersion":"11.6.2","description":"CLI tool to backup and sync game saves to cloud","directories":{},"_nodeVersion":"25.0.0","dependencies":{"ora":"^8.0.1","next":"16.0.1","rxjs":"^7.8.2","axios":"^1.6.2","chalk":"^5.3.0","react":"19.2.0","dotenv":"^16.3.1","zustand":"^5.0.8","bcryptjs":"^3.0.3","inquirer":"^9.2.12","form-data":"^4.0.0","react-dom":"19.2.0","jsonwebtoken":"^9.0.2","lucide-react":"^0.552.0","@supabase/ssr":"^0.7.0","@supabase/supabase-js":"^2.79.0","@tanstack/react-query":"^5.90.6"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","eslint":"^9","typescript":"^5","@types/node":"^20","tailwindcss":"^4","@types/react":"^19","@types/bcryptjs":"^2.4.6","@types/inquirer":"^9.0.7","@types/react-dom":"^19","eslint-config-next":"16.0.1","@types/jsonwebtoken":"^9.0.10","@tailwindcss/postcss":"^4"},"_npmOperationalInternal":{"tmp":"tmp/game-saver_1.0.1_1762850486086_0.3712835370277372","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@duonghuyhieu/game-saver","version":"1.0.2","keywords":["game","save","backup","cli","cloud"],"author":{"name":"Duong Huy Hieu"},"license":"MIT","_id":"@duonghuyhieu/game-saver@1.0.2","maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"bin":{"game-saver":"bin/cli.js"},"dist":{"shasum":"49144c8bf237f99d53185f9e88dcbfa662882306","tarball":"https://registry.npmjs.org/@duonghuyhieu/game-saver/-/game-saver-1.0.2.tgz","fileCount":11,"integrity":"sha512-aDvluHIJxZXnv0mwHodNi4qCMQhRa1rnCwGx3VGX7I7IK2LDOAvTTI8332M82+mYrlTTD8GNfUjXPT7UXV/eFA==","signatures":[{"sig":"MEUCIQDeAq+jej/76FHr8mAfP1vAhjRThemLTPL+83zdmiOLNAIgVxnvbq/YvnnKR55SFfkReD8aJKZwv4TPoeQg0eDekjs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33368},"type":"module","gitHead":"17c9b7f25c0f48fedf2204cb62475f93650e89f2","private":false,"scripts":{"cli":"tsx ./cli/index.ts","dev":"next dev","lint":"eslint","build":"next build","start":"next start","build:cli":"tsc -p tsconfig.cli.json","prepublishOnly":"npm run build:cli"},"_npmUser":{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"},"repository":{"url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git","type":"git"},"_npmVersion":"11.6.2","description":"CLI tool to backup and sync game saves to cloud","directories":{},"_nodeVersion":"25.0.0","dependencies":{"ora":"^8.0.1","next":"16.0.1","rxjs":"^7.8.2","axios":"^1.6.2","chalk":"^5.3.0","react":"19.2.0","dotenv":"^16.3.1","zustand":"^5.0.8","bcryptjs":"^3.0.3","inquirer":"^9.2.12","form-data":"^4.0.0","react-dom":"19.2.0","jsonwebtoken":"^9.0.2","lucide-react":"^0.552.0","@supabase/ssr":"^0.7.0","@supabase/supabase-js":"^2.79.0","@tanstack/react-query":"^5.90.6"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","eslint":"^9","typescript":"^5","@types/node":"^20","tailwindcss":"^4","@types/react":"^19","@types/bcryptjs":"^2.4.6","@types/inquirer":"^9.0.7","@types/react-dom":"^19","eslint-config-next":"16.0.1","@types/jsonwebtoken":"^9.0.10","@tailwindcss/postcss":"^4"},"_npmOperationalInternal":{"tmp":"tmp/game-saver_1.0.2_1762850937646_0.4500041385408413","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@duonghuyhieu/game-saver","version":"1.0.3","keywords":["game","save","backup","cli","cloud"],"author":{"name":"Duong Huy Hieu"},"license":"MIT","_id":"@duonghuyhieu/game-saver@1.0.3","maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"bin":{"game-saver":"bin/cli.js"},"dist":{"shasum":"30faa2633a9af7a5cb8c0b9e84e86d0ca23a41d0","tarball":"https://registry.npmjs.org/@duonghuyhieu/game-saver/-/game-saver-1.0.3.tgz","fileCount":11,"integrity":"sha512-KSIQllg6ND34g5/tj4fCIsLgmEJxBMd49O3kCi/D+VQUUwddAwumFF8+jsvkZSYCtz4PrIVTGBkmmnVoFUrlBQ==","signatures":[{"sig":"MEQCIBep/R6orE9Fnqio0Icg65XSlwu9HoybD9FCoCv8IYWuAiB+4rmOMIDQzq03vXpQAMUKadGtiyWS9cqwYzZg1bi5cA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":38071},"type":"module","gitHead":"36e3de363aa844919220b962b99ae36f1808c55a","private":false,"scripts":{"cli":"tsx ./cli/index.ts","dev":"next dev","lint":"eslint","build":"next build","start":"next start","build:cli":"tsc -p tsconfig.cli.json","prepublishOnly":"npm run build:cli"},"_npmUser":{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"},"repository":{"url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git","type":"git"},"_npmVersion":"11.6.2","description":"CLI tool to backup and sync game saves to cloud","directories":{},"_nodeVersion":"25.0.0","dependencies":{"ora":"^8.0.1","next":"16.0.1","rxjs":"^7.8.2","axios":"^1.6.2","chalk":"^5.3.0","react":"19.2.0","dotenv":"^16.3.1","zustand":"^5.0.8","bcryptjs":"^3.0.3","inquirer":"^9.2.12","form-data":"^4.0.0","react-dom":"19.2.0","jsonwebtoken":"^9.0.2","lucide-react":"^0.552.0","@supabase/ssr":"^0.7.0","@supabase/supabase-js":"^2.79.0","@tanstack/react-query":"^5.90.6"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","eslint":"^9","typescript":"^5","@types/node":"^20","tailwindcss":"^4","@types/react":"^19","@types/bcryptjs":"^2.4.6","@types/inquirer":"^9.0.7","@types/react-dom":"^19","eslint-config-next":"16.0.1","@types/jsonwebtoken":"^9.0.10","@tailwindcss/postcss":"^4"},"_npmOperationalInternal":{"tmp":"tmp/game-saver_1.0.3_1762861688565_0.5317442783283826","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@duonghuyhieu/game-saver","version":"1.0.4","keywords":["game","save","backup","cli","cloud"],"author":{"name":"Duong Huy Hieu"},"license":"MIT","_id":"@duonghuyhieu/game-saver@1.0.4","maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"bin":{"game-saver":"bin/cli.js"},"dist":{"shasum":"52153de10ce1dc383f89bf44d608e473da523252","tarball":"https://registry.npmjs.org/@duonghuyhieu/game-saver/-/game-saver-1.0.4.tgz","fileCount":11,"integrity":"sha512-k3cVSaqO+n+1WqUJec1D6/chKMAID+BXsTAYRNoS3S/OzbtnSncLcxaw5Fenp0dpyLFN3jzm7g4bpJWaMPJZgQ==","signatures":[{"sig":"MEUCIQCph6eWjg6YXRM0VYSMz/iMJumq4XNv52b8z5doYpLE4gIgWsUhu9doJjyU0+Sq/FKiroGJV8M4S3zEUjGcwH5/omY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39258},"type":"module","gitHead":"7875fd9a8ea452201973ff64cbdbcac94a8b23c7","private":false,"scripts":{"cli":"tsx ./cli/index.ts","dev":"next dev","lint":"eslint","build":"next build","start":"next start","build:cli":"tsc -p tsconfig.cli.json","prepublishOnly":"npm run build:cli"},"_npmUser":{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"},"repository":{"url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git","type":"git"},"_npmVersion":"11.6.2","description":"CLI tool to backup and sync game saves to cloud","directories":{},"_nodeVersion":"25.0.0","dependencies":{"ora":"^8.0.1","next":"16.0.1","rxjs":"^7.8.2","axios":"^1.6.2","chalk":"^5.3.0","react":"19.2.0","dotenv":"^16.3.1","zustand":"^5.0.8","bcryptjs":"^3.0.3","inquirer":"^9.2.12","form-data":"^4.0.0","react-dom":"19.2.0","jsonwebtoken":"^9.0.2","lucide-react":"^0.552.0","@supabase/ssr":"^0.7.0","@supabase/supabase-js":"^2.79.0","@tanstack/react-query":"^5.90.6"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","eslint":"^9","typescript":"^5","@types/node":"^20","tailwindcss":"^4","@types/react":"^19","@types/bcryptjs":"^2.4.6","@types/inquirer":"^9.0.7","@types/react-dom":"^19","eslint-config-next":"16.0.1","@types/jsonwebtoken":"^9.0.10","@tailwindcss/postcss":"^4"},"_npmOperationalInternal":{"tmp":"tmp/game-saver_1.0.4_1763524045285_0.7037236668692521","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@duonghuyhieu/game-saver","version":"1.0.5","private":false,"description":"CLI tool to backup and sync game saves to cloud","author":{"name":"Duong Huy Hieu"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git"},"keywords":["game","save","backup","cli","cloud"],"type":"module","bin":{"game-saver":"bin/cli.js"},"scripts":{"dev":"next dev","build":"next build","build:cli":"tsc -p tsconfig.cli.json","prepublishOnly":"npm run build:cli","start":"next start","lint":"eslint","cli":"tsx ./cli/index.ts"},"dependencies":{"@supabase/ssr":"^0.7.0","@supabase/supabase-js":"^2.79.0","@tanstack/react-query":"^5.90.6","axios":"^1.6.2","bcryptjs":"^3.0.3","chalk":"^5.3.0","dotenv":"^16.3.1","form-data":"^4.0.0","inquirer":"^9.2.12","jsonwebtoken":"^9.0.2","lucide-react":"^0.552.0","next":"16.0.1","ora":"^8.0.1","react":"19.2.0","react-dom":"19.2.0","rxjs":"^7.8.2","zustand":"^5.0.8"},"devDependencies":{"@tailwindcss/postcss":"^4","@types/bcryptjs":"^2.4.6","@types/inquirer":"^9.0.7","@types/jsonwebtoken":"^9.0.10","@types/node":"^20","@types/react":"^19","@types/react-dom":"^19","eslint":"^9","eslint-config-next":"16.0.1","tailwindcss":"^4","tsx":"^4.7.0","typescript":"^5"},"gitHead":"50abe7247cc55c1f89b6721b1e68e20b6ffbb350","_id":"@duonghuyhieu/game-saver@1.0.5","bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","_nodeVersion":"25.0.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-DV1MJJhcPnQNIiXoqehvU7JQy1Unx/DgxqZQX2dgJmy5fhGqGZmGCd5UKYTdYni/BTFuxujQ0mSbBGof18caiQ==","shasum":"c5edcf99aae7ddace293395c5d47c85e05af320c","tarball":"https://registry.npmjs.org/@duonghuyhieu/game-saver/-/game-saver-1.0.5.tgz","fileCount":12,"unpackedSize":38297,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCWGCakay5DH3g7gJzCXvzrefysoLy8pjIj8LEGJoOT7wIga7CJUKhrdhM/wBJeuwYl9pwc+eQXgGW82Db1L+rH6cI="}]},"_npmUser":{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"},"directories":{},"maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/game-saver_1.0.5_1763629139701_0.9432829675127068"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-11T08:27:51.955Z","modified":"2025-11-20T08:59:00.066Z","1.0.0":"2025-11-11T08:27:52.229Z","1.0.1":"2025-11-11T08:41:26.303Z","1.0.2":"2025-11-11T08:48:57.853Z","1.0.3":"2025-11-11T11:48:08.750Z","1.0.4":"2025-11-19T03:47:25.481Z","1.0.5":"2025-11-20T08:58:59.877Z"},"bugs":{"url":"https://github.com/duonghuyhieu/choi-cung-teppy/issues"},"author":{"name":"Duong Huy Hieu"},"license":"MIT","homepage":"https://github.com/duonghuyhieu/choi-cung-teppy#readme","keywords":["game","save","backup","cli","cloud"],"repository":{"type":"git","url":"git+https://github.com/duonghuyhieu/choi-cung-teppy.git"},"description":"CLI tool to backup and sync game saves to cloud","maintainers":[{"name":"huyhieu93","email":"gunnyhuyhieu@gmail.com"}],"readme":"# Game Saver\n\nHệ thống quản lý và đồng bộ save game trên cloud. Gồm web app (admin portal) và **CLI tool** chạy trực tiếp trong terminal.\n\n## 🚀 Live Demo\n\n- **Web App**: [https://choi-cung-teppy.vercel.app](https://choi-cung-teppy.vercel.app)\n- **CLI Tool**: `npx @duonghuyhieu/game-saver`\n\n## Tính năng\n\n- 🎮 **Xem danh sách game** - không cần đăng nhập\n- 📥 **Tải game** - link tự động mở trong browser\n- 💾 **Backup save game** lên cloud (Supabase Storage)\n- 🔄 **Đồng bộ** save game giữa nhiều thiết bị\n- ☁️ **Download save public** do admin chia sẻ\n- ⚡ **Tự động inject** save vào thư mục game\n- 🖥️ **CLI thuần túy** - chạy ngay trong terminal, không cần mở app riêng\n- 🌐 **Web Portal** - quản lý saves, tải save trực tiếp trên web\n\n## Kiến trúc\n\n```\nweb/\n├── app/          # Next.js Web App (Admin + User Portal)\n├── cli/          # Node.js CLI Tool\n│   ├── index.ts          # CLI entry point\n│   ├── config.ts         # API configuration\n│   └── services/         # API client & file system\n├── types/        # Shared TypeScript types\n├── bin/          # CLI executable\n└── components/   # React components\n```\n\n## Tech Stack\n\n- **Web**: Next.js 14, React, Supabase, Tailwind CSS\n- **CLI**: Node.js, Inquirer, Chalk, Ora\n- **Database**: Supabase (PostgreSQL)\n- **Storage**: Supabase Storage\n- **Deploy**: Vercel (web), NPM (CLI)\n\n---\n\n## 📚 Hướng dẫn sử dụng\n\n### 👥 Cho người dùng (End Users):\n\n- **[⚡ Quick Start Guide](./QUICK_START_USER.md)** - Bắt đầu trong 5 phút!\n- **[📖 Hướng dẫn đầy đủ](./CLI_USER_GUIDE.md)** - Hướng dẫn chi tiết từng bước\n\n### 👨‍💻 Cho Developers:\n\nĐọc tiếp phần [Setup cho Developers](#setup-cho-developers) bên dưới.\n\n---\n\n## Cài đặt và Sử dụng\n\n### Người dùng: Chạy CLI ngay\n\n```bash\nnpx @duonghuyhieu/game-saver\n```\n\n**CLI sẽ chạy ngay trong terminal - không cần cài đặt!**\n\n```\n ██████   █████  ███    ███ ███████     ███████  █████  ██    ██ ███████ ██████\n██       ██   ██ ████  ████ ██          ██      ██   ██ ██    ██ ██      ██   ██\n██   ███ ███████ ██ ████ ██ █████       ███████ ███████ ██    ██ █████   ██████\n██    ██ ██   ██ ██  ██  ██ ██               ██ ██   ██  ██  ██  ██      ██   ██\n ██████  ██   ██ ██      ██ ███████     ███████ ██   ██   ████   ███████ ██   ██\n\nQuan ly va dong bo save game tren cloud\n\n? Chon hanh dong: (Use arrow keys)\n❯ [1] Chon game\n  [2] Dang nhap\n  [3] Dang ky\n  [0] Thoat\n```\n\n### Quy trình sử dụng CLI\n\n#### Lần đầu sử dụng:\n1. Chạy: `npx @duonghuyhieu/game-saver`\n2. Chọn **\"[1] Chon game\"** để xem danh sách\n3. Chọn game bạn muốn chơi\n4. Chọn **\"[1] Tai game\"** để mở link download\n5. Sau khi tải và cài game, quay lại CLI\n6. Chọn **\"[2] Dang nhap\"** hoặc **\"[3] Dang ky\"**\n7. Chọn **\"[2] Lay file save\"** để tải save public của admin (full progress!)\n\n#### Backup save của bạn:\n1. Chơi game và có progress\n2. Chạy CLI và đăng nhập\n3. Chọn game → **\"[3] Tai len file save\"**\n4. Save tự động đọc từ thư mục game và upload lên cloud\n\n#### Đồng bộ sang máy khác:\n1. Chạy CLI trên máy mới\n2. Đăng nhập cùng tài khoản\n3. Chọn game → **\"[2] Lay file save\"**\n4. Chọn save của bạn từ danh sách\n5. Save tự động ghi vào thư mục game!\n\n---\n\n## Setup cho Developers\n\n### 1. Supabase Setup\n\n#### a. Tạo Supabase Project\n1. Đăng ký tại https://supabase.com\n2. Tạo project mới\n3. Lưu lại:\n   - `SUPABASE_URL`\n   - `SUPABASE_ANON_KEY`\n   - `SUPABASE_SERVICE_ROLE_KEY`\n\n#### b. Chạy Database Schema\n1. Mở **SQL Editor** trong Supabase Dashboard\n2. Copy nội dung file `supabase-schema.sql`\n3. Paste và chạy\n\n#### c. Tạo Storage Bucket\n1. Vào **Storage** trong Supabase Dashboard\n2. Tạo bucket mới tên: `save-files`\n3. Để public = false\n\n---\n\n### 2. Web App Setup (Local Development)\n\n```bash\n# Clone repository\ngit clone https://github.com/duonghuyhieu/choi-cung-teppy.git\ncd choi-cung-teppy\n\n# Copy environment file\ncp .env.example .env.local\n\n# Chỉnh sửa .env.local với Supabase credentials\n# NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co\n# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbG...\n# SUPABASE_SERVICE_ROLE_KEY=eyJhbG...  ⚠️ QUAN TRỌNG: Cần thiết để upload files!\n# JWT_SECRET=your-random-secret-key\n\n# Install dependencies\nnpm install\n\n# Run development server\nnpm run dev\n```\n\nWeb app chạy tại: http://localhost:3000\n\n---\n\n### 3. Deploy Web App lên Vercel\n\n```bash\n# Push to GitHub (đã có git trong web/)\ngit push\n```\n\n#### Vercel Dashboard Setup:\n\n1. **Import repository** từ GitHub\n2. **Không cần set Root Directory** (project đã ở root)\n3. **⚠️ QUAN TRỌNG: Add Environment Variables** (Settings → Environment Variables):\n\n   ```\n   NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co\n   NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbG...\n   SUPABASE_SERVICE_ROLE_KEY=eyJhbG...  👈 QUAN TRỌNG!\n   JWT_SECRET=your-random-secret-key\n   ```\n\n   **⚠️ Lưu ý:**\n   - Phải thêm **TẤT CẢ** các biến này trước khi deploy\n   - **SUPABASE_SERVICE_ROLE_KEY** cần thiết để upload files! Lấy từ Settings → API → service_role key\n   - Copy từ file `.env.local` của bạn\n   - Không bỏ sót biến nào, nếu không build sẽ bị lỗi!\n\n4. **Deploy!**\n\n#### Troubleshooting Vercel Build Errors:\n\n**Lỗi: \"supabaseUrl is required\"**\n- ✅ Kiểm tra đã add `NEXT_PUBLIC_SUPABASE_URL` trong Vercel Environment Variables chưa\n- ✅ Redeploy sau khi thêm biến môi trường mới\n\n**Lỗi: \"supabaseAnonKey is required\"**\n- ✅ Kiểm tra đã add `NEXT_PUBLIC_SUPABASE_ANON_KEY` chưa\n\nTạo Admin User:\n```bash\ncurl -X POST https://choi-cung-teppy.vercel.app/api/auth/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"email\": \"admin@yourdomain.com\",\n    \"username\": \"admin\",\n    \"password\": \"your-secure-password\",\n    \"role\": \"admin\"\n  }'\n```\n\n---\n\n### 4. CLI Tool Setup\n\n#### Development (Local)\n\n```bash\n# CLI sử dụng production URL mặc định: https://choi-cung-teppy.vercel.app\n# Không cần tạo file .env nếu muốn dùng production\n\n# Nếu muốn test với local development server:\necho \"API_URL=http://localhost:3000\" > cli/.env\n\n# Run CLI\nnpm run cli\n```\n\n#### Production: Publish lên NPM\n\n**Package đã được chuẩn bị sẵn!** Xem hướng dẫn chi tiết tại [PUBLISH_NPM.md](./PUBLISH_NPM.md)\n\nTóm tắt các bước:\n1. ✅ Web app đã deploy tại: https://choi-cung-teppy.vercel.app\n2. ✅ Production URL đã được cấu hình trong `cli/config.ts`\n3. Build: `npm run build:cli`\n4. Publish: `npm publish --access public`\n5. User chạy: `npx @duonghuyhieu/game-saver`\n\n---\n\n## Sử dụng\n\n### Web App (Admin)\n\n1. Login vào https://choi-cung-teppy.vercel.app\n2. Thêm games:\n   - Tên game\n   - **Save path template**: `%APPDATA%/GameName/saves/*.sav`\n   - Download links\n3. Upload public save files (optional)\n\n### CLI Tool (User)\n\n1. **Chạy CLI**: `npx @duonghuyhieu/game-saver`\n2. **Chọn game** từ danh sách (không cần đăng nhập!)\n3. **Tải game** (nếu chưa có):\n   - Chọn \"Tai game\" để xem download links\n   - Link sẽ tự động mở trong trình duyệt\n4. **Quản lý save files** (cần đăng nhập):\n   - **Lay file save**: Tải save từ cloud và tự động ghi vào thư mục game\n   - **Tai len file save**: Đọc save từ thư mục game và upload lên cloud\n5. **Đồng bộ nhiều máy**:\n   - Upload save từ máy 1\n   - Download save về máy 2 với cùng tài khoản\n\n---\n\n## Path Template Examples\n\n```\nWindows Game Pass:\n%LOCALAPPDATA%/Packages/Microsoft.*/LocalState/*.sav\n\nSteam:\n%USERPROFILE%/Documents/My Games/GameName/*.sav\n\nEpic:\n%LOCALAPPDATA%/EpicGamesLauncher/Saved/SaveGames/*.sav\n\nCustom:\n%APPDATA%/GameName/saves/slot*.dat\n```\n\nHỗ trợ wildcards `*` - sẽ match file đầu tiên tìm thấy.\n\n---\n\n## API Endpoints\n\n### Authentication\n```\nPOST   /api/auth/register\nPOST   /api/auth/login\nPOST   /api/auth/logout\nGET    /api/auth/me\n```\n\n### Games\n```\nGET    /api/games\nPOST   /api/games            (admin only)\nGET    /api/games/:id\nPUT    /api/games/:id        (admin only)\nDELETE /api/games/:id        (admin only)\n```\n\n### Save Files\n```\nGET    /api/games/:id/saves\nPOST   /api/games/:id/saves\nGET    /api/saves/:id\nPUT    /api/saves/:id\nDELETE /api/saves/:id\n```\n\n---\n\n## Features\n\n### CLI Features\n✅ **Browse games without login** - xem danh sách game không cần đăng nhập\n✅ **Auto-open download links** - tự động mở link trong browser\n✅ **Interactive menu** - dùng arrow keys để navigate\n✅ **Persistent authentication** - token lưu trong `~/.game-saver-token`\n✅ **Auto save injection** - tự động ghi save vào đúng thư mục game\n✅ **Auto save upload** - tự động đọc save từ thư mục game\n✅ **Colored output** - dễ đọc với chalk\n✅ **Loading spinners** - feedback khi đang xử lý\n✅ **Auto path resolution** - tự động tìm file theo template\n✅ **Error handling** - thông báo lỗi rõ ràng\n\n### Web Features\n✅ **Browse all games** - xem tất cả game và download links\n✅ **Download saves on web** - tải save files trực tiếp trên web\n✅ **My Saves page** - quản lý tất cả saves của bạn\n✅ **Public saves display** - xem và tải save public trên game detail page\n✅ **JWT authentication** - đăng nhập an toàn\n✅ **Admin panel** - quản lý games, links và public saves (admin only)\n✅ **File upload/download** - tải lên và tải xuống save files\n✅ **Row Level Security (RLS)** - bảo mật dữ liệu với Supabase RLS\n\n---\n\n## Bảo mật\n\n- ✅ JWT authentication\n- ✅ HTTP-only cookies\n- ✅ Supabase RLS\n- ✅ Admin role-based access\n- ✅ Presigned URLs cho downloads\n- ✅ Token stored locally (~/.game-saver-token)\n\n---\n\n## Troubleshooting\n\n### Vercel Build Error: \"supabaseUrl is required\"\n**Nguyên nhân**: Thiếu biến môi trường trên Vercel\n\n**Giải pháp**:\n1. Vào Vercel Dashboard → chọn project\n2. **Settings** → **Environment Variables**\n3. Thêm các biến sau:\n   ```\n   NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co\n   NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key\n   SUPABASE_SERVICE_ROLE_KEY=your-service-role-key\n   JWT_SECRET=your-jwt-secret\n   ```\n4. **Deployments** → chọn build mới nhất → **Redeploy**\n\n### CLI không connect được API\n```bash\n# Check API_URL\ncat cli/.env\n\n# hoặc hardcode trong cli/config.ts\n```\n\n### Save file không inject được\n- Kiểm tra path template có đúng không\n- Chạy CLI với quyền Administrator (nếu cần write vào Program Files)\n\n### Upload save lỗi \"new row violates row-level security policy\"\n**Nguyên nhân**: Service Role Key chưa được cấu hình\n\n**Giải pháp**: Xem chi tiết tại [FIX_UPLOAD_ERROR.md](./FIX_UPLOAD_ERROR.md)\n\nTóm tắt:\n1. Lấy Service Role Key từ Supabase Dashboard (Settings → API)\n2. Thêm vào .env.local: `SUPABASE_SERVICE_ROLE_KEY=eyJhbG...`\n3. Chạy storage policies SQL (xem FIX_UPLOAD_ERROR.md)\n4. Restart server\n\n### Upload save lỗi khác\n- Kiểm tra file size < 50MB\n- Kiểm tra bucket 'save-files' đã được tạo chưa\n\n---\n\n## Requirements\n\n- **Node.js**: 16+ (để chạy CLI)\n- **NPM**: 8+ (để chạy NPX)\n- **OS**: Windows (vì dùng %APPDATA% paths)\n\n---\n\n## Cost (Free Tier)\n\n- Supabase: 500MB DB + 1GB Storage → **$0**\n- Vercel: 100GB Bandwidth → **$0**\n- NPM: Unlimited downloads → **$0**\n\n**Total: $0/month** ✅\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}