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.
See the code
9Drive is a storage gateway web app for connecting multiple Google Drive accounts into one virtual storage dashboard. Users can register with email/password 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.
Copyright 2026 Zenhosta
Licensed under the Apache License, Version 2.0. See LICENSE for details.
9drive folder.POST /api/v1/uploads.9drive folder back into MySQL.update.sh directly from the Settings UI (PM2 setup).Live preview: https://9drive.zenhosta.com


backend/ Express API, Prisma schema, Google Drive integration
frontend/ Vite React app
Default database used by this project:
host: localhost
port: 3306
database: 9drive
user: root
password: empty
The easiest way to prepare the project is using the automated setup script. It generates local environment files with secure keys, installs dependencies, and generates the Prisma client. MySQL 8+ must already be running for local development; Docker setup includes MySQL.
Make sure to open PowerShell and navigate to the project directory first. For example, if you cloned the project to E:\AUTO KLIK\9Drive:
# 1. Switch to the drive where the project is located (if necessary)
E:
# 2. Navigate to the project folder
cd "E:\AUTO KLIK\9Drive"
# 3. Run the automated setup script
powershell -ExecutionPolicy Bypass -File .\setup.ps1
bash ./setup.sh
Enter your MySQL connection URL when prompted. Google Client ID/Secret can be skipped and configured later.
Once setup is complete, apply migrations and start backend and frontend in separate terminals:
cd backend
npm run prisma:migrate
npm run dev
cd frontend
npm run dev
If you prefer to configure the project manually:
Install backend dependencies:
cd backend
npm install
Install frontend dependencies:
cd ../frontend
npm install
Create a database:
CREATE DATABASE 9drive;
If using MySQL CLI:
mysql -u root -e "CREATE DATABASE IF NOT EXISTS 9drive;"
Create backend/.env:
DATABASE_URL="mysql://root@localhost:3306/9drive"
APP_PORT=4000
FRONTEND_URL="http://localhost:5173"
JWT_ACCESS_SECRET="change-this-jwt-secret-at-least-32-chars"
TOKEN_ENCRYPTION_KEY="change-this-encryption-key-32bytes!"
ACCESS_TOKEN_TTL_SECONDS=900
REFRESH_TOKEN_TTL_DAYS=30
MAX_UPLOAD_BYTES=5368709120
RECAPTCHA_SECRET_KEY=""
# Used only by `npm run seed:google-config`.
# These values are encrypted and stored in DB as global Google OAuth config.
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
GOOGLE_REDIRECT_URI="http://localhost:4000/connected-accounts/google/callback"
Important:
JWT_ACCESS_SECRET should be long and random.TOKEN_ENCRYPTION_KEY should be long and random.backend/.env.Create or confirm frontend/.env:
VITE_API_URL=http://localhost:4000
VITE_RECAPTCHA_SITE_KEY=
Captcha is disabled when VITE_RECAPTCHA_SITE_KEY or backend RECAPTCHA_SECRET_KEY is empty. Set both values to enable captcha on registration.
cd backend
npm run prisma:migrate
If Prisma client generation is blocked on Windows by a running Node process, stop running backend/frontend dev servers and run:
npx prisma generate
Google setup is done in Google Cloud Console, not Google Search Console. Google Search Console is for website indexing/search ownership. OAuth and Drive API are managed in Google Cloud Console.
Open Google Cloud Console:
https://console.cloud.google.com/
APIs & Services -> Library
Google Drive API
Google Drive API.Enable.Direct URL pattern:
https://console.developers.google.com/apis/api/drive.googleapis.com/overview?project=YOUR_PROJECT_ID
If Google Drive API is disabled, you will see an error like:
Google Drive API has not been used in project ... before or it is disabled.
APIs & Services -> OAuth consent screen
External
App name
User support email
Developer contact email
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
Full Drive access is required so Google sign-in can connect the first Drive account automatically and sync files manually added to the 9drive folder.
Testing, add test users.Add every Google account that will test the app:
OAuth consent screen -> Test users -> Add users
If you do not add test users, Google may show:
Access blocked: app has not completed the Google verification process
Error 403: access_denied
APIs & Services -> Credentials
Create Credentials -> OAuth client ID
Web application
http://localhost:5173
http://localhost:4000/connected-accounts/google/callback
Client ID
Client Secret
Put values into backend/.env:
GOOGLE_CLIENT_ID="your-client-id"
GOOGLE_CLIENT_SECRET="your-client-secret"
GOOGLE_REDIRECT_URI="http://localhost:4000/connected-accounts/google/callback"
Then run:
cd backend
npm run seed:google-config
This 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.
Start backend:
cd backend
npm run dev
Backend runs at:
http://localhost:4000
Start frontend:
cd frontend
npm run dev
Frontend runs at:
http://localhost:5173
This repository includes Docker files for running MySQL, backend, and frontend together.
Files:
docker-compose.yml
.env.docker.example
backend/Dockerfile
frontend/Dockerfile
frontend/nginx.conf
Copy the example env file:
cp .env.docker.example .env
On Windows PowerShell:
Copy-Item .env.docker.example .env
Edit .env:
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=9drive
FRONTEND_URL=http://localhost:5173
VITE_API_URL=http://localhost:4000
VITE_RECAPTCHA_SITE_KEY=
JWT_ACCESS_SECRET=replace-with-long-random-secret
TOKEN_ENCRYPTION_KEY=replace-with-long-random-secret
RECAPTCHA_SECRET_KEY=
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://localhost:4000/connected-accounts/google/callback
Captcha is disabled when either VITE_RECAPTCHA_SITE_KEY or RECAPTCHA_SECRET_KEY is empty.
docker compose up -d --build
Services:
frontend: http://localhost:5173
backend: http://localhost:4000
mysql: localhost:3306
The backend container runs Prisma migrations automatically on startup:
npm run db:migrate:deploy
This applies pending migrations such as S3 storage support before the API starts, so deployments from an older database can update safely without dropping data.
It also seeds the global Google OAuth config automatically when GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set to real values in .env. If those values are blank or still placeholders, the backend still starts and logs a warning. Google connect/sign-in will be unavailable until you set real Google OAuth credentials and restart the stack:
docker compose up -d --build
Automatic Docker startup seeding is usually enough. If you update Google OAuth values while containers are already running, seed the global Google OAuth config manually:
docker compose exec backend npm run seed:google-config
This stores GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URI from Docker env into MySQL as encrypted global config.
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f mysql
docker compose down
Remove database volume too:
docker compose down -v
JWT_ACCESS_SECRET and TOKEN_ENCRYPTION_KEY.VITE_API_URL changes because Vite embeds env at build time.VITE_RECAPTCHA_SITE_KEY changes because Vite embeds env at build time.Follow these steps to deploy 9Drive to a VPS (such as Ubuntu/Debian) using Docker:
sudo apt update
sudo apt install -y docker.io docker-compose
sudo systemctl enable --now docker
git clone https://github.com/your-github-username/9drive.git
cd 9drive
Copy the example environment file to .env:
cp .env.docker.example .env
Edit the .env file (e.g., nano .env) and configure the values for your production VPS domain/IP:
FRONTEND_URL: Set to your public domain or VPS IP (e.g., http://103.xxx.xxx.xxx:5173 or https://9drive.yourdomain.com).VITE_API_URL: Set to your public backend URL (e.g., http://103.xxx.xxx.xxx:4000 or https://api.9drive.yourdomain.com).GOOGLE_REDIRECT_URI: Set to your public redirect callback URL (e.g., http://103.xxx.xxx.xxx:4000/connected-accounts/google/callback).JWT_ACCESS_SECRET and TOKEN_ENCRYPTION_KEY (encryption key must be exactly 32 characters/bytes).GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET.Run Docker Compose to build and start the database, backend, and frontend containers in the background:
docker compose up -d --build
Initialize the encrypted Google configuration in the database:
docker compose exec backend npm run seed:google-config
http://your-vps-ip:5173 or https://9drive.yourdomain.com).http://your-vps-ip:4000/connected-accounts/google/callback or https://api.9drive.yourdomain.com/connected-accounts/google/callback).Run production migrations before starting the backend:
cd backend
npm run db:migrate:deploy
npm run start
Or use the combined command:
cd backend
npm run start:deploy
npm run db:migrate:deploy uses Prisma production migrations and does not reset the database. If Prisma reports migration drift, stop the deploy and repair migration history first; do not run prisma migrate reset on production.
http://localhost:5173
Continue with Google and connect Drive./settings already shows the connected account.Settings, click Connect Drive, approve access, and confirm the account appears.Quota Tracker.All Files.9drive.9drive, then click Sync Drive in All Files.View
Download
Rename
Move to Folder
Delete
Auth:
POST /auth/register
POST /auth/login
GET /auth/google/url
GET /auth/google/callback
POST /auth/google/exchange
POST /auth/refresh
POST /auth/logout
GET /auth/me
Google accounts:
GET /connected-accounts/google/connect-url
GET /connected-accounts/google/callback
GET /connected-accounts
POST /connected-accounts/:id/sync-quota
DELETE /connected-accounts/:id
Storage:
GET /storage/summary
Folders:
GET /folders
GET /folders/recent?limit=4
POST /folders
DELETE /folders/:id
Files:
GET /files
GET /files?folderId=<id>
GET /files?q=<search>
GET /files/shared-links
GET /files/:id
PATCH /files/:id
PATCH /files/batch
DELETE /files/batch
POST /files/sync-google
POST /files/:id/share
DELETE /files/:id/share
POST /files/:id/preview-token
GET /files/:id/view-url
GET /files/:id/download
DELETE /files/:id
GET /files/preview/:token
Uploads:
POST /uploads
Upload is multipart/form-data. Metadata fields should be appended before the file:
sizeBytes
fileName
mimeType
folderId optional
file
9drive.backend/.env is ignored by git.TOKEN_ENCRYPTION_KEY, JWT_ACCESS_SECRET, RECAPTCHA_SECRET_KEY, OAuth client secrets, or raw share/preview/handoff tokens.Instead of seeding Google credentials manually using npm run seed:google-config, you can set them up directly from the frontend dashboard:
https://103.65.237.136.nip.io:4000/connected-accounts/google/callback).The config is automatically encrypted and saved into the database, enabling Google sign-in and Google Drive connections instantly.
For native VPS setups running with PM2, 9Drive includes a fully automated system update trigger and log monitor in the Settings UI.
update.sh script in the background.git reset --hard) and pulls the latest changes.pm2 restart 9drive-backend).If you want to update manually via the terminal, run:
./update.sh
Or run the commands individually:
git reset --hard
git pull origin main
cd backend && npm install && npx prisma generate && npm run build && npx prisma migrate deploy
cd ../frontend && npm install && npm run build
pm2 restart 9drive-backend
Backend:
cd backend
npm run build
Frontend:
cd frontend
npm run build
248 followers · starred Jul 2026
1,258 followers · starred Jun 2026
17 followers · starred Jul 2026
8 followers · starred Jun 2026
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.
See the code
9Drive is a storage gateway web app for connecting multiple Google Drive accounts into one virtual storage dashboard. Users can register with email/password 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.
Copyright 2026 Zenhosta
Licensed under the Apache License, Version 2.0. See LICENSE for details.
9drive folder.POST /api/v1/uploads.9drive folder back into MySQL.update.sh directly from the Settings UI (PM2 setup).Live preview: https://9drive.zenhosta.com


backend/ Express API, Prisma schema, Google Drive integration
frontend/ Vite React app
Default database used by this project:
host: localhost
port: 3306
database: 9drive
user: root
password: empty
The easiest way to prepare the project is using the automated setup script. It generates local environment files with secure keys, installs dependencies, and generates the Prisma client. MySQL 8+ must already be running for local development; Docker setup includes MySQL.
Make sure to open PowerShell and navigate to the project directory first. For example, if you cloned the project to E:\AUTO KLIK\9Drive:
# 1. Switch to the drive where the project is located (if necessary)
E:
# 2. Navigate to the project folder
cd "E:\AUTO KLIK\9Drive"
# 3. Run the automated setup script
powershell -ExecutionPolicy Bypass -File .\setup.ps1
bash ./setup.sh
Enter your MySQL connection URL when prompted. Google Client ID/Secret can be skipped and configured later.
Once setup is complete, apply migrations and start backend and frontend in separate terminals:
cd backend
npm run prisma:migrate
npm run dev
cd frontend
npm run dev
If you prefer to configure the project manually:
Install backend dependencies:
cd backend
npm install
Install frontend dependencies:
cd ../frontend
npm install
Create a database:
CREATE DATABASE 9drive;
If using MySQL CLI:
mysql -u root -e "CREATE DATABASE IF NOT EXISTS 9drive;"
Create backend/.env:
DATABASE_URL="mysql://root@localhost:3306/9drive"
APP_PORT=4000
FRONTEND_URL="http://localhost:5173"
JWT_ACCESS_SECRET="change-this-jwt-secret-at-least-32-chars"
TOKEN_ENCRYPTION_KEY="change-this-encryption-key-32bytes!"
ACCESS_TOKEN_TTL_SECONDS=900
REFRESH_TOKEN_TTL_DAYS=30
MAX_UPLOAD_BYTES=5368709120
RECAPTCHA_SECRET_KEY=""
# Used only by `npm run seed:google-config`.
# These values are encrypted and stored in DB as global Google OAuth config.
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
GOOGLE_REDIRECT_URI="http://localhost:4000/connected-accounts/google/callback"
Important:
JWT_ACCESS_SECRET should be long and random.TOKEN_ENCRYPTION_KEY should be long and random.backend/.env.Create or confirm frontend/.env:
VITE_API_URL=http://localhost:4000
VITE_RECAPTCHA_SITE_KEY=
Captcha is disabled when VITE_RECAPTCHA_SITE_KEY or backend RECAPTCHA_SECRET_KEY is empty. Set both values to enable captcha on registration.
cd backend
npm run prisma:migrate
If Prisma client generation is blocked on Windows by a running Node process, stop running backend/frontend dev servers and run:
npx prisma generate
Google setup is done in Google Cloud Console, not Google Search Console. Google Search Console is for website indexing/search ownership. OAuth and Drive API are managed in Google Cloud Console.
Open Google Cloud Console:
https://console.cloud.google.com/
APIs & Services -> Library
Google Drive API
Google Drive API.Enable.Direct URL pattern:
https://console.developers.google.com/apis/api/drive.googleapis.com/overview?project=YOUR_PROJECT_ID
If Google Drive API is disabled, you will see an error like:
Google Drive API has not been used in project ... before or it is disabled.
APIs & Services -> OAuth consent screen
External
App name
User support email
Developer contact email
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
Full Drive access is required so Google sign-in can connect the first Drive account automatically and sync files manually added to the 9drive folder.
Testing, add test users.Add every Google account that will test the app:
OAuth consent screen -> Test users -> Add users
If you do not add test users, Google may show:
Access blocked: app has not completed the Google verification process
Error 403: access_denied
APIs & Services -> Credentials
Create Credentials -> OAuth client ID
Web application
http://localhost:5173
http://localhost:4000/connected-accounts/google/callback
Client ID
Client Secret
Put values into backend/.env:
GOOGLE_CLIENT_ID="your-client-id"
GOOGLE_CLIENT_SECRET="your-client-secret"
GOOGLE_REDIRECT_URI="http://localhost:4000/connected-accounts/google/callback"
Then run:
cd backend
npm run seed:google-config
This 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.
Start backend:
cd backend
npm run dev
Backend runs at:
http://localhost:4000
Start frontend:
cd frontend
npm run dev
Frontend runs at:
http://localhost:5173
This repository includes Docker files for running MySQL, backend, and frontend together.
Files:
docker-compose.yml
.env.docker.example
backend/Dockerfile
frontend/Dockerfile
frontend/nginx.conf
Copy the example env file:
cp .env.docker.example .env
On Windows PowerShell:
Copy-Item .env.docker.example .env
Edit .env:
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=9drive
FRONTEND_URL=http://localhost:5173
VITE_API_URL=http://localhost:4000
VITE_RECAPTCHA_SITE_KEY=
JWT_ACCESS_SECRET=replace-with-long-random-secret
TOKEN_ENCRYPTION_KEY=replace-with-long-random-secret
RECAPTCHA_SECRET_KEY=
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://localhost:4000/connected-accounts/google/callback
Captcha is disabled when either VITE_RECAPTCHA_SITE_KEY or RECAPTCHA_SECRET_KEY is empty.
docker compose up -d --build
Services:
frontend: http://localhost:5173
backend: http://localhost:4000
mysql: localhost:3306
The backend container runs Prisma migrations automatically on startup:
npm run db:migrate:deploy
This applies pending migrations such as S3 storage support before the API starts, so deployments from an older database can update safely without dropping data.
It also seeds the global Google OAuth config automatically when GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set to real values in .env. If those values are blank or still placeholders, the backend still starts and logs a warning. Google connect/sign-in will be unavailable until you set real Google OAuth credentials and restart the stack:
docker compose up -d --build
Automatic Docker startup seeding is usually enough. If you update Google OAuth values while containers are already running, seed the global Google OAuth config manually:
docker compose exec backend npm run seed:google-config
This stores GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URI from Docker env into MySQL as encrypted global config.
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f mysql
docker compose down
Remove database volume too:
docker compose down -v
JWT_ACCESS_SECRET and TOKEN_ENCRYPTION_KEY.VITE_API_URL changes because Vite embeds env at build time.VITE_RECAPTCHA_SITE_KEY changes because Vite embeds env at build time.Follow these steps to deploy 9Drive to a VPS (such as Ubuntu/Debian) using Docker:
sudo apt update
sudo apt install -y docker.io docker-compose
sudo systemctl enable --now docker
git clone https://github.com/your-github-username/9drive.git
cd 9drive
Copy the example environment file to .env:
cp .env.docker.example .env
Edit the .env file (e.g., nano .env) and configure the values for your production VPS domain/IP:
FRONTEND_URL: Set to your public domain or VPS IP (e.g., http://103.xxx.xxx.xxx:5173 or https://9drive.yourdomain.com).VITE_API_URL: Set to your public backend URL (e.g., http://103.xxx.xxx.xxx:4000 or https://api.9drive.yourdomain.com).GOOGLE_REDIRECT_URI: Set to your public redirect callback URL (e.g., http://103.xxx.xxx.xxx:4000/connected-accounts/google/callback).JWT_ACCESS_SECRET and TOKEN_ENCRYPTION_KEY (encryption key must be exactly 32 characters/bytes).GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET.Run Docker Compose to build and start the database, backend, and frontend containers in the background:
docker compose up -d --build
Initialize the encrypted Google configuration in the database:
docker compose exec backend npm run seed:google-config
http://your-vps-ip:5173 or https://9drive.yourdomain.com).http://your-vps-ip:4000/connected-accounts/google/callback or https://api.9drive.yourdomain.com/connected-accounts/google/callback).Run production migrations before starting the backend:
cd backend
npm run db:migrate:deploy
npm run start
Or use the combined command:
cd backend
npm run start:deploy
npm run db:migrate:deploy uses Prisma production migrations and does not reset the database. If Prisma reports migration drift, stop the deploy and repair migration history first; do not run prisma migrate reset on production.
http://localhost:5173
Continue with Google and connect Drive./settings already shows the connected account.Settings, click Connect Drive, approve access, and confirm the account appears.Quota Tracker.All Files.9drive.9drive, then click Sync Drive in All Files.View
Download
Rename
Move to Folder
Delete
Auth:
POST /auth/register
POST /auth/login
GET /auth/google/url
GET /auth/google/callback
POST /auth/google/exchange
POST /auth/refresh
POST /auth/logout
GET /auth/me
Google accounts:
GET /connected-accounts/google/connect-url
GET /connected-accounts/google/callback
GET /connected-accounts
POST /connected-accounts/:id/sync-quota
DELETE /connected-accounts/:id
Storage:
GET /storage/summary
Folders:
GET /folders
GET /folders/recent?limit=4
POST /folders
DELETE /folders/:id
Files:
GET /files
GET /files?folderId=<id>
GET /files?q=<search>
GET /files/shared-links
GET /files/:id
PATCH /files/:id
PATCH /files/batch
DELETE /files/batch
POST /files/sync-google
POST /files/:id/share
DELETE /files/:id/share
POST /files/:id/preview-token
GET /files/:id/view-url
GET /files/:id/download
DELETE /files/:id
GET /files/preview/:token
Uploads:
POST /uploads
Upload is multipart/form-data. Metadata fields should be appended before the file:
sizeBytes
fileName
mimeType
folderId optional
file
9drive.backend/.env is ignored by git.TOKEN_ENCRYPTION_KEY, JWT_ACCESS_SECRET, RECAPTCHA_SECRET_KEY, OAuth client secrets, or raw share/preview/handoff tokens.Instead of seeding Google credentials manually using npm run seed:google-config, you can set them up directly from the frontend dashboard:
https://103.65.237.136.nip.io:4000/connected-accounts/google/callback).The config is automatically encrypted and saved into the database, enabling Google sign-in and Google Drive connections instantly.
For native VPS setups running with PM2, 9Drive includes a fully automated system update trigger and log monitor in the Settings UI.
update.sh script in the background.git reset --hard) and pulls the latest changes.pm2 restart 9drive-backend).If you want to update manually via the terminal, run:
./update.sh
Or run the commands individually:
git reset --hard
git pull origin main
cd backend && npm install && npx prisma generate && npm run build && npx prisma migrate deploy
cd ../frontend && npm install && npm run build
pm2 restart 9drive-backend
Backend:
cd backend
npm run build
Frontend:
cd frontend
npm run build
248 followers · starred Jul 2026
1,258 followers · starred Jun 2026
17 followers · starred Jul 2026
8 followers · starred Jun 2026