feat: add profile fields for local users

DB→domain→API→AP→frontend end-to-end. Fields stored as
JSONB, exposed via PATCH /users/me, serialized as AP
PropertyValue attachment. Editor in federation settings,
display on profile card.
This commit is contained in:
2026-05-29 13:54:25 +02:00
parent 14a869cc8d
commit 805bd9534f
19 changed files with 224 additions and 27 deletions

View File

@@ -1,7 +1,9 @@
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { getMe } from "@/lib/api";
import { FederationPanel } from "@/components/federation/federation-panel";
import { MigrationSettings } from "@/components/federation/migration-settings";
import { ProfileFieldsEditor } from "@/components/profile-fields-editor";
export default async function FederationSettingsPage() {
const token = (await cookies()).get("auth_token")?.value;
@@ -9,6 +11,8 @@ export default async function FederationSettingsPage() {
redirect("/login");
}
const me = await getMe(token);
return (
<div className="space-y-6">
<div className="glass-effect glossy-effect bottom rounded-md shadow-fa-lg p-4">
@@ -18,6 +22,7 @@ export default async function FederationSettingsPage() {
other instances.
</p>
</div>
<ProfileFieldsEditor initial={me.profileFields} />
<FederationPanel />
<MigrationSettings />
</div>

View File

@@ -224,6 +224,27 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
{user.bio}
</p>
{user.profileFields.length > 0 && (
<div className="mt-4 space-y-0 text-sm">
{user.profileFields.map((field) => (
<div
key={field.name}
className="grid grid-cols-[minmax(0,5rem)_1fr] gap-2 border-t py-1"
>
<span
className="font-medium text-muted-foreground truncate"
title={field.name}
>
{field.name}
</span>
<span className="break-all min-w-0">
{field.value}
</span>
</div>
))}
</div>
)}
{isOwnProfile && (
<div
id="profile-card__stats"