A compact tool for turning OneDrive, iCloud Drive, and Google Drive public sharing links into permanent links, with optional API access to the current temporary download URL.
- Accepted Inputs: public sharing links from
onedrive.live.com,1drv.ms,sharepoint.com,icloud.com/iclouddrive, anddrive.google.com - Official Microsoft Resolution Flow: prefers Shares API first, then falls back to Badger token, SharePoint drive bootstrap, or the official download endpoint when needed
- Official Apple Resolution Flow: resolves iCloud Drive public shares through Apple's public CloudKit
records/resolveendpoint and extracts the current asset download URL from the returned shared record - Google Drive Public Share Flow: resolves Google Drive public file shares through the public share page metadata and extracts the current download URL plus file information
- Clean Web UI: generate, open, and copy permanent links directly from the page
- Optional File Extension Input: keep the public link clean while preserving a readable suffix when needed
- API Resolution:
/api/resolvereturns the current download URL plus file metadata - Unified Domain: backend provides the permanent-link domain without extra frontend configuration
- Dual Deployment: Cloudflare Worker or Docker self-hosting
Note: this is not a full OneDrive API client. It implements the Microsoft-owned resolution path needed specifically for public file-share download links. For SharePoint Personal public links, it tries Shares API first; if the raw short link is rejected as an invalid shares key, it falls back to the anonymous share page's bootstrap data (
driveUrl+driveAccessToken) and resolves the file through Microsoft's_api/v2.0/drives/...endpoint. Some short links may also use Microsoft's officialdownload.aspxendpoint first.
Deploy on any system that supports Docker (cloud server, local PC, NAS, Raspberry Pi, etc.):
# 1) Clone the repo, or extract the project archive first
git clone https://github.com/Shisly/OpenDLink.git
cd OpenDLink
# 2) Copy the example env file
cp .env.example .env
# Windows PowerShell:
# Copy-Item .env.example .env
# 3) Adjust .env if needed, then build and start
docker compose up -d --buildOr use pre-built image directly:
docker run -d -p 3000:3000 ghcr.io/shisly/opendlink:latestUse this when you want to deploy the whole service to Cloudflare, or when you want a dedicated Google Drive proxy Worker.
# 1) Clone the repo, or extract the project archive first
git clone https://github.com/Shisly/OpenDLink.git
cd OpenDLink
# 2) Install dependencies (Node.js 20+ required)
npm install
# 3) Build and validate the Worker bundle
npm run verifyThen deploy the generated dist/cloudflare-worker/worker.js file in Cloudflare Dashboard.
.
├── dist/ # Generated runtime artifacts for Docker and Worker deployment
├── public/ # Frontend page
├── scripts/ # Build and verification scripts
├── src/ # TypeScript source for server / worker / shared core
├── Dockerfile
├── docker-compose.yml
└── README.md
Notes:
- In this project,
dist/is a release artifact, not a disposable cache directory, so it is intentionally committed - Docker builds use
dist/server.jsdirectly - Cloudflare Worker deployment uses
dist/cloudflare-worker/worker.jsdirectly - Each build overwrites the corresponding files under
dist/; it does not keep appending extra generated copies
- Docker Engine is installed
- Docker Compose plugin is installed and
docker compose versionworks - Your terminal is currently inside the project root, the directory that contains
docker-compose.yml
public/index.htmldist/server.jsdist/shared/opendlink-core.jspackage.jsonpackage-lock.jsonDockerfiledocker-compose.yml
-
Download/clone the project and enter the project root:
git clone https://github.com/Shisly/OpenDLink.git cd OpenDLinkIf you downloaded a ZIP instead:
- Extract it first
- Enter the extracted folder
- Make sure
docker-compose.ymlis present in the current directory
-
Copy the example environment file:
cp .env.example .env
Windows PowerShell:
Copy-Item .env.example .env -
Edit
.envand at least set your public domain:DOMAIN=https://opendlink.your-domain.com GOOGLE_PROXY_DOMAIN=https://your-google-proxy.workers.dev # Optional: # HOST_PORT=3000 # CORS_ORIGIN=* # LOG=true
Notes:
docker composeautomatically reads the.envfile from the project root- Values such as
DOMAIN,GOOGLE_PROXY_DOMAIN, andHOST_PORTare first consumed bydocker-compose.yml, then passed into the container GOOGLE_PROXY_DOMAINdrives the frontend "Cloudflare Proxy" button for Google Drive- It should point to a separately deployed Cloudflare Worker proxy domain
- If left empty, the frontend will hide that button
-
If you changed source files such as
src/server.tsorsrc/shared/*, regenerate runtime artifacts first:npm install npm run build:runtime
Notes:
- The Docker image packages the
dist/directory directly - If you only edit source files but do not rebuild
dist/, the container will not include your latest code
- The Docker image packages the
-
Start the service:
docker compose up -d --build
-
Check status:
docker compose ps docker compose logs -f
-
Verify locally first:
- Open
http://localhost:3000in your browser - If you set
HOST_PORT=8080, openhttp://localhost:8080 - If you configured reverse proxy and HTTPS, then visit your final public domain instead
- Open
server {
listen 80;
listen 443 ssl;
server_name opendlink.your-domain.com;
# SSL certificates (use certbot)
# ssl_certificate /etc/letsencrypt/live/opendlink.your-domain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/opendlink.your-domain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}certbot --nginx -d opendlink.your-domain.com
nginx -s reloadNotes:
- If your host port is not
3000, updateproxy_pass http://127.0.0.1:3000;to match theHOST_PORTvalue in.env - After Nginx is in front,
DOMAINin.envshould be your final public HTTPS domain
| Path | Description |
|---|---|
GET / |
Frontend page for generating permanent links |
GET /health |
Health check endpoint |
GET /api/config |
Returns backend domain config |
GET /api/resolve?shareUrl=... |
Returns the current download URL and file metadata through the public-share flow for OneDrive, iCloud Drive, or Google Drive |
GET /1drv/{base64}.{ext} |
OneDrive / SharePoint permanent link. Resolves the latest upstream download URL and redirects to it |
GET /icloud/{base64}.{ext} |
iCloud permanent link. Resolves the latest upstream download URL and proxies the file stream with filename headers |
GET /google/{base64}.{ext} |
Google Drive permanent link. Resolves the latest upstream download URL and redirects to it |
GET /google/proxy/{base64}.{ext} |
Google Drive proxy permanent link. Resolves the latest upstream download URL and proxies the file stream |
- Node.js 20 or newer is installed
- npm is installed
- You have a Cloudflare account
- Your terminal is currently inside the project root
src/cloudflare-worker/worker.template.tsis the Worker template sourcesrc/shared/opendlink-core.tsis the shared resolver sourcedist/cloudflare-worker/worker.template.jsis the generated runtime templatedist/cloudflare-worker/worker.jsis the final deployable file- Do NOT manually edit anything under
dist/ - After modifying
public/index.html,src/cloudflare-worker/worker.template.ts, orsrc/shared/opendlink-core.ts, rebuild and validate
cd OpenDLink
npm install
npm run build:worker
npm run validate:workerFull verification:
npm run verifyvalidate:worker checks:
- No
__HTML_PLACEHOLDER__remaining indist/cloudflare-worker/worker.js - ESM syntax is valid
default export.fetchexistsGET /api/configandGET /pass smoke tests
-
Enter the project root:
cd OpenDLink -
Install dependencies:
npm install
-
If you modified pages or templates:
npm run build:worker npm run validate:worker
For a full end-to-end check, you can run:
npm run verify
-
Open
dist/cloudflare-worker/worker.js -
Copy the full file into the Cloudflare Worker editor, then adjust the top config if needed:
const CONFIG = { WORKER_DOMAIN: '', // Leave empty for auto-detect, or set custom domain GOOGLE_PROXY_DOMAIN: '', // Leave empty to hide the Google Drive Cloudflare proxy button CORS_ORIGIN: '*', };
Notes:
WORKER_DOMAINcan stay empty if the Worker should use the current request host automatically- If you use a custom domain, write the full URL such as
https://opendlink.your-domain.com GOOGLE_PROXY_DOMAINshould point to the Worker domain dedicated to Google Drive proxy downloads- If this Worker itself is the dedicated Google Drive proxy site,
WORKER_DOMAINandGOOGLE_PROXY_DOMAINcan be the same domain - Always use full
https://...URLs here, not bare hostnames
-
Login to Cloudflare Dashboard
-
Go to
Workers & Pages→Create→Worker -
Paste the full content of
dist/cloudflare-worker/worker.js, then clickSave and Deploy -
For a custom domain, open Worker settings and configure:
Triggers→Add Custom Domain -
After deployment, verify these URLs first:
https://your-worker-domain/https://your-worker-domain/api/config- If this Worker is a Google Drive proxy site, also test:
https://your-worker-domain/google/proxy/{base64}.{ext}
https://your-worker.workers.dev/1drv/{base64ShareUrl}.mp4
https://your-worker.workers.dev/icloud/{base64ShareUrl}.mp4
https://your-worker.workers.dev/google/{base64ShareUrl}.mp4
https://your-worker.workers.dev/google/proxy/{base64ShareUrl}.mp4
| File | Purpose |
|---|---|
src/cloudflare-worker/worker.template.ts |
Worker template source |
src/shared/opendlink-core.ts |
Shared resolver source for Node and Worker |
public/index.html |
Frontend page source |
dist/cloudflare-worker/worker.template.js |
Runtime template generated by scripts/build-runtime.js |
dist/cloudflare-worker/worker.js |
Generated deployable file |
scripts/build-worker.js |
Builds runtime artifacts, injects public/index.html, and emits the Worker bundle |
scripts/validate-worker.js |
Syntax and route validation |
User Browser Backend (Worker/Docker) Microsoft Official Endpoints
│ │ │
│── Open Page ──────────────>│ │
│<─ Return HTML ─────────────│ │
│ │ │
│── GET /api/config ────────>│ │
│<─ { domain: "..." } ───────│ │
│ │ │
│── GET /api/resolve ───────>│ │
│ │── Call official flow ───>│
│ │ (Prefer Shares API) │
│ │ (If 401/403, request │
│ │ official Badger token │
│ │ and retry) │
│ │ (If short link is │
│ │ incompatible, switch │
│ │ to official │
│ │ download.aspx or │
│ │ share-page Drive flow)│
│ │<─ Current CDN URL / file │
│<─ Resolution result JSON ──│ │
│ (Page generates permanent link; API can return temp)│
│ │ │
│── GET /1drv/{base64}.mp4 ──────>│ │
│ │── Call official flow ───>│
│ │<─ Current CDN URL ───────│
│<─ 302 → CDN URL ───────────│ │
│ (Permanent Link: External URL stays constant) │
Q: Why can't I get the direct link?
A: Make sure the sharing link is set to "Anyone with the link can view" (public sharing), not password-protected or organization-only.
Q: Why do some public 1drv.ms links that used to return 401/403 work now?
A: Some of those links have actually been migrated to Microsoft's SharePoint Personal backend. Anonymous Shares API access is rejected there, but Microsoft's own frontend first obtains a Badger token and then retries the same Shares API. This project now follows that Microsoft API flow directly.
Q: Why can a 1drv.ms link resolve, but a my.sharepoint.com/:u:/g/personal/... link may fail if handled the same way?
A: 1drv.ms can still be treated by Microsoft's Shares API as a valid sharing URL key, so Badger retry is enough. The newer SharePoint Personal short link is different: the raw short URL is often rejected by Shares API as Invalid shares key. Microsoft’s share page then bootstraps an anonymous driveUrl and driveAccessToken, and uses the SharePoint/OneDrive drive API to fetch the same @content.downloadUrl. This project now follows that same fallback path.
Q: Does this tool implement the entire OneDrive API?
A: No. It only implements the official Microsoft path needed for “public file share -> downloadable URL” resolution. Shares API, Badger token, SharePoint share-page bootstrap, Drive API, and some official download endpoints are in scope. Upload, folder browsing, account auth, and broader OneDrive client features are not.
Q: How long is the temporary link returned by the API valid? A: Usually about 1 hour, controlled by OneDrive CDN signature. You need to re-fetch after expiration.
Q: Is the permanent link really permanent? A: More precisely, it is a permanent public entry rather than an immutable CDN URL. As long as the original sharing link remains valid, the permanent link resolves a fresh downloadable address on each visit, so the published URL can remain constant.
Q: Why not manually edit worker.js?
A: Embedding full HTML into Worker can easily introduce escaping or comment closure issues. The safest approach is to maintain src/cloudflare-worker/worker.template.ts, src/shared/opendlink-core.ts, and public/index.html, then generate the runtime files via script.
Q: Are performance requirements high for self-hosting?
A: Very low. The backend only calls the API once and returns a 302. Actual file traffic is still handled directly by OneDrive CDN. You can run it on a low-end VPS, Raspberry Pi, or even your local machine.
MIT License © 2026 Shisly