File size: 9,694 Bytes
6980b1d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const uuid_1 = require("uuid");
const google_auth_library_1 = require("google-auth-library");
const database_1 = require("../db/database");
const auth_1 = require("../middleware/auth");
const router = express_1.default.Router();
const client = new google_auth_library_1.OAuth2Client(process.env.GOOGLE_CLIENT_ID);
// Sign up with email/password
router.post('/signup', async (req, res) => {
    try {
        const { email, password, name } = req.body;
        // Validation
        if (!email || !password || !name) {
            return res.status(400).json({ error: 'Email, password, and name are required' });
        }
        if (password.length < 6) {
            return res.status(400).json({ error: 'Password must be at least 6 characters' });
        }
        // Check if user already exists
        const existingUser = await database_1.database.get('SELECT id FROM users WHERE email = ?', [email]);
        if (existingUser) {
            return res.status(400).json({ error: 'User already exists' });
        }
        // Hash password
        const saltRounds = 10;
        const passwordHash = await bcryptjs_1.default.hash(password, saltRounds);
        // Generate verification token
        const verificationToken = (0, uuid_1.v4)();
        // Create user
        const result = await database_1.database.run('INSERT INTO users (email, name, password_hash, verification_token) VALUES (?, ?, ?, ?)', [email, name, passwordHash, verificationToken]);
        // Create default tenant for user
        const tenantResult = await database_1.database.run('INSERT INTO tenants (name, subdomain) VALUES (?, ?)', [`${name}'s Workspace`, `tenant-${result.lastID}`]);
        // Link user to tenant
        await database_1.database.run('INSERT INTO user_tenants (user_id, tenant_id, role) VALUES (?, ?, ?)', [result.lastID, tenantResult.lastID, 'owner']);
        // Generate JWT token
        const token = jsonwebtoken_1.default.sign({ userId: result.lastID, tenantId: tenantResult.lastID }, process.env.JWT_SECRET, { expiresIn: '7d' });
        // Get user data
        const user = await database_1.database.get('SELECT id, email, name, avatar, created_at FROM users WHERE id = ?', [result.lastID]);
        res.status(201).json({
            message: 'User created successfully',
            token,
            user,
            tenant: { id: tenantResult.lastID, name: `${name}'s Workspace` }
        });
    }
    catch (error) {
        console.error('Signup error:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
});
// Sign in with email/password
router.post('/signin', async (req, res) => {
    try {
        const { email, password } = req.body;
        if (!email || !password) {
            return res.status(400).json({ error: 'Email and password are required' });
        }
        // Get user
        const user = await database_1.database.get('SELECT * FROM users WHERE email = ?', [email]);
        if (!user || !user.password_hash) {
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        // Check password
        const isValidPassword = await bcryptjs_1.default.compare(password, user.password_hash);
        if (!isValidPassword) {
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        // Get user's tenant
        const userTenant = await database_1.database.get(`SELECT t.id, t.name, t.subdomain, t.plan 

       FROM tenants t 

       JOIN user_tenants ut ON t.id = ut.tenant_id 

       WHERE ut.user_id = ? AND ut.role = 'owner'`, [user.id]);
        // Generate JWT token
        const token = jsonwebtoken_1.default.sign({ userId: user.id, tenantId: userTenant?.id }, process.env.JWT_SECRET, { expiresIn: '7d' });
        // Remove sensitive data
        const { password_hash, verification_token, ...safeUser } = user;
        res.json({
            message: 'Signed in successfully',
            token,
            user: safeUser,
            tenant: userTenant
        });
    }
    catch (error) {
        console.error('Signin error:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
});
// Google OAuth
router.post('/google', async (req, res) => {
    try {
        const { credential } = req.body;
        if (!credential) {
            return res.status(400).json({ error: 'Google credential is required' });
        }
        // Verify Google token
        const ticket = await client.verifyIdToken({
            idToken: credential,
            audience: process.env.GOOGLE_CLIENT_ID,
        });
        const payload = ticket.getPayload();
        if (!payload) {
            return res.status(400).json({ error: 'Invalid Google token' });
        }
        const { sub: googleId, email, name, picture } = payload;
        // Check if user exists
        let user = await database_1.database.get('SELECT * FROM users WHERE google_id = ? OR email = ?', [googleId, email]);
        if (user) {
            // Update Google ID if needed
            if (!user.google_id) {
                await database_1.database.run('UPDATE users SET google_id = ?, avatar = ? WHERE id = ?', [googleId, picture, user.id]);
                user.google_id = googleId;
                user.avatar = picture;
            }
        }
        else {
            // Create new user
            const result = await database_1.database.run('INSERT INTO users (email, name, google_id, avatar, email_verified) VALUES (?, ?, ?, ?, ?)', [email, name, googleId, picture, true]);
            // Create default tenant
            const tenantResult = await database_1.database.run('INSERT INTO tenants (name, subdomain) VALUES (?, ?)', [`${name}'s Workspace`, `tenant-${result.lastID}`]);
            // Link user to tenant
            await database_1.database.run('INSERT INTO user_tenants (user_id, tenant_id, role) VALUES (?, ?, ?)', [result.lastID, tenantResult.lastID, 'owner']);
            user = {
                id: result.lastID,
                email,
                name,
                google_id: googleId,
                avatar: picture,
                email_verified: true
            };
        }
        // Get user's tenant
        const userTenant = await database_1.database.get(`SELECT t.id, t.name, t.subdomain, t.plan 

       FROM tenants t 

       JOIN user_tenants ut ON t.id = ut.tenant_id 

       WHERE ut.user_id = ? AND ut.role = 'owner'`, [user.id]);
        // Generate JWT token
        const token = jsonwebtoken_1.default.sign({ userId: user.id, tenantId: userTenant?.id }, process.env.JWT_SECRET, { expiresIn: '7d' });
        // Remove sensitive data
        const { password_hash, verification_token, ...safeUser } = user;
        res.json({
            message: 'Google authentication successful',
            token,
            user: safeUser,
            tenant: userTenant
        });
    }
    catch (error) {
        console.error('Google auth error:', error);
        res.status(500).json({ error: 'Google authentication failed' });
    }
});
// Get current user
router.get('/me', auth_1.authenticateToken, async (req, res) => {
    try {
        const { userId, tenantId } = req.user;
        // Get user data
        const user = await database_1.database.get('SELECT id, email, name, avatar, email_verified, created_at FROM users WHERE id = ?', [userId]);
        if (!user) {
            return res.status(404).json({ error: 'User not found' });
        }
        // Get tenant data
        const tenant = await database_1.database.get('SELECT id, name, subdomain, plan, settings FROM tenants WHERE id = ?', [tenantId]);
        res.json({
            user,
            tenant
        });
    }
    catch (error) {
        console.error('Get user error:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
});
// Update user profile
router.put('/profile', auth_1.authenticateToken, async (req, res) => {
    try {
        const { userId } = req.user;
        const { name, avatar } = req.body;
        const updates = [];
        const params = [];
        if (name) {
            updates.push('name = ?');
            params.push(name);
        }
        if (avatar) {
            updates.push('avatar = ?');
            params.push(avatar);
        }
        if (updates.length === 0) {
            return res.status(400).json({ error: 'No valid fields to update' });
        }
        updates.push('updated_at = CURRENT_TIMESTAMP');
        params.push(userId);
        await database_1.database.run(`UPDATE users SET ${updates.join(', ')} WHERE id = ?`, params);
        // Get updated user
        const user = await database_1.database.get('SELECT id, email, name, avatar, email_verified, created_at FROM users WHERE id = ?', [userId]);
        res.json({
            message: 'Profile updated successfully',
            user
        });
    }
    catch (error) {
        console.error('Update profile error:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
});
// Logout (client-side token removal, but we can blacklist if needed)
router.post('/logout', auth_1.authenticateToken, (req, res) => {
    res.json({ message: 'Logged out successfully' });
});
exports.default = router;
//# sourceMappingURL=auth.js.map