File size: 4,011 Bytes
7707ce7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71bfeca
 
 
7707ce7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { Router, Response } from 'express';
import { supabase } from '../../db/client';
import { logger } from '../../utils/logger';
import { authenticateUser, AuthenticatedRequest } from '../middleware/auth';

const router = Router();

const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const ALLOWED_ROLES = new Set(['user', 'admin']);

async function isAdmin(userId: string): Promise<boolean> {
  const { data, error } = await supabase
    .from('user_profiles')
    .select('role')
    .eq('id', userId)
    .single();

  if (error) {
    logger.warn('Failed to verify admin for user management', {
      userId,
      error: error.message,
    });
    return false;
  }

  return data?.role === 'admin';
}

router.post(
  '/api/admin/users',
  authenticateUser,
  async (req, res: Response): Promise<void> => {
    const authReq = req as AuthenticatedRequest;

    try {
      if (!(await isAdmin(authReq.userId))) {
        res.status(403).json({ error: 'Admin access required' });
        return;
      }

      const email = String(authReq.body.email || '').trim().toLowerCase();
      const password = String(authReq.body.password || '');
      const displayName = String(authReq.body.displayName || '').trim();
      const role = String(authReq.body.role || 'user').trim().toLowerCase();
      const ticketBalance = Number(authReq.body.ticketBalance ?? 0);

      if (!EMAIL_PATTERN.test(email)) {
        res.status(400).json({ error: 'A valid email address is required' });
        return;
      }
      if (password.length < 8 || password.length > 128) {
        res.status(400).json({ error: 'Password must contain 8 to 128 characters' });
        return;
      }
      if (displayName.length > 80) {
        res.status(400).json({ error: 'Display name must not exceed 80 characters' });
        return;
      }
      if (!ALLOWED_ROLES.has(role)) {
        res.status(400).json({ error: 'Role must be user or admin' });
        return;
      }
      if (!Number.isInteger(ticketBalance) || ticketBalance < 0 || ticketBalance > 1_000_000) {
        res.status(400).json({ error: 'Ticket balance must be an integer from 0 to 1,000,000' });
        return;
      }

      const { data: created, error: createError } = await supabase.auth.admin.createUser({
        email,
        password,
        email_confirm: true,
        app_metadata: {
          dashboard_provisioned: true,
        },
        user_metadata: {
          display_name: displayName || email.split('@')[0],
        },
      });

      if (createError || !created.user) {
        const message = createError?.message || 'Supabase did not return the created user';
        const status = /already|registered|exists/i.test(message) ? 409 : 400;
        res.status(status).json({ error: message });
        return;
      }

      const { data: profile, error: profileError } = await supabase
        .from('user_profiles')
        .update({
          display_name: displayName || email.split('@')[0],
          role,
          ticket_balance: ticketBalance,
          updated_at: new Date().toISOString(),
        })
        .eq('id', created.user.id)
        .select('id, email, display_name, role, ticket_balance, created_at')
        .single();

      if (profileError || !profile) {
        await supabase.auth.admin.deleteUser(created.user.id).catch(() => undefined);
        throw profileError || new Error('User profile was not created');
      }

      logger.info('Dashboard account created by admin', {
        adminUserId: authReq.userId,
        createdUserId: created.user.id,
        role,
        ticketBalance,
      });

      res.status(201).json({ user: profile });
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : String(error);
      logger.error('Admin user creation failed', {
        adminUserId: authReq.userId,
        error: message,
      });
      res.status(500).json({ error: 'Failed to create dashboard account' });
    }
  },
);

export default router;