Barazo AppView backend
barazo.forum
1import { pgTable, text, timestamp, boolean, integer, index, jsonb } from 'drizzle-orm/pg-core'
2import { sql } from 'drizzle-orm'
3
4export const users = pgTable(
5 'users',
6 {
7 did: text('did').primaryKey(),
8 handle: text('handle').notNull(),
9 displayName: text('display_name'),
10 avatarUrl: text('avatar_url'),
11 bannerUrl: text('banner_url'),
12 bio: text('bio'),
13 role: text('role', { enum: ['user', 'moderator', 'admin'] })
14 .notNull()
15 .default('user'),
16 isBanned: boolean('is_banned').notNull().default(false),
17 reputationScore: integer('reputation_score').notNull().default(0),
18 firstSeenAt: timestamp('first_seen_at', { withTimezone: true }).notNull().defaultNow(),
19 lastActiveAt: timestamp('last_active_at', { withTimezone: true }).notNull().defaultNow(),
20 declaredAge: integer('declared_age'),
21 maturityPref: text('maturity_pref', {
22 enum: ['safe', 'mature', 'adult'],
23 })
24 .notNull()
25 .default('safe'),
26 /** Account creation date resolved from PLC directory on first encounter. */
27 accountCreatedAt: timestamp('account_created_at', { withTimezone: true }),
28 followersCount: integer('followers_count').notNull().default(0),
29 followsCount: integer('follows_count').notNull().default(0),
30 atprotoPostsCount: integer('atproto_posts_count').notNull().default(0),
31 hasBlueskyProfile: boolean('has_bluesky_profile').notNull().default(false),
32 /** AT Protocol labels from the Bluesky AppView (self-applied and moderator-applied). */
33 atprotoLabels: jsonb('atproto_labels')
34 .$type<Array<{ val: string; src: string; neg: boolean; cts: string }>>()
35 .notNull()
36 .default([]),
37 },
38 (table) => [
39 index('users_role_elevated_idx')
40 .on(table.role)
41 .where(sql`role IN ('moderator', 'admin')`),
42 index('users_handle_idx').on(table.handle),
43 index('users_account_created_at_idx').on(table.accountCreatedAt),
44 ]
45)