[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-83293":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":17,"stars90d":15,"forks30d":15,"starsTrendScore":18,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":28,"readmeContent":29,"aiSummary":10,"trendingCount":15,"starSnapshotCount":15,"syncStatus":14,"lastSyncTime":30,"discoverSource":31},83293,"9drive","zenhosta\u002F9drive","zenhosta","9Drive is a storage gateway web app for connecting multiple Google Drive accounts into one virtual storage dashboard. Users can connect Google Drive accounts, track quota, upload files, organize files with virtual folders, preview files, and let the backend route uploads to the Drive account with enough free space.","https:\u002F\u002F9drive.zenhosta.com",null,"TypeScript",498,169,2,0,11,354,80,6.69,false,"main",true,[24,25,26,27],"drive","gateway","google","storage","2026-06-12 02:04:33","![9Drive cover](https:\u002F\u002Fi.ibb.co.com\u002F35BySv1C\u002Fimage.png)\n\n# 9Drive\n\n9Drive is a storage gateway web app for connecting multiple Google Drive accounts into one virtual storage dashboard. Users can register with email\u002Fpassword or Google, automatically connect their first Google Drive account during Google sign-in, track quota, upload files into a dedicated `9drive` Drive folder, organize files with virtual folders, preview files, sync MySQL from Google Drive, and let the backend route uploads to the Drive account with enough free space.\n\n## Features\n\n- React + Vite frontend.\n- Express + TypeScript backend.\n- MySQL database with Prisma migrations.\n- Bearer token authentication.\n- Email\u002Fpassword auth plus Google sign-in\u002Fregister with automatic first Drive connection.\n- Global Google OAuth config stored encrypted in DB.\n- Optional reCAPTCHA on email\u002Fpassword registration.\n- Direct upload stream to Google Drive. Files are not stored on the server.\n- Google Drive uploads are stored under a root `9drive` folder.\n- Manual sync from the Google Drive `9drive` folder back into MySQL.\n- Multi-account storage quota summary.\n- Quota tracker page.\n- Virtual folders.\n- File preview, download, rename, move, and delete actions.\n- Bottom-right upload progress panel.\n\n## Preview\n\nLive preview: https:\u002F\u002F9drive.zenhosta.com\n\n![9Drive dashboard preview](https:\u002F\u002Fi.ibb.co.com\u002FHLjG3JRf\u002Fimage.png)\n\n![9Drive shared file preview](https:\u002F\u002Fi.ibb.co.com\u002FQLpYGmx\u002Fimage.png)\n\n## Star History\n\n[![Star History Chart](https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=zenhosta\u002F9drive&type=Date)](https:\u002F\u002Fwww.star-history.com\u002F#zenhosta\u002F9drive&Date)\n\n## Project Structure\n\n```txt\nbackend\u002F   Express API, Prisma schema, Google Drive integration\nfrontend\u002F  Vite React app\n```\n\n## Requirements\n\n- Node.js 20+\n- npm\n- MySQL running locally\n- Google Cloud project\n- Google OAuth Client ID and Client Secret\n\nDefault database used by this project:\n\n```txt\nhost: localhost\nport: 3306\ndatabase: 9drive\nuser: root\npassword: empty\n```\n\n## 1. Clone And Install\n\n```bash\ngit clone git@github.com:zenhosta\u002F9drive.git\ncd 9drive\n```\n\nInstall backend dependencies:\n\n```bash\ncd backend\nnpm install\n```\n\nInstall frontend dependencies:\n\n```bash\ncd ..\u002Ffrontend\nnpm install\n```\n\n## 2. Create MySQL Database\n\nCreate database:\n\n```sql\nCREATE DATABASE 9drive;\n```\n\nIf using MySQL CLI:\n\n```bash\nmysql -u root -e \"CREATE DATABASE IF NOT EXISTS 9drive;\"\n```\n\n## 3. Backend Environment\n\nCreate `backend\u002F.env`:\n\n```env\nDATABASE_URL=\"mysql:\u002F\u002Froot@localhost:3306\u002F9drive\"\nAPP_PORT=4000\nFRONTEND_URL=\"http:\u002F\u002Flocalhost:5173\"\nJWT_ACCESS_SECRET=\"change-this-jwt-secret-at-least-32-chars\"\nTOKEN_ENCRYPTION_KEY=\"change-this-encryption-key-32bytes!\"\nACCESS_TOKEN_TTL_SECONDS=900\nREFRESH_TOKEN_TTL_DAYS=30\nMAX_UPLOAD_BYTES=5368709120\nRECAPTCHA_SECRET_KEY=\"\"\n\n# Used only by `npm run seed:google-config`.\n# These values are encrypted and stored in DB as global Google OAuth config.\nGOOGLE_CLIENT_ID=\"\"\nGOOGLE_CLIENT_SECRET=\"\"\nGOOGLE_REDIRECT_URI=\"http:\u002F\u002Flocalhost:4000\u002Fconnected-accounts\u002Fgoogle\u002Fcallback\"\n```\n\nImportant:\n\n- `JWT_ACCESS_SECRET` should be long and random.\n- `TOKEN_ENCRYPTION_KEY` should be long and random.\n- Do not commit `backend\u002F.env`.\n- Google OAuth credentials are used by the seed script, then stored encrypted in the database.\n\n## 4. Frontend Environment\n\nCreate or confirm `frontend\u002F.env`:\n\n```env\nVITE_API_URL=http:\u002F\u002Flocalhost:4000\nVITE_RECAPTCHA_SITE_KEY=\n```\n\nCaptcha is disabled when `VITE_RECAPTCHA_SITE_KEY` or backend `RECAPTCHA_SECRET_KEY` is empty. Set both values to enable captcha on registration.\n\n## 5. Run Prisma Migrations\n\n```bash\ncd backend\nnpm run prisma:migrate\n```\n\nIf Prisma client generation is blocked on Windows by a running Node process, stop running backend\u002Ffrontend dev servers and run:\n\n```bash\nnpx prisma generate\n```\n\n## 6. Google Cloud Setup\n\nGoogle setup is done in Google Cloud Console, not Google Search Console. Google Search Console is for website indexing\u002Fsearch ownership. OAuth and Drive API are managed in Google Cloud Console.\n\nOpen Google Cloud Console:\n\n```txt\nhttps:\u002F\u002Fconsole.cloud.google.com\u002F\n```\n\n### 6.1 Create Or Select Project\n\n1. Open Google Cloud Console.\n2. Click project selector in top bar.\n3. Create a new project or select an existing project.\n4. Remember the project name because OAuth client and Drive API must be in the same project.\n\n### 6.2 Enable Google Drive API\n\n1. Go to:\n\n```txt\nAPIs & Services -> Library\n```\n\n2. Search:\n\n```txt\nGoogle Drive API\n```\n\n3. Open `Google Drive API`.\n4. Click `Enable`.\n5. Wait a few minutes if Google says the API was enabled recently.\n\nDirect URL pattern:\n\n```txt\nhttps:\u002F\u002Fconsole.developers.google.com\u002Fapis\u002Fapi\u002Fdrive.googleapis.com\u002Foverview?project=YOUR_PROJECT_ID\n```\n\nIf Google Drive API is disabled, you will see an error like:\n\n```txt\nGoogle Drive API has not been used in project ... before or it is disabled.\n```\n\n### 6.3 Configure OAuth Consent Screen\n\n1. Go to:\n\n```txt\nAPIs & Services -> OAuth consent screen\n```\n\n2. Choose app type:\n\n```txt\nExternal\n```\n\n3. Fill required fields:\n\n```txt\nApp name\nUser support email\nDeveloper contact email\n```\n\n4. Add scopes:\n\n```txt\nhttps:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fdrive\nhttps:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fuserinfo.email\nhttps:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fuserinfo.profile\n```\n\nFull Drive access is required so Google sign-in can connect the first Drive account automatically and sync files manually added to the `9drive` folder.\n\n5. If publishing status is `Testing`, add test users.\n\nAdd every Google account that will test the app:\n\n```txt\nOAuth consent screen -> Test users -> Add users\n```\n\nIf you do not add test users, Google may show:\n\n```txt\nAccess blocked: app has not completed the Google verification process\nError 403: access_denied\n```\n\n### 6.4 Create OAuth Client\n\n1. Go to:\n\n```txt\nAPIs & Services -> Credentials\n```\n\n2. Click:\n\n```txt\nCreate Credentials -> OAuth client ID\n```\n\n3. Application type:\n\n```txt\nWeb application\n```\n\n4. Add authorized JavaScript origin:\n\n```txt\nhttp:\u002F\u002Flocalhost:5173\n```\n\n5. Add authorized redirect URI:\n\n```txt\nhttp:\u002F\u002Flocalhost:4000\u002Fconnected-accounts\u002Fgoogle\u002Fcallback\n```\n\n6. Click Create.\n7. Copy:\n\n```txt\nClient ID\nClient Secret\n```\n\n### 6.5 Seed Google OAuth Config\n\nPut values into `backend\u002F.env`:\n\n```env\nGOOGLE_CLIENT_ID=\"your-client-id\"\nGOOGLE_CLIENT_SECRET=\"your-client-secret\"\nGOOGLE_REDIRECT_URI=\"http:\u002F\u002Flocalhost:4000\u002Fconnected-accounts\u002Fgoogle\u002Fcallback\"\n```\n\nThen run:\n\n```bash\ncd backend\nnpm run seed:google-config\n```\n\nThis stores the Google OAuth config as a global encrypted provider config in MySQL. Google sign-in uses the same config and automatically connects the first Drive account. Logged-in users can still click `Connect Drive` in Settings to add more Drive accounts.\n\n## 7. Run Development Servers\n\nStart backend:\n\n```bash\ncd backend\nnpm run dev\n```\n\nBackend runs at:\n\n```txt\nhttp:\u002F\u002Flocalhost:4000\n```\n\nStart frontend:\n\n```bash\ncd frontend\nnpm run dev\n```\n\nFrontend runs at:\n\n```txt\nhttp:\u002F\u002Flocalhost:5173\n```\n\n## Docker Deployment\n\nThis repository includes Docker files for running MySQL, backend, and frontend together.\n\nFiles:\n\n```txt\ndocker-compose.yml\n.env.docker.example\nbackend\u002FDockerfile\nfrontend\u002FDockerfile\nfrontend\u002Fnginx.conf\n```\n\n### 1. Prepare Docker Env\n\nCopy the example env file:\n\n```bash\ncp .env.docker.example .env\n```\n\nOn Windows PowerShell:\n\n```powershell\nCopy-Item .env.docker.example .env\n```\n\nEdit `.env`:\n\n```env\nMYSQL_ROOT_PASSWORD=root\nMYSQL_DATABASE=9drive\n\nFRONTEND_URL=http:\u002F\u002Flocalhost:5173\nVITE_API_URL=http:\u002F\u002Flocalhost:4000\nVITE_RECAPTCHA_SITE_KEY=\n\nJWT_ACCESS_SECRET=replace-with-long-random-secret\nTOKEN_ENCRYPTION_KEY=replace-with-long-random-secret\nRECAPTCHA_SECRET_KEY=\n\nGOOGLE_CLIENT_ID=your-google-client-id\nGOOGLE_CLIENT_SECRET=your-google-client-secret\nGOOGLE_REDIRECT_URI=http:\u002F\u002Flocalhost:4000\u002Fconnected-accounts\u002Fgoogle\u002Fcallback\n```\n\nCaptcha is disabled when either `VITE_RECAPTCHA_SITE_KEY` or `RECAPTCHA_SECRET_KEY` is empty.\n\n### 2. Start Containers\n\n```bash\ndocker compose up -d --build\n```\n\nServices:\n\n```txt\nfrontend: http:\u002F\u002Flocalhost:5173\nbackend:  http:\u002F\u002Flocalhost:4000\nmysql:    localhost:3306\n```\n\nThe backend container runs Prisma migrations automatically on startup:\n\n```txt\nnpx prisma migrate deploy\n```\n\n### 3. Seed Google OAuth Config In Docker\n\nAfter containers are running, seed the global Google OAuth config:\n\n```bash\ndocker compose exec backend npm run seed:google-config\n```\n\nThis stores `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `GOOGLE_REDIRECT_URI` from Docker env into MySQL as encrypted global config.\n\n### 4. View Logs\n\n```bash\ndocker compose logs -f backend\ndocker compose logs -f frontend\ndocker compose logs -f mysql\n```\n\n### 5. Stop Containers\n\n```bash\ndocker compose down\n```\n\nRemove database volume too:\n\n```bash\ndocker compose down -v\n```\n\n### Docker Production Notes\n\n- Replace localhost URLs with production domain.\n- Update Google OAuth authorized JavaScript origin.\n- Update Google OAuth redirect URI.\n- Use strong `JWT_ACCESS_SECRET` and `TOKEN_ENCRYPTION_KEY`.\n- Do not expose MySQL port publicly in production.\n- Put frontend\u002Fbackend behind HTTPS reverse proxy.\n- Rebuild frontend when `VITE_API_URL` changes because Vite embeds env at build time.\n- Rebuild frontend when `VITE_RECAPTCHA_SITE_KEY` changes because Vite embeds env at build time.\n\n## 8. Manual Test Flow\n\n1. Open frontend:\n\n```txt\nhttp:\u002F\u002Flocalhost:5173\n```\n\n2. Register a user with email\u002Fpassword and captcha, or click `Continue with Google and connect Drive`.\n3. If using Google sign-in, approve Drive access once and confirm `\u002Fsettings` already shows the connected account.\n4. If using email\u002Fpassword, open `Settings`, click `Connect Drive`, approve access, and confirm the account appears.\n5. Open `Quota Tracker`.\n6. Confirm quota appears.\n7. Open `All Files`.\n8. Create nested virtual folders.\n9. Upload a file and confirm it appears under Google Drive root folder `9drive`.\n10. Add or remove a file manually inside Google Drive folder `9drive`, then click `Sync Drive` in All Files.\n11. Watch bottom-right upload progress.\n12. Right-click file row for actions:\n\n```txt\nView\nDownload\nRename\nMove to Folder\nDelete\n```\n\n## API Overview\n\nAuth:\n\n```txt\nPOST \u002Fauth\u002Fregister\nPOST \u002Fauth\u002Flogin\nGET \u002Fauth\u002Fgoogle\u002Furl\nGET \u002Fauth\u002Fgoogle\u002Fcallback\nPOST \u002Fauth\u002Fgoogle\u002Fexchange\nPOST \u002Fauth\u002Frefresh\nPOST \u002Fauth\u002Flogout\nGET \u002Fauth\u002Fme\n```\n\nGoogle accounts:\n\n```txt\nGET \u002Fconnected-accounts\u002Fgoogle\u002Fconnect-url\nGET \u002Fconnected-accounts\u002Fgoogle\u002Fcallback\nGET \u002Fconnected-accounts\nPOST \u002Fconnected-accounts\u002F:id\u002Fsync-quota\nDELETE \u002Fconnected-accounts\u002F:id\n```\n\nStorage:\n\n```txt\nGET \u002Fstorage\u002Fsummary\n```\n\nFolders:\n\n```txt\nGET \u002Ffolders\nGET \u002Ffolders\u002Frecent?limit=4\nPOST \u002Ffolders\nDELETE \u002Ffolders\u002F:id\n```\n\nFiles:\n\n```txt\nGET \u002Ffiles\nGET \u002Ffiles?folderId=\u003Cid>\nGET \u002Ffiles?q=\u003Csearch>\nGET \u002Ffiles\u002Fshared-links\nGET \u002Ffiles\u002F:id\nPATCH \u002Ffiles\u002F:id\nPATCH \u002Ffiles\u002Fbatch\nDELETE \u002Ffiles\u002Fbatch\nPOST \u002Ffiles\u002Fsync-google\nPOST \u002Ffiles\u002F:id\u002Fshare\nDELETE \u002Ffiles\u002F:id\u002Fshare\nPOST \u002Ffiles\u002F:id\u002Fpreview-token\nGET \u002Ffiles\u002F:id\u002Fview-url\nGET \u002Ffiles\u002F:id\u002Fdownload\nDELETE \u002Ffiles\u002F:id\nGET \u002Ffiles\u002Fpreview\u002F:token\n```\n\nUploads:\n\n```txt\nPOST \u002Fuploads\n```\n\nUpload is `multipart\u002Fform-data`. Metadata fields should be appended before the file:\n\n```txt\nsizeBytes\nfileName\nmimeType\nfolderId optional\nfile\n```\n\n## Security Notes\n\n- Backend never stores uploaded files on disk.\n- Uploads are streamed through the backend to Google Drive folder `9drive`.\n- Google tokens are encrypted in MySQL.\n- Refresh tokens for app sessions are hashed in MySQL.\n- Google auth handoff tokens, public share tokens, and preview tokens are hashed before lookup\u002Fuse.\n- `backend\u002F.env` is ignored by git.\n- Do not expose `TOKEN_ENCRYPTION_KEY`, `JWT_ACCESS_SECRET`, `RECAPTCHA_SECRET_KEY`, OAuth client secrets, or raw share\u002Fpreview\u002Fhandoff tokens.\n\n## Production Notes\n\n- Replace localhost redirect URIs with production URLs.\n- Add production domain to Google OAuth authorized origins.\n- Set OAuth consent screen to production when ready.\n- Google may require verification for public apps.\n- Use strong secrets.\n- Put the backend behind HTTPS.\n- Consider secure cookies or stronger token storage for production.\n\n## Build\n\nBackend:\n\n```bash\ncd backend\nnpm run build\n```\n\nFrontend:\n\n```bash\ncd frontend\nnpm run build\n```\n","2026-06-11 04:10:49","CREATED_QUERY"]