├── .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 | 
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 |
--------------------------------------------------------------------------------