UpdateUser must not hand frontend_settings to xorm as a Go value. The
column is declared interface{} with `xorm:"json null"`, and xorm
mishandles both forms a caller might leave there:
- a map[string]any is passed straight to the driver, which rejects it
("unsupported type map[string]interface {}, a map") -- a 500 on every
settings save;
- a []byte (what the old code produced via json.Marshal) is base64
encoded, so the column ended up holding e.g. "bnVsbA==" (base64 of
"null"). Clients that expect an object (the Flutter app) then crash
while decoding a string.
Marshal the value to a JSON string ourselves so xorm stores it verbatim,
and only write the column for forceOverride callers -- the user-settings
endpoints, which are the only callers that set it. Other callers (OIDC
login, avatar upload, ...) leave it out entirely: listing a nil interface
in Cols makes xorm write NULL, which would wipe a user's settings on
every login.
Add migration 20260627101958 to repair rows already double-encoded by the
old code, decoding the base64 back to the original object or to NULL.
Values that do not base64-decode to a JSON null or object are left as-is,
so a legitimate string-valued setting is never rewritten.