├── .gitignore ├── README.md ├── background.js ├── docs └── tutorial.png ├── icon.png ├── manifest.json ├── popup.html └── popup.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 115ToAlist 2 | 3 | Chrome插件,自动同步115 cookie到alist 4 | 5 | ## 使用方法 6 | 7 | 1. repo右上角 `Code` > `Download ZIP`,文件夹解压到本地 8 | 2. 打开Chrome插件开发者模式,地址栏输入 `chrome://extensions/`,右上角打开开发者模式 9 | 3. 把解压出来的文件夹拖进页面 10 | 4. 右上角打开插件页面,此时已经会显示当前截取的115 cookie,在下方填写alist后台地址和账户名密码后,下次登录115 cookie会被自动同步到alist 11 | 12 | ![tutorial](./docs/tutorial.png) 13 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | const setLastError = (msg) => chrome.storage.sync.set({ errorMsg: msg }); 2 | 3 | const readLocalStorage = async (keys) => { 4 | return new Promise((resolve, reject) => { 5 | chrome.storage.sync.get(keys, resolve); 6 | }); 7 | }; 8 | 9 | async function getTokenAndBase() { 10 | const { alistUrl, alistUsername, alistHash } = await readLocalStorage(['alistUrl', 'alistUsername', 'alistHash']); 11 | if (!alistUrl) { 12 | setLastError("failed: no url"); 13 | return [null, null]; 14 | } 15 | const hashUrl = new URL('/api/auth/login/hash', alistUrl).href; 16 | const resp = await fetch(hashUrl, { 17 | method: 'POST', 18 | body: `{"username":"${alistUsername}","password":"${alistHash}"}`, 19 | headers: { 20 | 'Content-Type': 'application/json;charset=UTF-8' 21 | } 22 | }); 23 | const json = await resp.json(); 24 | if (json.code != 200) { 25 | setLastError(`get token failed: ${json.message}`); 26 | return [null, null]; 27 | } 28 | return [json.data.token, alistUrl]; 29 | } 30 | 31 | async function updateCookieToAlist(cookie) { 32 | const [token, url] = await getTokenAndBase(); 33 | if (!token) { 34 | return; 35 | } 36 | const listUrl = new URL('/api/admin/storage/list', url).href; 37 | const resp = await fetch(listUrl, {headers: {"authorization": token}}); 38 | const obj = await resp.json(); 39 | const item = obj.data.content.find(x => x.driver == '115 Cloud'); 40 | if (!item) { 41 | setLastError("cannot find storage"); 42 | return; 43 | } 44 | let addition = JSON.parse(item.addition); 45 | addition.cookie = cookie; 46 | item.addition = JSON.stringify(addition); 47 | const updateUrl = new URL('/api/admin/storage/update', url).href; 48 | const setResp = await fetch(updateUrl, { 49 | method: 'POST', 50 | headers: { "authorization": token, 'Content-Type': 'application/json;charset=UTF-8' }, 51 | body: JSON.stringify(item), 52 | }); 53 | const setObj = await setResp.json(); 54 | if (setObj.code != 200) { 55 | setLastError(`update failed: ${setObj.message}`); 56 | return; 57 | } 58 | 59 | console.log(new Date(), 'update succ'); 60 | setLastError('success'); 61 | } 62 | 63 | function interceptCookie() { 64 | chrome.cookies.getAll({ domain: ".115.com" }, cookies => { 65 | const lastInterceptedCookie = cookies.filter(x => x.domain == '.115.com').map(c => `${c.name}=${c.value}`).join("; "); 66 | const lastInterceptedTime = new Date().toLocaleString(); 67 | chrome.storage.sync.set({ lastInterceptedCookie, lastInterceptedTime }); 68 | updateCookieToAlist(lastInterceptedCookie); 69 | }); 70 | } 71 | 72 | chrome.cookies.onChanged.addListener(async (evt) => { 73 | if (evt.removed || !evt.cookie.domain.includes('.115.com')) { 74 | return; 75 | } 76 | 77 | interceptCookie(); 78 | }); 79 | 80 | chrome.runtime.onInstalled.addListener(function (details) { 81 | interceptCookie(); 82 | }); 83 | 84 | chrome.runtime.onMessage.addListener( 85 | function (request, sender, sendResponse) { 86 | if (request.msg == "sync") interceptCookie(); 87 | } 88 | ); 89 | -------------------------------------------------------------------------------- /docs/tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yenkn/115-to-alist/048f2d92da218ccca0a64a16971a91316b85511e/docs/tutorial.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yenkn/115-to-alist/048f2d92da218ccca0a64a16971a91316b85511e/icon.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "115ToAlist", 4 | "description": "Update 115 cookies to Alist automatically.", 5 | "version": "1.0", 6 | "permissions": [ 7 | "cookies", 8 | "storage" 9 | ], 10 | "host_permissions": [ 11 | "" 12 | ], 13 | "background": { 14 | "service_worker": "background.js" 15 | }, 16 | "icons": { 17 | "16": "icon.png", 18 | "32": "icon.png", 19 | "48": "icon.png", 20 | "128": "icon.png" 21 | }, 22 | "action": { 23 | "default_popup": "popup.html", 24 | "default_icon": "icon.png" 25 | } 26 | } -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cookie Interceptor 5 | 45 | 46 | 47 |
48 |
49 |
N/A
50 |
Update at
51 |
52 |
53 | 54 |
55 |
56 | 57 | 58 |
59 |
60 | 61 | 62 |
63 |
64 | 65 | 66 |
67 | 68 |
69 |
70 |
71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | async function sha256(message) { 2 | const msgBuffer = new TextEncoder().encode(message); 3 | const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); 4 | const hashArray = Array.from(new Uint8Array(hashBuffer)); 5 | const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); 6 | return hashHex; 7 | } 8 | 9 | const setMessage = (msg) => document.getElementById("status").textContent = msg; 10 | 11 | const updateElements = () => { 12 | chrome.storage.sync.get(['errorMsg', "lastInterceptedCookie", "lastInterceptedTime", "alistUrl", "alistUser"], ({ errorMsg, lastInterceptedCookie, lastInterceptedTime, alistUrl, alistUser }) => { 13 | document.getElementById("errorMsg").textContent = errorMsg || ""; 14 | document.getElementById("cookieStr").textContent = lastInterceptedCookie || "N/A"; 15 | document.getElementById("cookieTime").textContent = lastInterceptedTime || ""; 16 | document.getElementById("alistUrl").value = alistUrl || ""; 17 | document.getElementById("alistUsername").value = alistUser || "admin"; 18 | }); 19 | }; 20 | 21 | chrome.storage.sync.onChanged.addListener(updateElements); 22 | 23 | document.addEventListener("DOMContentLoaded", () => { 24 | updateElements(); 25 | 26 | // Save backend URL 27 | document.getElementById("saveBtn").addEventListener("click", async () => { 28 | const alistUrl = document.getElementById("alistUrl").value; 29 | const alistUsername = document.getElementById("alistUsername").value; 30 | const alistPassword = document.getElementById("alistPassword").value; 31 | const alistHash = await sha256(`${alistPassword}-https://github.com/alist-org/alist`); 32 | 33 | const hashUrl = new URL('/api/auth/login/hash', alistUrl).href; 34 | fetch(hashUrl, { 35 | method: 'POST', 36 | body: `{"username":"${alistUsername}","password":"${alistHash}"}`, 37 | headers: { 38 | 'Content-Type': 'application/json;charset=UTF-8' 39 | } 40 | }).then(async resp => { 41 | if (!resp.ok) { 42 | setMessage("resp: " + resp.statusText); 43 | return; 44 | } 45 | 46 | const json = await resp.json(); 47 | if (json.code != 200) { 48 | setMessage("alist err: " + json.message); 49 | return; 50 | } 51 | 52 | chrome.storage.sync.set({ alistUrl, alistUsername, alistHash }, () => { 53 | setMessage("saved!"); 54 | }); 55 | 56 | }).catch(err => setMessage("err: " + err.toString())); 57 | }); 58 | 59 | document.getElementById("syncBtn").addEventListener("click", async () => { 60 | chrome.runtime.sendMessage({ msg: "sync" }); 61 | }); 62 | 63 | }); 64 | --------------------------------------------------------------------------------