magicrew-auth 0.3.34 → 0.3.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/page/app.js DELETED
@@ -1,243 +0,0 @@
1
- // 邀请上级页:只负责把选择写进本插件的 options(inviteBySite),
2
- // 真正用它的是 index.mjs 里的 getOpts() 与 src/hooks/methods.mjs 的 inviteFromOptions()。
3
- //
4
- // 账号清单来自 codes.js —— 那是插件在 Node 侧生成的静态脚本
5
- // (页面是沙箱:没有 fetch、读不到磁盘,也拿不到账号)。
6
-
7
- const SITES = [
8
- { id: "magicrew", name: "超级麦吉(国内站)" },
9
- { id: "magicrew-intl", name: "MagiCrew(国际站)" },
10
- ];
11
-
12
- const state = {
13
- options: {}, // 服务端已知的完整 options,保存时原样带回去
14
- pick: {}, // siteId -> { mode, code, org, accountId }
15
- };
16
-
17
- const data = () => window.MAGICREW_CODES?.sites ?? {};
18
- const $ = (id) => document.getElementById(id);
19
-
20
- function currentOf(siteId) {
21
- const per = state.options?.inviteBySite?.[siteId];
22
- if (per && ["auto", "fixed", "custom"].includes(per.mode)) return per;
23
- const pol = data()[siteId]?.policy;
24
- if (pol && (pol.mode === "fixed" || pol.mode === "custom") && pol.code) return pol;
25
- return { mode: "auto", code: "" };
26
- }
27
-
28
- function labelOf(siteId) {
29
- const cur = currentOf(siteId);
30
- if (cur.mode === "fixed" || cur.mode === "custom") {
31
- const who = cur.accountId || cur.org || "";
32
- return (who ? who + " · " : "") + cur.code;
33
- }
34
- const rolling = data()[siteId]?.rolling || "";
35
- return "自动(跟随最后一个登录 / 签到的号" + (rolling ? ",当前 " + rolling : "") + ")";
36
- }
37
-
38
- function render() {
39
- const app = $("app");
40
- app.innerHTML = "";
41
-
42
- for (const site of SITES) {
43
- const info = data()[site.id] || {};
44
- const codes = info.codes || {};
45
- const cur = currentOf(site.id);
46
-
47
- const sec = document.createElement("section");
48
- sec.innerHTML = `<h2>${site.name}</h2>`;
49
-
50
- const now = document.createElement("div");
51
- now.className = "now";
52
- now.innerHTML = `当前:<b>${labelOf(site.id)}</b>`;
53
- sec.appendChild(now);
54
-
55
- // 当前选择指向已删除的账号(登录侧已自动回退)—— 页面上要说明白
56
- const warn = document.createElement("div");
57
- warn.className = "hint";
58
- const pageDead = cur.mode === "fixed" && cur.org && !codes[cur.org] && cur.code !== (info.author || "");
59
- if (pageDead) {
60
- warn.textContent = "⚠ 页面选的账号已不在已登录列表里,新号会自动回退到滚动 / 作者码。重新选一个账号或点「自动」。";
61
- warn.style.color = "#f2b277";
62
- sec.appendChild(warn);
63
- } else if (info.policyStale && (cur.mode === "fixed" || cur.mode === "custom") && cur.code === info.policy?.code) {
64
- warn.textContent = "⚠ 当前固定的账号已删除,新号会自动回退到滚动 / 作者码。点「自动」或重新选一个账号。";
65
- warn.style.color = "#f2b277";
66
- sec.appendChild(warn);
67
- } else if (info.rollingStale && cur.mode === "auto") {
68
- warn.textContent = "⚠ 滚动码属于已删除的账号,新号将改用作者码兜底。选一个账号或自定义码可避免。";
69
- warn.style.color = "#f2b277";
70
- sec.appendChild(warn);
71
- }
72
-
73
- const chips = document.createElement("div");
74
- chips.className = "chips";
75
-
76
- const mkChip = (text, active, onClick) => {
77
- const b = document.createElement("button");
78
- b.className = "chip";
79
- b.textContent = text;
80
- b.setAttribute("aria-pressed", active ? "true" : "false");
81
- b.onclick = onClick;
82
- chips.appendChild(b);
83
- };
84
-
85
- const author = info.author || "";
86
- if (author) {
87
- mkChip("我的(作者 MYCF711)", cur.mode === "fixed" && cur.code === author, () => {
88
- state.pick[site.id] = { mode: "fixed", code: author, org: "", accountId: "MYCF711" };
89
- render();
90
- save(site.id);
91
- });
92
- }
93
-
94
- mkChip("自动(滚动)", cur.mode === "auto", () => {
95
- state.pick[site.id] = { mode: "auto", code: "" };
96
- render();
97
- save(site.id);
98
- });
99
-
100
- for (const [org, v] of Object.entries(codes)) {
101
- if (!v?.code || v.code === author) continue;
102
- const who = v.accountId || org;
103
- mkChip(`${who}(${v.code})`, cur.code === v.code && cur.mode !== "auto", () => {
104
- state.pick[site.id] = { mode: "fixed", code: v.code, org, accountId: v.accountId || "" };
105
- render();
106
- save(site.id);
107
- });
108
- }
109
- sec.appendChild(chips);
110
-
111
- const custom = document.createElement("div");
112
- custom.className = "custom";
113
- const input = document.createElement("input");
114
- input.type = "text";
115
- input.placeholder = "自定义邀请码(IV 开头 12 位)";
116
- input.value = cur.mode === "custom" ? cur.code : "";
117
- const btn = document.createElement("button");
118
- btn.className = "save";
119
- btn.textContent = "保存自定义码";
120
- btn.onclick = () => {
121
- const code = input.value.trim().toUpperCase();
122
- if (!/^IV[A-Z0-9]{10}$/.test(code)) {
123
- setStatus("邀请码格式不对:应以 IV 开头、共 12 位", "bad");
124
- return;
125
- }
126
- state.pick[site.id] = { mode: "custom", code };
127
- render();
128
- save(site.id);
129
- };
130
- custom.append(input, btn);
131
- sec.appendChild(custom);
132
-
133
- const lockWrap = document.createElement("label");
134
- lockWrap.className = "lock";
135
- const lock = document.createElement("input");
136
- lock.type = "checkbox";
137
- lock.checked = lockOf(site.id);
138
- lock.onchange = () => saveLock(site.id, lock.checked);
139
- lockWrap.append(lock, document.createTextNode(" 锁定(签到不再覆盖)"));
140
- sec.appendChild(lockWrap);
141
-
142
- if (!Object.keys(codes).length) {
143
- const hint = document.createElement("div");
144
- hint.className = "hint";
145
- hint.textContent = "还没有记录到任何账号的邀请码 —— 等这个站点签到 / 登录过一次就会出现在这里。";
146
- sec.appendChild(hint);
147
- }
148
-
149
- app.appendChild(sec);
150
- }
151
- }
152
-
153
- function setStatus(msg, kind) {
154
- const el = $("status");
155
- el.textContent = msg || "";
156
- el.className = kind || "";
157
- }
158
-
159
- // ── 写 options:magpie 限「每个页面每秒最多一次」 ─────────────────
160
- //
161
- // 连点两个账号会撞上 "a page sets its options once a second at most"。
162
- // 所以这里串行 + 合并:同一时刻只有一个请求在飞,期间的新状态只保留最后一个,
163
- // 两次之间至少隔 1 秒;被限流就再等一轮重试(最多 3 次)。
164
- let inFlight = null;
165
- let pending = null;
166
- let lastAt = 0;
167
- const GAP_MS = 1050;
168
-
169
- function queueSave(patch, okMsg) {
170
- pending = { body: { ...(state.options || {}), ...patch }, okMsg };
171
- if (inFlight) return inFlight;
172
- inFlight = (async () => {
173
- let tries = 0;
174
- while (pending) {
175
- const wait = Math.max(0, GAP_MS - (Date.now() - lastAt));
176
- if (wait) await new Promise((r) => setTimeout(r, wait));
177
- const job = pending;
178
- pending = null;
179
- lastAt = Date.now();
180
- try {
181
- const saved = (await magpie.options.set(job.body)) ?? job.body;
182
- state.options = typeof saved === "object" && saved ? { ...job.body, ...saved } : job.body;
183
- setStatus(job.okMsg || "已保存", "ok")
184
- render() // 当前那一行跟着变;
185
- tries = 0;
186
- } catch (e) {
187
- const msg = String(e?.message ?? e);
188
- if (/once a second|too often/i.test(msg) && tries < 3) {
189
- tries += 1;
190
- if (!pending) pending = job; // 放回去,下一轮再试
191
- continue;
192
- }
193
- setStatus("保存失败:" + msg, "bad");
194
- }
195
- }
196
- })().finally(() => {
197
- inFlight = null;
198
- });
199
- return inFlight;
200
- }
201
-
202
- async function save(siteId) {
203
- const pick = state.pick[siteId];
204
- if (!pick) return;
205
- const base = { ...(state.options || {}) };
206
- const bySite = { ...(base.inviteBySite || {}), [siteId]: pick };
207
- await queueSave({ inviteBySite: bySite }, "已保存 —— 下一个新号就按这个走");
208
- }
209
-
210
- function lockOf(siteId) {
211
- const by = state.options?.inviteLockBySite;
212
- if (by && typeof by === "object" && Object.prototype.hasOwnProperty.call(by, siteId)) {
213
- return by[siteId] !== false;
214
- }
215
- if (state.options && Object.prototype.hasOwnProperty.call(state.options, "inviteLock")) {
216
- return state.options.inviteLock !== false;
217
- }
218
- return true;
219
- }
220
-
221
- async function saveLock(siteId, on) {
222
- const base = { ...(state.options || {}) };
223
- const by = { ...(base.inviteLockBySite || {}), [siteId]: !!on };
224
- await queueSave({ inviteLockBySite: by }, on ? "已锁定:签到不再覆盖" : "已解锁:跟随最后一个登录/签到的号");
225
- }
226
-
227
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
228
-
229
- async function boot() {
230
- // 先用 codes.js 画出来 —— 不依赖宿主回应;桥慢 / 挂了页面也不是空的
231
- render();
232
- try {
233
- await Promise.race([magpie.ready, sleep(3000)]);
234
- } catch {}
235
- try {
236
- state.options = (await Promise.race([magpie.options.get(), sleep(5000)])) ?? {};
237
- } catch (e) {
238
- setStatus("读不到当前设置:" + (e?.message ?? e), "bad");
239
- }
240
- render();
241
- }
242
-
243
- boot();
package/page/index.html DELETED
@@ -1,53 +0,0 @@
1
- <!doctype html>
2
- <html lang="zh">
3
- <head>
4
- <meta charset="utf-8" />
5
- <title>超级麦吉设置</title>
6
- <script src="_magpie/bridge.js"></script>
7
- <style>
8
- :root { color-scheme: light dark; }
9
- body {
10
- margin: 0; padding: 16px 18px 28px;
11
- font: 13px/1.5 -apple-system, "Segoe UI", "Microsoft YaHei", system-ui, sans-serif;
12
- background: var(--magpie-bg, #1b1b1d); color: var(--magpie-fg, #e6e6e6);
13
- }
14
- h1 { font-size: 14px; font-weight: 600; margin: 0 0 4px; }
15
- .sub { opacity: .6; margin: 0 0 16px; font-size: 12px; }
16
- section { border: 1px solid rgba(128,128,128,.25); border-radius: 10px; padding: 12px 14px; margin-bottom: 14px; }
17
- h2 { font-size: 13px; font-weight: 600; margin: 0 0 8px; display: flex; align-items: center; gap: 8px; }
18
- .now { font-size: 12px; opacity: .8; margin-bottom: 10px; }
19
- .now b { font-weight: 600; opacity: 1; }
20
- .chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
21
- button.chip {
22
- font: inherit; padding: 5px 10px; border-radius: 999px; cursor: pointer;
23
- border: 1px solid rgba(128,128,128,.35); background: transparent; color: inherit;
24
- }
25
- button.chip:hover { background: rgba(128,128,128,.16); }
26
- button.chip[aria-pressed="true"] { border-color: var(--magpie-accent, #6b8afd); background: rgba(107,138,253,.18); }
27
- .custom { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
28
- input[type="text"] {
29
- font: inherit; padding: 6px 9px; border-radius: 8px; min-width: 190px;
30
- border: 1px solid rgba(128,128,128,.35); background: rgba(128,128,128,.08); color: inherit;
31
- }
32
- button.save {
33
- font: inherit; padding: 6px 12px; border-radius: 8px; cursor: pointer;
34
- border: 1px solid var(--magpie-accent, #6b8afd); background: rgba(107,138,253,.22); color: inherit;
35
- }
36
- .hint { font-size: 12px; opacity: .55; margin-top: 8px; }
37
- .lock { display: flex; align-items: center; gap: 6px; font-size: 12px; margin-top: 10px; opacity: .85; }
38
- #status { position: sticky; bottom: 0; font-size: 12px; padding: 8px 0 0; min-height: 18px; }
39
- #status.ok { color: #5ec27d; }
40
- #status.bad { color: #f2777a; }
41
- </style>
42
- </head>
43
- <body>
44
- <h1>超级麦吉设置</h1>
45
- <p class="sub">新账号的邀请上级在这里设置。改完立即生效,不需要重新登录。</p>
46
-
47
- <div id="app"></div>
48
- <div id="status"></div>
49
-
50
- <script src="codes.js"></script>
51
- <script src="app.js"></script>
52
- </body>
53
- </html>