@@ -96,6 +96,144 @@ export const auth = betterAuth({
9696 organizationLimit : 1 , // Users can only be in one organization
9797 roles : WXYCRoles ,
9898 // Role information is included via custom JWT definePayload function above
99+ organizationHooks : {
100+ // Sync global user.role when members are added to default organization
101+ afterAddMember : async ( { member, user : userData , organization : orgData } ) => {
102+ try {
103+ const defaultOrgSlug = process . env . DEFAULT_ORG_SLUG ;
104+ if ( ! defaultOrgSlug ) {
105+ console . warn (
106+ "DEFAULT_ORG_SLUG is not set, skipping admin role sync"
107+ ) ;
108+ return ;
109+ }
110+
111+ // Only update for default organization
112+ if ( orgData . slug !== defaultOrgSlug ) {
113+ return ;
114+ }
115+
116+ // Check if role should grant admin permissions
117+ const adminRoles = [ "stationManager" , "admin" , "owner" ] ;
118+ if ( adminRoles . includes ( member . role ) ) {
119+ // Update user.role to "admin" for Better Auth Admin plugin
120+ const userId = userData . id ;
121+ await db
122+ . update ( user )
123+ . set ( { role : "admin" } )
124+ . where ( eq ( user . id , userId ) ) ;
125+ console . log (
126+ `Granted admin role to user ${ userId } (${ userData . email } ) with ${ member . role } role in default organization`
127+ ) ;
128+ }
129+ } catch ( error ) {
130+ console . error ( "Error syncing admin role in afterAddMember:" , error ) ;
131+ }
132+ } ,
133+
134+ // Sync global user.role when member roles are updated
135+ afterUpdateMemberRole : async ( {
136+ member,
137+ previousRole,
138+ user : userData ,
139+ organization : orgData ,
140+ } ) => {
141+ try {
142+ const defaultOrgSlug = process . env . DEFAULT_ORG_SLUG ;
143+ if ( ! defaultOrgSlug ) {
144+ console . warn (
145+ "DEFAULT_ORG_SLUG is not set, skipping admin role sync"
146+ ) ;
147+ return ;
148+ }
149+
150+ // Only update for default organization
151+ if ( orgData . slug !== defaultOrgSlug ) {
152+ return ;
153+ }
154+
155+ const adminRoles = [ "stationManager" , "admin" , "owner" ] ;
156+ const shouldHaveAdmin = adminRoles . includes ( member . role ) ;
157+ const previouslyHadAdmin = adminRoles . includes ( previousRole ) ;
158+
159+ const userId = userData . id ;
160+ if ( shouldHaveAdmin && ! previouslyHadAdmin ) {
161+ // Promoted to admin role - grant admin
162+ await db
163+ . update ( user )
164+ . set ( { role : "admin" } )
165+ . where ( eq ( user . id , userId ) ) ;
166+ console . log (
167+ `Granted admin role to user ${ userId } (${ userData . email } ) after promotion to ${ member . role } `
168+ ) ;
169+ } else if ( ! shouldHaveAdmin && previouslyHadAdmin ) {
170+ // Demoted from admin role - remove admin
171+ await db
172+ . update ( user )
173+ . set ( { role : null } )
174+ . where ( eq ( user . id , userId ) ) ;
175+ console . log (
176+ `Removed admin role from user ${ userId } (${ userData . email } ) after demotion from ${ previousRole } to ${ member . role } `
177+ ) ;
178+ }
179+ } catch ( error ) {
180+ console . error (
181+ "Error syncing admin role in afterUpdateMemberRole:" ,
182+ error
183+ ) ;
184+ }
185+ } ,
186+
187+ // Sync global user.role when members are removed from default organization
188+ afterRemoveMember : async ( { user : userData , organization : orgData } ) => {
189+ try {
190+ const defaultOrgSlug = process . env . DEFAULT_ORG_SLUG ;
191+ if ( ! defaultOrgSlug ) {
192+ console . warn (
193+ "DEFAULT_ORG_SLUG is not set, skipping admin role sync"
194+ ) ;
195+ return ;
196+ }
197+
198+ // Only update for default organization
199+ if ( orgData . slug !== defaultOrgSlug ) {
200+ return ;
201+ }
202+
203+ // Check if user has any other memberships with admin roles
204+ const otherAdminMemberships = await db
205+ . select ( { role : member . role } )
206+ . from ( member )
207+ . innerJoin (
208+ organization ,
209+ sql `${ member . organizationId } = ${ organization . id } ` as any
210+ )
211+ . where (
212+ sql `${ member . userId } = ${ userData . id }
213+ AND ${ organization . slug } = ${ defaultOrgSlug }
214+ AND ${ member . role } IN ('admin', 'owner', 'stationManager')` as any
215+ )
216+ . limit ( 1 ) ;
217+
218+ // If no other admin memberships exist, remove admin role
219+ if ( otherAdminMemberships . length === 0 ) {
220+ const userId = userData . id ;
221+ await db
222+ . update ( user )
223+ . set ( { role : null } )
224+ . where ( eq ( user . id , userId ) ) ;
225+ console . log (
226+ `Removed admin role from user ${ userId } (${ userData . email } ) after removal from default organization`
227+ ) ;
228+ }
229+ } catch ( error ) {
230+ console . error (
231+ "Error syncing admin role in afterRemoveMember:" ,
232+ error
233+ ) ;
234+ }
235+ } ,
236+ } ,
99237 } ) ,
100238 ] ,
101239
@@ -110,43 +248,6 @@ export const auth = betterAuth({
110248 } ,
111249 } ,
112250
113- // Admin configuration - organization owners/admins/stationManagers are admins
114- admin : {
115- // Check if user has admin, owner, or stationManager role in the WXYC organization
116- requireAdmin : async ( user : any , request ?: any ) => {
117- if ( ! user ?. id ) return false ;
118-
119- try {
120- const defaultOrgSlug = process . env . DEFAULT_ORG_SLUG ;
121-
122- if ( ! defaultOrgSlug ) {
123- throw new Error (
124- "DEFAULT_ORG_SLUG is not set in environment variables."
125- ) ;
126- }
127-
128- // Query the member table to check if user has admin/owner/stationManager role in WXYC org
129- const adminMember = await db
130- . select ( { memberId : member . id } )
131- . from ( member )
132- . innerJoin (
133- organization ,
134- sql `${ member . organizationId } = ${ organization . id } ` as any
135- )
136- . where (
137- sql `${ member . userId } = ${ user . id }
138- AND ${ organization . slug } = ${ defaultOrgSlug }
139- AND ${ member . role } IN ('admin', 'owner', 'stationManager')` as any
140- )
141- . limit ( 1 ) ;
142-
143- return adminMember . length > 0 ;
144- } catch ( error ) {
145- console . error ( "Error checking admin status:" , error ) ;
146- return false ;
147- }
148- } ,
149- } ,
150251} ) ;
151252
152253export type Auth = typeof auth ;
0 commit comments