feat: add Google OAuth login with USER role and pending approval flow
- Add GET /api/auth/google and GET /api/auth/callback/google routes with CSRF state protection and account linking via googleId - Add getPublicOrigin() for dynamic redirect_uri (supports reverse proxy via X-Forwarded-Proto) - Add USER role to schema (default for new Google sign-ins), make password optional, add googleId and image fields - Role-based redirect after login: USER → /profile, ADMIN/DEVELOPER → /dashboard - Profile page shows pending approval alert for USER role - Dashboard redirects USER role back to profile - Login page shows specific error messages per OAuth error code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -50,10 +50,8 @@ export const Route = createFileRoute('/users')({
|
||||
const fetcher = (url: string) => fetch(url).then((res) => res.json())
|
||||
|
||||
const getRoleColor = (role: string) => {
|
||||
const r = (role || '').toLowerCase()
|
||||
if (r.includes('super')) return 'red'
|
||||
if (r.includes('admin')) return 'brand-blue'
|
||||
if (r.includes('developer')) return 'violet'
|
||||
if (role === 'DEVELOPER') return 'violet'
|
||||
if (role === 'ADMIN') return 'brand-blue'
|
||||
return 'gray'
|
||||
}
|
||||
|
||||
@@ -97,7 +95,7 @@ function UsersPage() {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
role: 'USER',
|
||||
role: 'ADMIN',
|
||||
})
|
||||
|
||||
const handleCreateUser = async () => {
|
||||
@@ -119,7 +117,7 @@ function UsersPage() {
|
||||
mutateOperators()
|
||||
mutateStats()
|
||||
closeCreate()
|
||||
setCreateForm({ name: '', email: '', password: '', role: 'USER' })
|
||||
setCreateForm({ name: '', email: '', password: '', role: 'ADMIN' })
|
||||
} else {
|
||||
const err = await res.json()
|
||||
throw new Error(err.error || 'Failed to create user')
|
||||
@@ -457,11 +455,12 @@ function UsersPage() {
|
||||
<Select
|
||||
label="Role"
|
||||
data={[
|
||||
{ value: 'USER', label: 'User (Pending)' },
|
||||
{ value: 'ADMIN', label: 'Admin' },
|
||||
{ value: 'DEVELOPER', label: 'Developer' },
|
||||
]}
|
||||
value={editForm.role}
|
||||
onChange={(val) => setEditForm({ ...editForm, role: val || 'USER' })}
|
||||
onChange={(val) => setEditForm({ ...editForm, role: val || 'ADMIN' })}
|
||||
/>
|
||||
<Button
|
||||
fullWidth
|
||||
|
||||
Reference in New Issue
Block a user