labull-framework 1.4.1 → 2.0.0
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/README.md +2 -2
- package/dist/auth.js +1 -1
- package/dist/{chunk-index-3dQJWK2S.js → chunk-index-Bf5t2QKZ.js} +1 -1
- package/dist/{chunk-index-BVIJeFeJ.js → chunk-index-CXSN4mHl.js} +20 -20
- package/dist/index.js +2 -2
- package/dist/magic-block.js +10 -10
- package/dist/style.css +1 -1
- package/dist/types/auth-real.d.ts +3 -3
- package/dist/ui.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ import { useAuth } from 'labull-framework/auth'
|
|
|
41
41
|
import { openTab } from 'labull-framework/ui'
|
|
42
42
|
import type { SideItem } from 'labull-framework/ui'
|
|
43
43
|
|
|
44
|
-
const { user,
|
|
44
|
+
const { user, isAdmin, logout } = useAuth()
|
|
45
45
|
const notice = ref('')
|
|
46
46
|
|
|
47
47
|
/** 一级 = 模块(点它滑出二级菜单),二级 = 页面;点一项干什么写在它自己的 onClick 里。 */
|
|
@@ -148,7 +148,7 @@ workspace/<可读名>-<块 uuid>/
|
|
|
148
148
|
|
|
149
149
|
## 两份东西要知道
|
|
150
150
|
|
|
151
|
-
- 🔴 **前端不做授权**:`
|
|
151
|
+
- 🔴 **前端不做授权**:`isAdmin` / `groups` / `permissions` 只是界面显隐,判据全在后端。
|
|
152
152
|
- 🔴 **单例只有一份**:`auth` 的状态是模块级单例,`ui` 与 `magic-block` 共用同一份
|
|
153
153
|
(产物里共享代码在 `dist/chunk-*.js`,不是各拷贝一份)—— 所以"用户菜单说登录了、
|
|
154
154
|
宿主说没登录"这种事不会发生。
|
package/dist/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as s, a as t, C as r, b as u, c as A, d as i, e as c, f as h, g as n, h as E, l as o, i as S, j as _, s as l, u as I } from "./chunk-index-
|
|
1
|
+
import { A as s, a as t, C as r, b as u, c as A, d as i, e as c, f as h, g as n, h as E, l as o, i as S, j as _, s as l, u as I } from "./chunk-index-CXSN4mHl.js";
|
|
2
2
|
export {
|
|
3
3
|
s as API_BASE,
|
|
4
4
|
t as AUTH_DEADLINE_MS,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent as z, ref as S, computed as x, watch as ae, onMounted as X, onBeforeUnmount as Y, openBlock as t, createElementBlock as l, normalizeClass as $, unref as d, createElementVNode as e, Fragment as T, withDirectives as te, withKeys as Q, withModifiers as H, vModelText as _e, createCommentVNode as _, renderList as D, toDisplayString as w, nextTick as me, createBlock as ue, resolveDynamicComponent as ke, normalizeProps as ye, mergeProps as we, createTextVNode as W, renderSlot as Z, createVNode as F, vShow as oe } from "vue";
|
|
2
2
|
import { useRouter as ne, useRoute as re, RouterView as $e } from "vue-router";
|
|
3
3
|
import { e as se, o as le, _ as J, d as ce, p as ge, f as Ce, i as ie } from "./chunk-ProductShell.vue_vue_type_style_index_0_scoped_06035587_lang-Fky1ghd2.js";
|
|
4
|
-
import { u as Pe, e as q, g as xe, i as Le, d as Ee, l as Te, a as Se, s as Me } from "./chunk-index-
|
|
4
|
+
import { u as Pe, e as q, g as xe, i as Le, d as Ee, l as Te, a as Se, s as Me } from "./chunk-index-CXSN4mHl.js";
|
|
5
5
|
const Ie = ["onKeydown"], Oe = {
|
|
6
6
|
key: 2,
|
|
7
7
|
class: "labull-search-panel",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ref as l, computed as T } from "vue";
|
|
2
|
-
let
|
|
2
|
+
let u = "";
|
|
3
3
|
function U(t) {
|
|
4
|
-
|
|
4
|
+
u = String(t ?? "").trim().replace(/\/+$/, "");
|
|
5
5
|
}
|
|
6
|
-
const h = 8e3, $ = 1500, y = "labull.csrf", C = "x-labull-csrf", P = ["GET", "HEAD", "OPTIONS"],
|
|
6
|
+
const h = 8e3, $ = 1500, y = "labull.csrf", C = "x-labull-csrf", P = ["GET", "HEAD", "OPTIONS"], i = l(null);
|
|
7
7
|
function v(t) {
|
|
8
8
|
for (const e of (typeof document > "u" ? "" : document.cookie).split(";")) {
|
|
9
9
|
const [n, ...s] = e.trim().split("=");
|
|
@@ -15,8 +15,8 @@ function m(t = "GET", e = !1) {
|
|
|
15
15
|
const n = e ? { "Content-Type": "application/json" } : {}, s = v(y);
|
|
16
16
|
return s && !P.includes(t.toUpperCase()) && (n[C] = s), n;
|
|
17
17
|
}
|
|
18
|
-
async function
|
|
19
|
-
const n = String(e.method ?? "GET").toUpperCase(), s = await fetch(`${
|
|
18
|
+
async function A(t, e = {}) {
|
|
19
|
+
const n = String(e.method ?? "GET").toUpperCase(), s = await fetch(`${u}${t}`, {
|
|
20
20
|
...e,
|
|
21
21
|
method: n,
|
|
22
22
|
credentials: "include",
|
|
@@ -35,25 +35,25 @@ function f() {
|
|
|
35
35
|
}
|
|
36
36
|
function O(t = f()) {
|
|
37
37
|
const e = t.startsWith("/") && !t.startsWith("//") ? t : "/";
|
|
38
|
-
return `${
|
|
38
|
+
return `${u}/auth/login?redirect=${encodeURIComponent(e)}`;
|
|
39
39
|
}
|
|
40
40
|
function w(t = f()) {
|
|
41
41
|
window.location.assign(O(t));
|
|
42
42
|
}
|
|
43
43
|
function _() {
|
|
44
|
-
|
|
44
|
+
i.value = null, window.location.assign(`${u}/auth/logout`);
|
|
45
45
|
}
|
|
46
46
|
async function b() {
|
|
47
47
|
try {
|
|
48
|
-
const t = await
|
|
49
|
-
return
|
|
48
|
+
const t = await A("/auth/me");
|
|
49
|
+
return i.value = t, t;
|
|
50
50
|
} catch (t) {
|
|
51
51
|
const e = t?.status;
|
|
52
52
|
return (e === 401 || e === 403) && await Promise.race([
|
|
53
|
-
fetch(`${
|
|
53
|
+
fetch(`${u}/auth/logout`, { method: "POST", credentials: "include", headers: m("POST", !0) }).catch(() => {
|
|
54
54
|
}),
|
|
55
55
|
new Promise((n) => setTimeout(n, $))
|
|
56
|
-
]),
|
|
56
|
+
]), i.value = null, null;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
async function p(t = h) {
|
|
@@ -74,7 +74,7 @@ async function D(t = f(), e = h) {
|
|
|
74
74
|
function R(t = {}) {
|
|
75
75
|
const e = t.deadlineMs ?? h, n = l(!1), s = l(!1), r = l(!1);
|
|
76
76
|
let a = null, d = !1;
|
|
77
|
-
async function
|
|
77
|
+
async function E(o) {
|
|
78
78
|
const c = await p(e);
|
|
79
79
|
if (c !== "timeout" && c) {
|
|
80
80
|
d = !0, s.value = !1, r.value = !1, n.value = !0;
|
|
@@ -88,14 +88,14 @@ function R(t = {}) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
function g(o) {
|
|
91
|
-
return d ? Promise.resolve() : a || (a =
|
|
91
|
+
return d ? Promise.resolve() : a || (a = E(o).finally(() => {
|
|
92
92
|
a = null;
|
|
93
93
|
}), a);
|
|
94
94
|
}
|
|
95
|
-
function
|
|
95
|
+
function S(o) {
|
|
96
96
|
d = !1, n.value = !1, g(o);
|
|
97
97
|
}
|
|
98
|
-
return { open: n, denied: s, unreachable: r, pass: g, recheck:
|
|
98
|
+
return { open: n, denied: s, unreachable: r, pass: g, recheck: S };
|
|
99
99
|
}
|
|
100
100
|
function j() {
|
|
101
101
|
async function t() {
|
|
@@ -103,12 +103,12 @@ function j() {
|
|
|
103
103
|
return e === "timeout" ? null : e;
|
|
104
104
|
}
|
|
105
105
|
return {
|
|
106
|
-
user:
|
|
107
|
-
|
|
106
|
+
user: i,
|
|
107
|
+
isAdmin: T(() => i.value?.isAdmin === !0),
|
|
108
108
|
/** 🔴 只是界面提示位(本地读得到 CSRF cookie 就算),**不是授权判据**:要挡就在后端挡。 */
|
|
109
109
|
isAuthenticated: () => v(y) !== "",
|
|
110
110
|
refresh: t,
|
|
111
|
-
apiFetch:
|
|
111
|
+
apiFetch: A,
|
|
112
112
|
authHeaders: m,
|
|
113
113
|
currentPath: f,
|
|
114
114
|
login: w,
|
|
@@ -116,10 +116,10 @@ function j() {
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
export {
|
|
119
|
-
|
|
119
|
+
u as A,
|
|
120
120
|
$ as C,
|
|
121
121
|
h as a,
|
|
122
|
-
|
|
122
|
+
A as b,
|
|
123
123
|
m as c,
|
|
124
124
|
R as d,
|
|
125
125
|
f as e,
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as e, a as t, C as r, b as o, c as i, d as l, e as u, f as c, g as n, h as T, l as A, i as b, j as p, s as d, u as h } from "./chunk-index-
|
|
2
|
-
import { A as E, P as _, a as P, b as U, _ as f, U as g, i as I, l as x } from "./chunk-index-
|
|
1
|
+
import { A as e, a as t, C as r, b as o, c as i, d as l, e as u, f as c, g as n, h as T, l as A, i as b, j as p, s as d, u as h } from "./chunk-index-CXSN4mHl.js";
|
|
2
|
+
import { A as E, P as _, a as P, b as U, _ as f, U as g, i as I, l as x } from "./chunk-index-Bf5t2QKZ.js";
|
|
3
3
|
import { T as N, a as m, c as D, f as L, i as v, b as B, o as M, p as H, s as O, u as R, d as j } from "./chunk-ProductShell.vue_vue_type_style_index_0_scoped_06035587_lang-Fky1ghd2.js";
|
|
4
4
|
export {
|
|
5
5
|
e as API_BASE,
|
package/dist/magic-block.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { shallowRef as vl, ref as j, computed as z, defineComponent as Ce, watch as Dt, nextTick as on, onMounted as fn, onBeforeUnmount as hn, openBlock as d, createElementBlock as p, normalizeStyle as vn, normalizeClass as Z, unref as G, createElementVNode as a, toDisplayString as f, createCommentVNode as C, Fragment as A, renderList as F, withModifiers as an, getCurrentInstance as gl, createBlock as _l, createTextVNode as x, withDirectives as mt, vModelCheckbox as rs, vModelText as ae, withKeys as yl, vShow as ye, createStaticVNode as kl, vModelSelect as $l, vModelDynamic as xl } from "vue";
|
|
2
2
|
import { useRouter as cs, useRoute as wl } from "vue-router";
|
|
3
|
-
import { b as $t, c as ue, A as we, u as us } from "./chunk-index-
|
|
4
|
-
import { s as ed } from "./chunk-index-
|
|
3
|
+
import { b as $t, c as ue, A as we, u as us } from "./chunk-index-CXSN4mHl.js";
|
|
4
|
+
import { s as ed } from "./chunk-index-CXSN4mHl.js";
|
|
5
5
|
import { u as Sl, o as rn, b as ds, _ as gn, d as Cl } from "./chunk-ProductShell.vue_vue_type_style_index_0_scoped_06035587_lang-Fky1ghd2.js";
|
|
6
6
|
function El(n) {
|
|
7
7
|
var e = {}, s = [];
|
|
@@ -1519,7 +1519,7 @@ const No = ["data-magic-block", "data-magic-module"], qo = ["srcdoc", "title"],
|
|
|
1519
1519
|
theme: { type: Boolean, default: !0 }
|
|
1520
1520
|
},
|
|
1521
1521
|
setup(n) {
|
|
1522
|
-
const e = n, s = Sl(), o = cs(), {
|
|
1522
|
+
const e = n, s = Sl(), o = cs(), { isAdmin: r } = us(), c = j(null), u = j(null), b = j(null), h = j(!1), v = j(0), _ = j(0);
|
|
1523
1523
|
let y = null, g = null, k = null, w = null, E = null, R = null;
|
|
1524
1524
|
const L = z(() => e.name.trim() || e.uuid), $ = z(() => b.value?.state ?? ""), N = z(() => $.value === "invalid" ? "校验不通过" : $.value === "draft" ? "未开发" : L.value), V = z(() => (b.value?.brief ?? "").trim() || (b.value?.name ?? "").trim() || L.value), et = z(() => (b.value?.reason ?? "").trim()), vt = z(() => b.value?.issues ?? []), P = z(() => Math.max(_.value, v.value, 1)), Pt = z(() => ({ height: `${P.value}px` })), ft = z(() => {
|
|
1525
1525
|
const O = {};
|
|
@@ -1717,7 +1717,7 @@ const No = ["data-magic-block", "data-magic-module"], qo = ["srcdoc", "title"],
|
|
|
1717
1717
|
], 64)) : C("", !0)
|
|
1718
1718
|
], 14, No));
|
|
1719
1719
|
}
|
|
1720
|
-
}), Xo = /* @__PURE__ */ gn(Yo, [["__scopeId", "data-v-
|
|
1720
|
+
}), Xo = /* @__PURE__ */ gn(Yo, [["__scopeId", "data-v-a3d61967"]]);
|
|
1721
1721
|
function gt(n) {
|
|
1722
1722
|
return typeof n == "string" ? n.trim() : "";
|
|
1723
1723
|
}
|
|
@@ -1899,7 +1899,7 @@ function en(n, e = {}) {
|
|
|
1899
1899
|
if (s === 504 || s === 408 || s === 502 || s === 503) {
|
|
1900
1900
|
const b = s === 502 || s === 503 ? `后端没答上来(${s}):这一趟请求送出去之后,前面的代理说"上游不可用" —— 多半是后面那个进程正在忙着算、或者刚重启。` : `网关等不及了(${s}):这一趟是「一次请求」把整个目录递上去,后端要「逐块」做保存前那套判据(试加载 + 引用深检,实测 0.8~3.7 秒/块)—— 算得越久越危险。`;
|
|
1901
1901
|
u.push(`${b}这一趟递上去的是 ${c}。`), (s === 504 || s === 408) && (u.push(""), u.push('换句话说:这不是你的块写错了 —— 是"一条请求算得太久、中间没有回话"被前置网关掐断了。'), u.push("这次可以这么办(从省事到彻底):"), u.push("1. 再点一次「读回改动」——但先把这次不动的块文件夹移出目录,只留要改的那几个:判据完全一样,只是分母小了。"), u.push("2. 调大网关的超时:nginx 的 `proxy_read_timeout 300s;` / `proxy_send_timeout 300s;`;云负载均衡上一般也有一个「后端响应超时」,一起调。"), u.push('3. 前端升到 1.0.3+:读回改成「流式」(算完一块就回一句话),只要字节一直在往回走,网关就不会因为"没回话"把它掐掉。'));
|
|
1902
|
-
} else s === 413 ? (u.push(`这一趟递上去的正文太大了(413):后端一次收不下这么多内容(这一趟是 ${ss(e)})。`), u.push("把这次不动的块文件夹移出目录,只留要改的那几个再读回。")) : s === 401 || s === 403 ? (u.push(`后端不认这次请求(${s}
|
|
1902
|
+
} else s === 413 ? (u.push(`这一趟递上去的正文太大了(413):后端一次收不下这么多内容(这一趟是 ${ss(e)})。`), u.push("把这次不动的块文件夹移出目录,只留要改的那几个再读回。")) : s === 401 || s === 403 ? (u.push(`后端不认这次请求(${s}):读回要管理员(在管理员角色的组里)。`), u.push(s === 401 ? "登录态可能已经过期:刷新页面重新登录,再点「读回改动」。" : "这个账号不是管理员:换一个在管理员角色组里的账号。")) : s === 0 ? (u.push(`没连上后端(${o}):请求还没到服务端就断了 —— 后端进程没起来 / 地址不对 / 网络或代理断了。`), u.push(`原文:${r}`)) : s >= 500 ? (u.push(`后端在这次读回里抛异常了(${s}):这是平台的问题,不是你块的问题。`), u.push(`原文:${r}`), u.push('把这一句连同"读回"贴给维护的人;后端日志里那一趟的 traceback 才是真原因。')) : s >= 400 ? u.push(`这一次读回被拒了(${s}):${r}`) : u.push(`读回没成功:${r}`);
|
|
1903
1903
|
return s === 0 && u.length === 0 && u.push(r), u.join(`
|
|
1904
1904
|
`);
|
|
1905
1905
|
}
|
|
@@ -2092,8 +2092,8 @@ function pa(n) {
|
|
|
2092
2092
|
" work_id: string",
|
|
2093
2093
|
" /** 显示名(拿不到就是空串 —— 不编造)。 */",
|
|
2094
2094
|
" name: string",
|
|
2095
|
-
" /**
|
|
2096
|
-
"
|
|
2095
|
+
" /** 是不是管理员(在**管理员角色**的组里;不是 Django 的 is_staff)。 */",
|
|
2096
|
+
" is_admin: boolean",
|
|
2097
2097
|
" /** 所在组的名字。 */",
|
|
2098
2098
|
" groups: string[]",
|
|
2099
2099
|
" /** 直接权限 + 组权限的 codename(`app_label.codename`)。 */",
|
|
@@ -2592,7 +2592,7 @@ const Ba = { class: "mbc__head" }, Ta = ["title"], Aa = ["title"], Ma = {
|
|
|
2592
2592
|
两种情形共用它(前端声明了、手里却还没有那个文件夹 / 手里那个文件夹的清单 id 库里没有)
|
|
2593
2593
|
—— 对用户是同一件事:这个块不在库里。 */
|
|
2594
2594
|
"not-in-db": "不在数据库"
|
|
2595
|
-
}, s = e["not-in-db"], {
|
|
2595
|
+
}, s = e["not-in-db"], { isAdmin: o } = us(), r = cs(), { active: c } = Cl(), u = (i) => !ds(i, r), b = j(!1), h = j(""), v = j([]), _ = j(""), y = j(""), g = j(""), k = j([]), w = j(!1), E = j(!1), R = j("blocks"), L = j(""), $ = j([]), N = j(""), V = j(""), et = j(""), vt = j(""), P = j(!1), Pt = j(!1), ft = j(!1), St = j(!1), B = j(!1), M = j(null), S = j(null), D = j("one"), ht = j(pe()), lt = z(() => v.value.find((i) => i.id === ht.value) ?? null), Gt = () => D.value === "framework" ? { framework: !0 } : D.value === "all" ? {} : { blocks: [ht.value] }, te = z(() => D.value === "framework" ? "只铺框架文件(顶层那几份生成物 + example/):workspace/ 里的块代码一个字节都不写、也不删" : D.value === "one" && !lt.value ? "先在左边挑一个块(或者把范围改成「全部块」/「只铺框架文件」)" : "把库里的块铺到你选的文件夹");
|
|
2596
2596
|
function de(i) {
|
|
2597
2597
|
ht.value = i;
|
|
2598
2598
|
try {
|
|
@@ -3856,7 +3856,7 @@ ${t.text}`).join(`
|
|
|
3856
3856
|
}, f(ft.value ? "导出中…" : "导出"), 9, Lr)
|
|
3857
3857
|
]),
|
|
3858
3858
|
D.value === "framework" ? (d(), p("p", Pr, [...t[124] || (t[124] = [
|
|
3859
|
-
kl(' 这一趟只铺<b data-v-
|
|
3859
|
+
kl(' 这一趟只铺<b data-v-968efede>框架文件</b>:顶层那几份生成物(<span class="mbc__mono" data-v-968efede>AGENTS.lock.md</span> / <span class="mbc__mono" data-v-968efede>backend.lock.pyi</span> / <span class="mbc__mono" data-v-968efede>requirements.lock.txt</span> / <span class="mbc__mono" data-v-968efede>smoke.lock.mjs</span> / <span class="mbc__mono" data-v-968efede>pages.lock.ts</span> / <span class="mbc__mono" data-v-968efede>labull.css</span> / <span class="mbc__mono" data-v-968efede>CONTROLS.lock.html</span>) 与 <span class="mbc__mono" data-v-968efede>example/</span> 那一层。 🔴 <span class="mbc__mono" data-v-968efede>workspace/</span> 里的块代码<b data-v-968efede>一个字节都不写、也不删</b> (连「清空并导出」都不进那一层)—— 平台升级之后刷新框架就用这一档。 ', 23)
|
|
3860
3860
|
])])) : (d(), p("p", Ir, [
|
|
3861
3861
|
x(f(D.value === "all" ? `这一趟铺全部 ${v.value.length} 个块` : lt.value ? `这一趟只铺「${lt.value.module} / ${lt.value.name || lt.value.id}」` : "先挑一个块") + " ⇒ ", 1),
|
|
3862
3862
|
t[125] || (t[125] = a("span", { class: "mbc__mono" }, "workspace/<可读名>-<块 uuid>/", -1)),
|
|
@@ -4227,7 +4227,7 @@ ${t.text}`).join(`
|
|
|
4227
4227
|
}, "×")) : C("", !0)
|
|
4228
4228
|
], 2)) : C("", !0);
|
|
4229
4229
|
}
|
|
4230
|
-
}), Xu = /* @__PURE__ */ gn(Su, [["__scopeId", "data-v-
|
|
4230
|
+
}), Xu = /* @__PURE__ */ gn(Su, [["__scopeId", "data-v-968efede"]]), Cu = ["aria-label"], Eu = ["srcdoc"], Bu = { class: "mbd__head" }, Tu = { class: "mbd__title" }, Au = {
|
|
4231
4231
|
key: 0,
|
|
4232
4232
|
class: "mbd__body"
|
|
4233
4233
|
}, Mu = {
|