// The hub's tables: Better Auth's (people, sessions, passwords, signing keys, device sign-ins) and the hub's own // (linked IMVU accounts, link codes, the audit log). Better Auth's are as its CLI generates them for the plugins // used (username, admin, device-authorization, bearer, jwt), with the hub's linkCap added to user. import { sql } from "drizzle-orm"; import { bigint, boolean, index, integer, jsonb, pgTable, primaryKey, serial, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core"; const createdAt = () => timestamp("created_at", { withTimezone: true }).notNull().defaultNow(); const updatedAt = () => timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow() .$onUpdate(() => new Date()); // A person: one T5DE account. export const user = pgTable( "user", { id: text("id").primaryKey(), name: text("name").notNull(), email: text("email").notNull().unique(), emailVerified: boolean("email_verified").notNull().default(false), image: text("image"), createdAt: createdAt(), updatedAt: updatedAt(), username: text("username").unique(), displayUsername: text("display_username"), role: text("role").notNull().default("user"), banned: boolean("banned").default(false), banReason: text("ban_reason"), banExpires: timestamp("ban_expires", { withTimezone: true }), // How many IMVU accounts the person may link; moderators raise it on request. linkCap: integer("link_cap").notNull().default(10), }, (t) => [uniqueIndex("user_username_lower").on(sql`lower(${t.username})`)], ); export const session = pgTable( "session", { id: text("id").primaryKey(), expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), token: text("token").notNull().unique(), createdAt: createdAt(), updatedAt: updatedAt(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), impersonatedBy: text("impersonated_by"), }, (t) => [index("session_user").on(t.userId)], ); export const account = pgTable( "account", { id: text("id").primaryKey(), accountId: text("account_id").notNull(), providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), accessTokenExpiresAt: timestamp("access_token_expires_at", { withTimezone: true }), refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { withTimezone: true }), scope: text("scope"), password: text("password"), createdAt: createdAt(), updatedAt: updatedAt(), }, (t) => [index("account_user").on(t.userId)], ); export const verification = pgTable( "verification", { id: text("id").primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), createdAt: createdAt(), updatedAt: updatedAt(), }, (t) => [index("verification_identifier").on(t.identifier)], ); export const jwks = pgTable("jwks", { id: text("id").primaryKey(), publicKey: text("public_key").notNull(), privateKey: text("private_key").notNull(), createdAt: createdAt(), expiresAt: timestamp("expires_at", { withTimezone: true }), alg: text("alg"), crv: text("crv"), }); // A T5DE proxy signing in: it shows userCode, the person approves it on the website. export const deviceCode = pgTable("device_code", { id: text("id").primaryKey(), deviceCode: text("device_code").notNull(), userCode: text("user_code").notNull(), userId: text("user_id").references(() => user.id, { onDelete: "cascade" }), expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), status: text("status").notNull(), lastPolledAt: timestamp("last_polled_at", { withTimezone: true }), pollingInterval: integer("polling_interval"), clientId: text("client_id"), scope: text("scope"), createdAt: createdAt(), updatedAt: updatedAt(), }); // A linked IMVU account. Linking it again from another person moves it (the most recent proof wins). export const imvuLink = pgTable( "imvu_link", { imvuId: bigint("imvu_id", { mode: "number" }).primaryKey(), userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), // What catalogs and rooms see instead of the person, so a person's accounts can't be connected. pairwiseId: text("pairwise_id").notNull().unique(), username: text("username").notNull(), linkedAt: timestamp("linked_at", { withTimezone: true }).notNull().defaultNow(), showBadge: boolean("show_badge").notNull().default(false), showPresence: boolean("show_presence").notNull().default(false), listRooms: boolean("list_rooms").notNull().default(false), }, (t) => [index("imvu_link_user").on(t.userId)], ); // An IMVU room a T5DE user entered whose owner is a linked account, as IMVU last described it. Listed on the hub // only if its owner chose to list their rooms and IMVU says it's public and listed. export const room = pgTable( "room", { instanceId: text("instance_id").primaryKey(), // "-" ownerImvuId: bigint("owner_imvu_id", { mode: "number" }).notNull(), name: text("name").notNull(), description: text("description").notNull().default(""), imageURL: text("image_url").notNull().default(""), privacy: text("privacy").notNull(), type: text("type").notNull(), contentRating: text("content_rating").notNull().default(""), accessPass: boolean("access_pass").notNull().default(false), occupancy: integer("occupancy").notNull().default(0), capacity: integer("capacity").notNull().default(0), expired: boolean("expired").notNull().default(false), checkedAt: timestamp("checked_at", { withTimezone: true }).notNull().defaultNow(), seenAt: timestamp("seen_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [index("room_owner").on(t.ownerImvuId)], ); // An age check by one provider: of a person, or of one of their linked IMVU accounts. Only the outcome: never a // birth date, document, photo or name (providers that see those keep them). export const ageCheck = pgTable( "age_check", { id: text("id").primaryKey(), userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), // Set when the check is of one IMVU account (IMVU's own verification); null when it's of the person. imvuId: bigint("imvu_id", { mode: "number" }), provider: text("provider").notNull(), // "imvu", "didit", "wallet", ... method: text("method").notNull().default(""), // e.g. "imvu_age_verified", "id_document", "wallet_mdoc" status: text("status").notNull(), // "pending", "complete", "failed", "revoked" overEighteen: boolean("over_eighteen").notNull().default(false), providerRef: text("provider_ref"), // the provider's session or transaction ID createdAt: createdAt(), checkedAt: timestamp("checked_at", { withTimezone: true }), expiresAt: timestamp("expires_at", { withTimezone: true }), }, (t) => [ index("age_check_user").on(t.userId), index("age_check_imvu").on(t.imvuId), uniqueIndex("age_check_provider_ref").on(t.provider, t.providerRef), ], ); // A code the person is to put in an IMVU account's tagline, proving it's theirs. export const linkChallenge = pgTable( "link_challenge", { userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), imvuId: bigint("imvu_id", { mode: "number" }).notNull(), username: text("username").notNull(), code: text("code").notNull(), createdAt: createdAt(), }, (t) => [primaryKey({ columns: [t.userId, t.imvuId] })], ); export const audit = pgTable( "audit", { id: serial("id").primaryKey(), at: timestamp("at", { withTimezone: true }).notNull().defaultNow(), actorId: text("actor_id").references(() => user.id, { onDelete: "set null" }), action: text("action").notNull(), target: text("target").notNull().default(""), detail: jsonb("detail").$type>().notNull().default({}), }, (t) => [index("audit_at").on(t.at)], );