feat: avatar by link, loaded by the browser; Files nests workspaces under owners' homes

- POST /api/me/avatar/url stores the https link (users.avatar_link) instead
  of downloading it; GET /api/me/avatar and /api/users/:id/avatar answer 302
  to it (no referrer). Link rules: https, no userinfo, <= 2048 chars, no
  localhost or private/reserved IP literal. Upload and link replace each other.
- Settings -> Account first loads the link as an embedded image and refuses
  one this browser cannot show: a picture behind a login (Mattermost's
  /api/v4/users/<id>/image) gets no cookie cross-site and 401s.
- Files sidebar: each workspace under its owner's home, chevrons remembered
  per browser (agenthub.files.homesOpen); ownerless ones stay flat.
This commit is contained in:
2026-10-22 14:34:50 +07:00
parent 649f0599f8
commit 26952ae0d9
19 changed files with 463 additions and 289 deletions
+4 -3
View File
@@ -1933,9 +1933,10 @@ try {
}
// An account's own display name and email (the Users page's Edit user and
// Settings → Account), and when its avatar was last set (server/lib/avatars.js
// — the image is a file, the column only versions its URL). Additive; after
// the rebuild above, which predates them.
for (const col of ["display_name", "email", "avatar_at"]) {
// — the image is a file, the column only versions its URL — or `avatar_link`,
// a link the avatar routes redirect to). Additive; after the rebuild above,
// which predates them.
for (const col of ["display_name", "email", "avatar_at", "avatar_link"]) {
try {
db.prepare(`SELECT ${col} FROM users LIMIT 1`).get();
} catch {