@yeaft/webchat-agent 1.0.666 → 1.0.667
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/local-runtime/server/db/connection.js +23 -6
- package/local-runtime/server/db/user-preferences-db.js +9 -2
- package/local-runtime/server/routes/user-routes.js +29 -4
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +118 -118
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +1 -1
- package/package.json +1 -1
|
@@ -43,7 +43,11 @@ db.exec(`
|
|
|
43
43
|
sidebar_projects_ratio REAL NOT NULL DEFAULT 0.5
|
|
44
44
|
CHECK (sidebar_projects_ratio >= 0.15 AND sidebar_projects_ratio <= 0.85),
|
|
45
45
|
sidebar_actions_compact INTEGER NOT NULL DEFAULT 0
|
|
46
|
-
CHECK (sidebar_actions_compact IN (0, 1))
|
|
46
|
+
CHECK (sidebar_actions_compact IN (0, 1)),
|
|
47
|
+
sidebar_collapsed_projects TEXT NOT NULL DEFAULT '{}'
|
|
48
|
+
CHECK (json_valid(sidebar_collapsed_projects) AND json_type(sidebar_collapsed_projects) = 'object'),
|
|
49
|
+
sidebar_collapsed_sections TEXT NOT NULL DEFAULT '{}'
|
|
50
|
+
CHECK (json_valid(sidebar_collapsed_sections) AND json_type(sidebar_collapsed_sections) = 'object')
|
|
47
51
|
);
|
|
48
52
|
|
|
49
53
|
-- Finalized deletion survives removal of the users row. This prevents a
|
|
@@ -341,6 +345,10 @@ db.exec(`
|
|
|
341
345
|
const migrations = [
|
|
342
346
|
`ALTER TABLE user_preferences ADD COLUMN sidebar_actions_compact INTEGER NOT NULL DEFAULT 0
|
|
343
347
|
CHECK (sidebar_actions_compact IN (0, 1))`,
|
|
348
|
+
`ALTER TABLE user_preferences ADD COLUMN sidebar_collapsed_projects TEXT NOT NULL DEFAULT '{}'
|
|
349
|
+
CHECK (json_valid(sidebar_collapsed_projects) AND json_type(sidebar_collapsed_projects) = 'object')`,
|
|
350
|
+
`ALTER TABLE user_preferences ADD COLUMN sidebar_collapsed_sections TEXT NOT NULL DEFAULT '{}'
|
|
351
|
+
CHECK (json_valid(sidebar_collapsed_sections) AND json_type(sidebar_collapsed_sections) = 'object')`,
|
|
344
352
|
`ALTER TABLE sessions ADD COLUMN user_id TEXT REFERENCES users(id)`,
|
|
345
353
|
`ALTER TABLE users ADD COLUMN totp_secret TEXT`,
|
|
346
354
|
`ALTER TABLE users ADD COLUMN totp_enabled INTEGER DEFAULT 0`,
|
|
@@ -911,16 +919,25 @@ export const stmts = {
|
|
|
911
919
|
`),
|
|
912
920
|
|
|
913
921
|
getUserPreferences: db.prepare(`
|
|
914
|
-
SELECT sidebar_projects_ratio, sidebar_actions_compact
|
|
922
|
+
SELECT sidebar_projects_ratio, sidebar_actions_compact, sidebar_collapsed_projects, sidebar_collapsed_sections
|
|
923
|
+
FROM user_preferences WHERE user_id = ?
|
|
915
924
|
`),
|
|
916
925
|
|
|
917
926
|
upsertUserPreferences: db.prepare(`
|
|
918
|
-
INSERT INTO user_preferences (
|
|
919
|
-
|
|
927
|
+
INSERT INTO user_preferences (
|
|
928
|
+
user_id, sidebar_projects_ratio, sidebar_actions_compact, sidebar_collapsed_projects, sidebar_collapsed_sections
|
|
929
|
+
)
|
|
930
|
+
VALUES (?, COALESCE(?, 0.5), COALESCE(?, 0), ?, ?)
|
|
920
931
|
ON CONFLICT(user_id) DO UPDATE SET
|
|
921
932
|
sidebar_projects_ratio = COALESCE(?, user_preferences.sidebar_projects_ratio),
|
|
922
|
-
sidebar_actions_compact = COALESCE(?, user_preferences.sidebar_actions_compact)
|
|
923
|
-
|
|
933
|
+
sidebar_actions_compact = COALESCE(?, user_preferences.sidebar_actions_compact),
|
|
934
|
+
sidebar_collapsed_projects = json_patch(
|
|
935
|
+
user_preferences.sidebar_collapsed_projects, excluded.sidebar_collapsed_projects
|
|
936
|
+
),
|
|
937
|
+
sidebar_collapsed_sections = json_patch(
|
|
938
|
+
user_preferences.sidebar_collapsed_sections, excluded.sidebar_collapsed_sections
|
|
939
|
+
)
|
|
940
|
+
RETURNING sidebar_projects_ratio, sidebar_actions_compact, sidebar_collapsed_projects, sidebar_collapsed_sections
|
|
924
941
|
`),
|
|
925
942
|
|
|
926
943
|
// Invitation 操作
|
|
@@ -4,6 +4,8 @@ function toPreferences(row) {
|
|
|
4
4
|
return {
|
|
5
5
|
sidebarProjectsRatio: row?.sidebar_projects_ratio ?? 0.5,
|
|
6
6
|
sidebarActionsCompact: !!row?.sidebar_actions_compact,
|
|
7
|
+
sidebarCollapsedProjects: JSON.parse(row?.sidebar_collapsed_projects ?? '{}'),
|
|
8
|
+
sidebarCollapsedSections: JSON.parse(row?.sidebar_collapsed_sections ?? '{}'),
|
|
7
9
|
};
|
|
8
10
|
}
|
|
9
11
|
|
|
@@ -14,10 +16,15 @@ export const userPreferencesDb = {
|
|
|
14
16
|
},
|
|
15
17
|
|
|
16
18
|
// The HTTP boundary validates the patch. A single upsert preserves omitted
|
|
17
|
-
// fields without a read/merge/write race between
|
|
19
|
+
// fields and merges map keys without a read/merge/write race between clients.
|
|
20
|
+
// Empty maps are no-ops; explicit false values remain stored overrides.
|
|
18
21
|
update(userId, patch) {
|
|
19
22
|
const ratio = patch.sidebarProjectsRatio ?? null;
|
|
20
23
|
const compact = patch.sidebarActionsCompact === undefined ? null : Number(patch.sidebarActionsCompact);
|
|
21
|
-
|
|
24
|
+
const projects = JSON.stringify(patch.sidebarCollapsedProjects ?? {});
|
|
25
|
+
const sections = JSON.stringify(patch.sidebarCollapsedSections ?? {});
|
|
26
|
+
return toPreferences(stmts.upsertUserPreferences.get(
|
|
27
|
+
userId, ratio, compact, projects, sections, ratio, compact,
|
|
28
|
+
));
|
|
22
29
|
},
|
|
23
30
|
};
|
|
@@ -51,6 +51,26 @@ function ensureAgentSecret(user) {
|
|
|
51
51
|
return userDb.resetAgentSecret(user.id);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function isPlainObject(value) {
|
|
55
|
+
if (!value || typeof value !== 'object') return false;
|
|
56
|
+
const prototype = Object.getPrototypeOf(value);
|
|
57
|
+
return prototype === Object.prototype || prototype === null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const preferenceFields = new Set([
|
|
61
|
+
'sidebarProjectsRatio', 'sidebarActionsCompact', 'sidebarCollapsedProjects', 'sidebarCollapsedSections',
|
|
62
|
+
]);
|
|
63
|
+
const unsafeMapKeys = new Set(['__proto__', 'constructor', 'prototype']);
|
|
64
|
+
|
|
65
|
+
function isCollapseMap(value, sections = false) {
|
|
66
|
+
if (!isPlainObject(value)) return false;
|
|
67
|
+
const entries = Object.entries(value);
|
|
68
|
+
return (sections || entries.length <= 1000) && entries.every(([key, collapsed]) => (
|
|
69
|
+
typeof collapsed === 'boolean' && !unsafeMapKeys.has(key)
|
|
70
|
+
&& (sections ? key === 'projects' || key === 'recents' : key.length > 0 && key.length <= 200)
|
|
71
|
+
));
|
|
72
|
+
}
|
|
73
|
+
|
|
54
74
|
/**
|
|
55
75
|
* Register user profile, agent secret, and admin user management routes.
|
|
56
76
|
*/
|
|
@@ -104,17 +124,22 @@ export function registerUserRoutes(app, {
|
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
const body = req.body;
|
|
107
|
-
if (!
|
|
127
|
+
if (!isPlainObject(body)
|
|
108
128
|
|| Object.keys(body).length === 0
|
|
109
|
-
|| Object.keys(body).some(key => key
|
|
129
|
+
|| Object.keys(body).some(key => !preferenceFields.has(key))
|
|
110
130
|
|| (Object.hasOwn(body, 'sidebarProjectsRatio') && (
|
|
111
131
|
typeof body.sidebarProjectsRatio !== 'number'
|
|
112
132
|
|| !Number.isFinite(body.sidebarProjectsRatio)
|
|
113
133
|
|| body.sidebarProjectsRatio < 0.15 || body.sidebarProjectsRatio > 0.85
|
|
114
134
|
))
|
|
115
|
-
|| (Object.hasOwn(body, 'sidebarActionsCompact') && typeof body.sidebarActionsCompact !== 'boolean')
|
|
135
|
+
|| (Object.hasOwn(body, 'sidebarActionsCompact') && typeof body.sidebarActionsCompact !== 'boolean')
|
|
136
|
+
|| (Object.hasOwn(body, 'sidebarCollapsedProjects') && !isCollapseMap(body.sidebarCollapsedProjects))
|
|
137
|
+
|| (Object.hasOwn(body, 'sidebarCollapsedSections') && !isCollapseMap(body.sidebarCollapsedSections, true))) {
|
|
116
138
|
return res.status(400).json({
|
|
117
|
-
error: 'Expected a nonempty patch with only sidebarProjectsRatio (finite number between 0.15 and 0.85)
|
|
139
|
+
error: 'Expected a nonempty patch with only sidebarProjectsRatio (finite number between 0.15 and 0.85), '
|
|
140
|
+
+ 'sidebarActionsCompact (boolean), sidebarCollapsedProjects (boolean map, up to 1000 nonempty project IDs '
|
|
141
|
+
+ 'of at most 200 characters, excluding __proto__/constructor/prototype), and/or '
|
|
142
|
+
+ 'sidebarCollapsedSections (boolean map with only projects/recents)',
|
|
118
143
|
});
|
|
119
144
|
}
|
|
120
145
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.667"}
|