├── .gitignore ├── .idea ├── .gitignore ├── jsLibraryMappings.xml ├── misc.xml ├── modules.xml ├── onedex.iml └── vcs.xml ├── .vscode ├── extensions.json └── launch.json ├── Engin.ts ├── README.md ├── astro.config.mjs ├── oneindexconf.ts ├── package-lock.json ├── package.json ├── public ├── favicon.svg ├── list.png ├── ss1.png └── ss2.png ├── src ├── component │ └── drive │ │ └── video.jsx ├── env.d.ts ├── layout │ └── public.astro └── pages │ ├── api │ ├── download.ts │ └── stream.ts │ ├── drive │ └── [path].astro │ └── index.astro ├── tailwind.config.mjs └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | .idea/ 6 | # dependencies 7 | node_modules/ 8 | one.ipynb 9 | req.py 10 | test.json 11 | 12 | # logs 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | pnpm-debug.log* 17 | 18 | 19 | # environment variables 20 | .env 21 | .env.production 22 | 23 | # macOS-specific files 24 | .DS_Store 25 | .wrangler/ 26 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/onedex.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Engin.ts: -------------------------------------------------------------------------------- 1 | import { AllDrivers } from "./oneindexconf"; 2 | 3 | export interface DriveItems { 4 | name: string; 5 | id: string; 6 | size: number; 7 | type: string; 8 | data?: any; 9 | thumbnail?: string; 10 | } 11 | export interface Driverer { 12 | List(id?: string): Promise; // id is optional 13 | Search(name: string): Promise; 14 | GetFile(id: string, request: any): Promise; 15 | FetchAccessToken( 16 | client_id, 17 | client_secret, 18 | redirect_uri, 19 | token_url, 20 | refreshToken 21 | ): Promise; 22 | } 23 | 24 | interface config { 25 | ClientID: string; 26 | ClientSecret: string; 27 | redirect_url?: string; 28 | RefreshToken: string; 29 | } 30 | export interface DriverConfig { 31 | name: string; 32 | path: string; 33 | type: "onedrive" | "googledrive"; 34 | config: config; 35 | } 36 | 37 | // get driver by path 38 | export async function GetDriver(path: string): Promise { 39 | const driver = AllDrivers.find((d) => { 40 | return d.path === path; 41 | }); 42 | if (!driver) { 43 | throw new Error("Driver not found"); 44 | } 45 | 46 | switch (driver.type) { 47 | case "onedrive": 48 | const od = new OneDrive(); 49 | await od.FetchAccessToken( 50 | driver.config.ClientID, 51 | driver.config.ClientSecret, 52 | driver.config.redirect_url, 53 | "https://login.microsoftonline.com/common/oauth2/v2.0/token", 54 | driver.config.RefreshToken 55 | ); 56 | return od; 57 | default: 58 | throw new Error("Driver not found"); 59 | } 60 | } 61 | 62 | class OneDrive implements Driverer { 63 | private accessToken: string; 64 | public async FetchAccessToken( 65 | client_id: string, 66 | client_secret: string, 67 | redirect_uri: string, 68 | token_url: string, 69 | refreshToken: string 70 | ): Promise { 71 | try { 72 | const req = await fetch(token_url, { 73 | method: "POST", 74 | body: `client_id=${client_id}&scope=offline_access%20Files.ReadWrite.All&refresh_token=${refreshToken}&redirect_uri=${redirect_uri}&grant_type=refresh_token&client_secret=${client_secret}`, 75 | headers: { 76 | "Content-Type": "application/x-www-form-urlencoded", 77 | }, 78 | }); 79 | 80 | if (!req.ok) { 81 | throw new Error(`Request failed with status ${req.status}`); 82 | } 83 | 84 | const data = await req.json(); 85 | this.accessToken = data.access_token; 86 | console.log("access token", data); 87 | 88 | return data.access_token; 89 | } catch (error) { 90 | console.error("Error fetching access token:", error); 91 | throw error; 92 | } 93 | } 94 | 95 | async List(id?: string): Promise { 96 | let url = ""; 97 | if (id) { 98 | url = `https://graph.microsoft.com/v1.0/me/drive/items/${id}/children?$top=5000&$expand=thumbnails($select=medium)&$select=id,name,size,lastModifiedDateTime,content.downloadUrl,file,parentReference`; 99 | } else { 100 | url = 101 | "https://graph.microsoft.com/v1.0/me/drive/root/children?$top=5000&$expand=thumbnails($select=medium)&$select=id,name,size,lastModifiedDateTime,content.downloadUrl,file,parentReference"; 102 | } 103 | const request = await fetch(url, { 104 | headers: { 105 | Authorization: `Bearer ${this.accessToken}`, 106 | }, 107 | }); 108 | 109 | const data = await request.json(); 110 | 111 | // return data.value.map((item: any) => { 112 | // return { 113 | // name: item.name, 114 | // size: item.size, 115 | // id: item.id, 116 | // type: item.file ? item.file.mimeType : "folder", 117 | // thumbnail: 118 | // item.thumbnails && item.thumbnails.length > 0 119 | // ? item.thumbnails[0].medium.url 120 | // : null, 121 | // date: item.lastModifiedDateTime, 122 | // }; 123 | // }); 124 | // sort by new date 125 | const sorted = data.value.map((item: any) => { 126 | return { 127 | name: item.name, 128 | size: item.size, 129 | id: item.id, 130 | type: item.file ? item.file.mimeType : "folder", 131 | thumbnail: 132 | item.thumbnails && item.thumbnails.length > 0 133 | ? item.thumbnails[0].medium.url 134 | : null, 135 | data: item, 136 | }; 137 | }); 138 | sorted.sort((a, b) => { 139 | return ( 140 | +new Date(b.data.lastModifiedDateTime) - 141 | +new Date(a.data.lastModifiedDateTime) 142 | ); 143 | }); 144 | return sorted; 145 | } 146 | 147 | async GetFile(id: string, request: any): Promise { 148 | const url = `https://graph.microsoft.com/v1.0/me/drive/items/${id}/content`; 149 | const myheaders = { 150 | Authorization: `Bearer ${this.accessToken}`, 151 | }; 152 | try { 153 | if (request.headers.get("Range" || "range")) { 154 | console.log("got partial content request"); 155 | myheaders["Range"] = request.headers.get("Range" || "range"); 156 | const response = await fetch(url, { headers: myheaders }); 157 | const contentRange = response.headers.get("Content-Range"); 158 | const contentLength = response.headers.get("Content-Length"); 159 | const disposition = response.headers.get("content-disposition"); 160 | const filename = decodeURIComponent( 161 | disposition 162 | .substring(disposition.indexOf("utf-8") + 7) 163 | .replace(/['"]/g, "") 164 | .replace(";", "") 165 | .replace("filename=", "") 166 | ); 167 | return new Response(response.body, { 168 | status: 206, // Partial Content 169 | headers: { 170 | "Content-Type": "application/octet-stream", 171 | "Content-Disposition": `attachment; filename="${filename}"`, 172 | "Content-Range": contentRange, 173 | "Content-Length": contentLength, 174 | "Accept-Ranges": "bytes", 175 | }, 176 | }); 177 | } else { 178 | const Head = new Headers(); 179 | Head.set("Authorization", `Bearer ${this.accessToken}`); 180 | try { 181 | return await fetch(url, { headers: Head }); 182 | } catch (error) { 183 | return new Response("api limited, cant handle request ", { 184 | status: 500, 185 | }); 186 | } 187 | } 188 | } catch (error) { 189 | return new Response("api limited, cant handle request ", { 190 | status: 500, 191 | }); 192 | } 193 | } 194 | 195 | Search(name: string): Promise { 196 | return Promise.resolve([]); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OneIndex 2 | 3 | ![Image](https://github.com/mahbubmaruf178/one-index/blob/master/public/list.png?raw=true) 4 | This project allows you to create an index for your OneDrive,Gdrive,Pcloud.. etc files using Cloudflare Workers. With this index, you can download files using Cloudflare's reverse proxy, open files with an Android app, and enjoy resume download support. 5 | 6 | ## Installation 7 | 8 | ## How to deploy a site with GitHub 9 | 10 | - Fork this repository to your GitHub account. 11 | - Edit oneindexconf.ts file. 12 | 13 | { 14 | name: "onedrive",// any name you want 15 | path: "onedrive", //must be unique 16 | type: "onedrive", // onedrive, gdrive, pcloud, etc 17 | config: { 18 | redirect_url: "your redirect url here", 19 | ClientID: "your_client_id_here", 20 | ClientSecret: "your_client_secret_here", 21 | RefreshToken: "your_refresh_token_here", 22 | }, 23 | }, 24 | 25 | - Set up a new project on Cloudflare Pages. 26 | - Log in to the Cloudflare dashboard and select your account in Account Home > Pages. 27 | - Select Create a new Project and the Connect Git option. 28 | - Select the git project you want to deploy and click Begin setup 29 | - Use the following build settings: 30 | - Framework preset: Astro 31 | - Build command: npm run build 32 | - Build output directory: dist 33 | - Click the Save and Deploy button. 34 | 35 | ## Features 36 | 37 | - Download files with Cloudflare reverse proxy. 38 | - Open files with Android app and in windows PotPlayer.(for video files) 39 | - Resume download supported. 40 | - multi drive supported. 41 | 42 | ## Upcoming Features 43 | 44 | We have some exciting features in the pipeline: 45 | 46 | 1. Search for files. 47 | 2. Admin authentication for added security and control. 48 | 49 | ## Screenshots 50 | 51 | ![Image](https://github.com/mahbubmaruf178/one-index/blob/master/public/ss1.png?raw=true) 52 | ![Image](https://github.com/mahbubmaruf178/one-index/blob/master/public/ss2.png?raw=true) 53 | 54 | ## Contributing 55 | 56 | Contributions are welcome! Feel free to open issues and pull requests to help improve this project. 57 | 58 | ## License 59 | 60 | This project is licensed under the [MIT License](LICENSE). 61 | 62 | --- 63 | 64 | **Disclaimer:** This project is not affiliated with Microsoft or OneDrive. Please use it responsibly and in compliance with the terms of service of the services you are using. 65 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | import cloudflare from "@astrojs/cloudflare"; 3 | import tailwind from "@astrojs/tailwind"; 4 | 5 | import react from "@astrojs/react"; 6 | 7 | // https://astro.build/config 8 | export default defineConfig({ 9 | output: "server", 10 | adapter: cloudflare(), 11 | integrations: [tailwind(), react()] 12 | }); -------------------------------------------------------------------------------- /oneindexconf.ts: -------------------------------------------------------------------------------- 1 | import type { DriverConfig } from "./Engin"; 2 | 3 | export const AllDrivers: DriverConfig[] = [ 4 | { 5 | name: "onedrive", 6 | path: "onedrive", 7 | type: "onedrive", 8 | config: { 9 | redirect_url: "http://localhost:3000", 10 | ClientID: "dd8b4a2e-5bad-4256-b0f9-c360b51fde67", 11 | ClientSecret: "qoC8Q~pd8sZBIu48TFIfSzCP.9adpNI5YaWxMbqj", 12 | RefreshToken: "your_refresh_token_here", 13 | }, 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "one-index", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev --host", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro", 11 | "pages:dev": "wrangler pages dev --compatibility-date=2023-10-14 -- astro dev", 12 | "pages:deploy": "astro build && wrangler pages deploy ./dist" 13 | }, 14 | "dependencies": { 15 | "@astrojs/cloudflare": "^10.2.4", 16 | "@astrojs/react": "^3.3.1", 17 | "@astrojs/tailwind": "^5.1.0", 18 | "@types/react": "^18.3.1", 19 | "@types/react-dom": "^18.3.0", 20 | "astro": "^4.7.0", 21 | "axios": "^1.6.3", 22 | "react": "^18.3.1", 23 | "react-dom": "^18.3.1", 24 | "tailwindcss": "^3.3.3" 25 | }, 26 | "devDependencies": { 27 | "daisyui": "^4.4.24", 28 | "wrangler": "^3.13.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /public/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahbubmaruf178/one-index/a071b09a5f5c943dcba9bc6f172bdc33eb1732de/public/list.png -------------------------------------------------------------------------------- /public/ss1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahbubmaruf178/one-index/a071b09a5f5c943dcba9bc6f172bdc33eb1732de/public/ss1.png -------------------------------------------------------------------------------- /public/ss2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahbubmaruf178/one-index/a071b09a5f5c943dcba9bc6f172bdc33eb1732de/public/ss2.png -------------------------------------------------------------------------------- /src/component/drive/video.jsx: -------------------------------------------------------------------------------- 1 | export function VideoView({ item, path }) { 2 | const Origin = window.location.origin; 3 | return ( 4 |
5 |
6 | {item.thumbnail && ( 7 | {item.name} 12 | )} 13 | 14 | {item.name} 15 | 16 |
17 | 23 | 24 | 25 | 26 | 32 | 33 | 34 | 35 | 41 | 42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /src/layout/public.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title } = Astro.props ; 3 | --- 4 | 5 | 6 | 7 | 8 | {title} 9 | 16 | 17 | 18 | 19 | 34 |
35 | 36 |
37 | 38 | -------------------------------------------------------------------------------- /src/pages/api/download.ts: -------------------------------------------------------------------------------- 1 | import { GetDriver } from "../../../Engin.ts"; 2 | export async function GET({ request }) { 3 | const url = new URL(request.url); 4 | const id = url.searchParams.get("id"); 5 | const path = url.searchParams.get("path"); 6 | const driver = await GetDriver(path); 7 | return driver.GetFile(id, request); 8 | } 9 | -------------------------------------------------------------------------------- /src/pages/api/stream.ts: -------------------------------------------------------------------------------- 1 | export async function GET({ params, request }) { 2 | const clientUrl = new URL(request.url); 3 | const url = clientUrl.searchParams.get("url"); 4 | const myheaders = {}; 5 | let filename: string = ""; 6 | let contentRange: string = ""; 7 | let contentLength: string = ""; 8 | 9 | try { 10 | const rangeHeader = 11 | request.headers.get("Range") || request.headers.get("range"); 12 | if (rangeHeader) { 13 | // console.log("got partial content request"); 14 | myheaders["Range"] = rangeHeader; 15 | } 16 | 17 | const response = await fetch(url, { headers: myheaders }); 18 | contentRange = response.headers.get("Content-Range"); 19 | contentLength = response.headers.get("Content-Length"); 20 | 21 | if (response.headers.has("content-disposition")) { 22 | const contentDisposition = response.headers.get("content-disposition"); 23 | const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; 24 | const matches = filenameRegex.exec(contentDisposition); 25 | if (matches != null && matches[1]) { 26 | filename = matches[1].replace(/['"]/g, ""); 27 | } 28 | } else { 29 | // get filename from url 30 | filename = url.substring(url.lastIndexOf("/") + 1); 31 | } 32 | 33 | return new Response(response.body, { 34 | status: rangeHeader ? 206 : 200, // Partial Content or OK 35 | headers: { 36 | "Content-Type": "application/octet-stream", 37 | "Content-Disposition": `attachment; filename="${filename}"`, 38 | "Content-Range": contentRange, 39 | "Content-Length": contentLength, 40 | "Accept-Ranges": "bytes", 41 | }, 42 | }); 43 | } catch (error) { 44 | console.log("error", error); 45 | return new Response("internal error", { 46 | status: 500, 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/pages/drive/[path].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { GetDriver, type DriveItems } from "../../../Engin"; 3 | import { VideoView } from "../../component/drive/video"; 4 | import Public from "../../layout/public.astro"; 5 | 6 | const url = new URLSearchParams(Astro.params.url); 7 | const { path } = Astro.params; 8 | const driver = await GetDriver(path); 9 | const id = Astro.url.searchParams.get("id"); 10 | let items: DriveItems[]; 11 | let Origin = Astro.url.origin; 12 | if (id) { 13 | items = await driver.List(id); 14 | } else { 15 | items = await driver.List(); 16 | } 17 | --- 18 | 19 | 20 |
21 | {console.log(items)} 22 | { 23 | items.map((item: DriveItems, index) => ( 24 | <> 25 | {item.type === "folder" ? ( 26 | 30 | 31 | 32 | {item.name} 33 | 34 | 35 | {(item.size / 1024 / 1024).toFixed(2)} mb 36 | 37 | 38 | ) : item.type.includes("video") ? ( 39 | 40 | ) : ( 41 | 45 | 46 | {item.name}`` 47 | 48 | )} 49 | 50 | )) 51 | } 52 |
53 |
54 | 55 | 67 | -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { AllDrivers } from "../../oneindexconf"; 3 | import Public from "../layout/public.astro"; 4 | --- 5 | 6 | 7 |
8 | { 9 | AllDrivers.map((driver, index) => ( 10 | 14 |
15 | 16 | {driver.path} 17 |
18 |
19 | )) 20 | } 21 |
22 |
23 | -------------------------------------------------------------------------------- /tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | daisyui: { 9 | themes: ["light", "dark", "cupcake"], 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/base", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "react" 6 | } 7 | } --------------------------------------------------------------------------------