feat: profiles and /set profile, and id cards
* New functionality to set meta data on businesscards. * Can develop a user profile. * Business cards link to user profile.
This commit is contained in:
parent
cd8dfb94a3
commit
710985638f
35 changed files with 4932 additions and 435 deletions
|
|
@ -16,6 +16,7 @@ CREATE TABLE auth.users (
|
|||
|
||||
username auth.username NOT NULL,
|
||||
email auth.email,
|
||||
phone TEXT, -- TODO: migrate to auth.phone_number domain
|
||||
password_hash TEXT,
|
||||
auth_provider auth.auth_provider NOT NULL DEFAULT 'local',
|
||||
oauth_id TEXT,
|
||||
|
|
@ -24,6 +25,16 @@ CREATE TABLE auth.users (
|
|||
bio TEXT,
|
||||
avatar_url public.url,
|
||||
|
||||
-- HOSS membership profile fields
|
||||
name_first TEXT,
|
||||
name_last TEXT,
|
||||
summary TEXT, -- Brief one-liner tagline
|
||||
homepage public.url,
|
||||
avatar_source auth.avatar_source NOT NULL DEFAULT 'local',
|
||||
profile_visibility auth.profile_visibility NOT NULL DEFAULT 'public',
|
||||
contacts_visibility auth.profile_visibility NOT NULL DEFAULT 'members',
|
||||
organizations_visibility auth.profile_visibility NOT NULL DEFAULT 'members',
|
||||
|
||||
-- User preferences for default avatar selection
|
||||
birthday DATE,
|
||||
gender_preference auth.gender_preference NOT NULL DEFAULT 'gender_neutral',
|
||||
|
|
@ -127,6 +138,71 @@ CREATE INDEX idx_auth_friendships_friend_a ON auth.friendships (friend_a);
|
|||
CREATE INDEX idx_auth_friendships_friend_b ON auth.friendships (friend_b);
|
||||
CREATE INDEX idx_auth_friendships_pending ON auth.friendships (is_accepted) WHERE is_accepted = false;
|
||||
|
||||
-- =============================================================================
|
||||
-- User Contacts (Social/Contact Links)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE auth.user_contacts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
platform auth.contact_platform NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
label TEXT, -- Optional display label
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
|
||||
CONSTRAINT uq_auth_user_contacts_platform UNIQUE (user_id, platform, value),
|
||||
CONSTRAINT chk_auth_user_contacts_value_nonempty CHECK (length(trim(value)) > 0)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE auth.user_contacts IS 'User social/contact platform links';
|
||||
COMMENT ON COLUMN auth.user_contacts.platform IS 'Type of contact (discord, github, linkedin, etc.)';
|
||||
COMMENT ON COLUMN auth.user_contacts.value IS 'Platform-specific identifier (handle, URL, phone, etc.)';
|
||||
COMMENT ON COLUMN auth.user_contacts.label IS 'Optional user-friendly display label';
|
||||
COMMENT ON COLUMN auth.user_contacts.sort_order IS 'Display order (lower = first)';
|
||||
|
||||
CREATE INDEX idx_auth_user_contacts_user ON auth.user_contacts (user_id);
|
||||
CREATE INDEX idx_auth_user_contacts_platform ON auth.user_contacts (platform);
|
||||
|
||||
-- =============================================================================
|
||||
-- User Organizations (Affiliations)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE auth.user_organizations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
wikidata_qid public.wikidata_qid, -- Wikidata Q-number for org
|
||||
name TEXT NOT NULL, -- Display name (may differ from Wikidata)
|
||||
role TEXT, -- User's role/title
|
||||
department TEXT, -- Department/division
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
is_current BOOLEAN NOT NULL DEFAULT true,
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
|
||||
CONSTRAINT chk_auth_user_orgs_name_nonempty CHECK (length(trim(name)) > 0),
|
||||
CONSTRAINT chk_auth_user_orgs_dates CHECK (start_date IS NULL OR end_date IS NULL OR start_date <= end_date)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE auth.user_organizations IS 'User organizational affiliations';
|
||||
COMMENT ON COLUMN auth.user_organizations.wikidata_qid IS 'Wikidata Q-number for standardized organization lookup';
|
||||
COMMENT ON COLUMN auth.user_organizations.name IS 'Display name for the organization';
|
||||
COMMENT ON COLUMN auth.user_organizations.role IS 'User role/title at the organization';
|
||||
COMMENT ON COLUMN auth.user_organizations.is_current IS 'Whether this is a current affiliation';
|
||||
|
||||
CREATE INDEX idx_auth_user_organizations_user ON auth.user_organizations (user_id);
|
||||
CREATE INDEX idx_auth_user_organizations_wikidata ON auth.user_organizations (wikidata_qid)
|
||||
WHERE wikidata_qid IS NOT NULL;
|
||||
CREATE INDEX idx_auth_user_organizations_current ON auth.user_organizations (user_id, is_current)
|
||||
WHERE is_current = true;
|
||||
|
||||
-- =============================================================================
|
||||
-- Block List
|
||||
-- =============================================================================
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ CREATE TABLE scene.loose_props (
|
|||
server_prop_id UUID REFERENCES server.props(id) ON DELETE CASCADE,
|
||||
realm_prop_id UUID REFERENCES realm.props(id) ON DELETE CASCADE,
|
||||
|
||||
-- Custom prop name (overrides source prop name when set)
|
||||
prop_name TEXT,
|
||||
|
||||
-- Position in scene (PostGIS point, SRID 0)
|
||||
position public.virtual_point NOT NULL,
|
||||
|
||||
|
|
@ -223,11 +226,15 @@ CREATE TABLE scene.loose_props (
|
|||
is_locked BOOLEAN NOT NULL DEFAULT false,
|
||||
locked_by UUID REFERENCES auth.users(id) ON DELETE SET NULL,
|
||||
|
||||
-- Public state columns (loose props only have public state)
|
||||
-- Public state columns
|
||||
server_state JSONB NOT NULL DEFAULT '{}',
|
||||
realm_state JSONB NOT NULL DEFAULT '{}',
|
||||
user_state JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
-- Private state (transferred to picker, not visible while on ground)
|
||||
-- Used for business card snapshots captured at drop time
|
||||
server_private_state JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
|
||||
-- Must reference exactly one source
|
||||
|
|
@ -238,6 +245,7 @@ CREATE TABLE scene.loose_props (
|
|||
);
|
||||
|
||||
COMMENT ON TABLE scene.loose_props IS 'Props dropped in instances that can be picked up';
|
||||
COMMENT ON COLUMN scene.loose_props.prop_name IS 'Custom prop name overriding the source prop name (used for renamed items like business cards)';
|
||||
COMMENT ON COLUMN scene.loose_props.position IS 'Location in scene as PostGIS point (SRID 0)';
|
||||
COMMENT ON COLUMN scene.loose_props.expires_at IS 'When prop auto-decays (NULL = permanent)';
|
||||
COMMENT ON COLUMN scene.loose_props.is_locked IS 'If true, only moderators can move/scale/pickup this prop';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue