← back to Watches
database/repositories/users.js
159 lines
/**
* Users Repository
* Data access layer for user operations
*/
import pool from '../connection.js';
class UsersRepository {
/**
* Find all users (admin only)
*/
async findAll(limit = 50, offset = 0) {
const result = await pool.query(
`SELECT id, email, name, avatar_url, role, provider, email_verified,
is_active, created_at, last_login_at, login_count
FROM users
WHERE is_active = true
ORDER BY created_at DESC
LIMIT $1 OFFSET $2`,
[limit, offset]
);
return result.rows;
}
/**
* Find user by ID
*/
async findById(id) {
const result = await pool.query(
`SELECT id, email, name, avatar_url, role, provider, email_verified,
is_active, created_at, last_login_at, login_count
FROM users WHERE id = $1`,
[id]
);
return result.rows[0] || null;
}
/**
* Find user by email
*/
async findByEmail(email) {
const result = await pool.query(
`SELECT * FROM users WHERE email = $1`,
[email.toLowerCase()]
);
return result.rows[0] || null;
}
/**
* Find user by provider ID
*/
async findByProviderId(provider, providerId) {
const result = await pool.query(
`SELECT * FROM users WHERE provider = $1 AND provider_id = $2`,
[provider, providerId]
);
return result.rows[0] || null;
}
/**
* Create new user
*/
async create(userData) {
const { email, name, avatar_url, provider, provider_id, password_hash, email_verified } = userData;
const result = await pool.query(
`INSERT INTO users (email, name, avatar_url, provider, provider_id, password_hash, email_verified)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *`,
[email.toLowerCase(), name, avatar_url, provider || 'email', provider_id, password_hash, email_verified || false]
);
return result.rows[0];
}
/**
* Update user
*/
async update(id, updates) {
const { name, avatar_url, email_verified, role, is_active } = updates;
const result = await pool.query(
`UPDATE users
SET name = COALESCE($1, name),
avatar_url = COALESCE($2, avatar_url),
email_verified = COALESCE($3, email_verified),
role = COALESCE($4, role),
is_active = COALESCE($5, is_active),
updated_at = NOW()
WHERE id = $6
RETURNING *`,
[name, avatar_url, email_verified, role, is_active, id]
);
return result.rows[0] || null;
}
/**
* Update login stats
*/
async recordLogin(id) {
await pool.query(
`UPDATE users SET last_login_at = NOW(), login_count = login_count + 1 WHERE id = $1`,
[id]
);
}
/**
* Soft delete user
*/
async delete(id) {
const result = await pool.query(
`UPDATE users
SET is_active = false, email = email || '_deleted_' || id, updated_at = NOW()
WHERE id = $1
RETURNING id`,
[id]
);
return result.rows.length > 0;
}
/**
* Hard delete user (use with caution)
*/
async hardDelete(id) {
const result = await pool.query(
`DELETE FROM users WHERE id = $1 RETURNING id`,
[id]
);
return result.rows.length > 0;
}
/**
* Count total users
*/
async count() {
const result = await pool.query(
`SELECT COUNT(*) as total FROM users WHERE is_active = true`
);
return parseInt(result.rows[0].total);
}
/**
* Search users by name or email
*/
async search(query, limit = 20) {
const result = await pool.query(
`SELECT id, email, name, avatar_url, role, created_at
FROM users
WHERE is_active = true
AND (name ILIKE $1 OR email ILIKE $1)
ORDER BY name
LIMIT $2`,
[`%${query}%`, limit]
);
return result.rows;
}
}
export default new UsersRepository();