101 lines
2.7 KiB
JavaScript
101 lines
2.7 KiB
JavaScript
import { db } from '../db/index.js';
|
|
|
|
const selectGroupsWithMembers = db.prepare(`
|
|
SELECT
|
|
g.id,
|
|
g.name,
|
|
g.description,
|
|
g.created_at,
|
|
g.updated_at,
|
|
COUNT(DISTINCT u.id) AS member_count,
|
|
GROUP_CONCAT(u.id || '::' || u.username, '|||') AS member_pairs
|
|
FROM user_groups g
|
|
LEFT JOIN user_group_memberships m ON m.group_id = g.id
|
|
LEFT JOIN users u ON u.id = m.user_id
|
|
GROUP BY g.id
|
|
ORDER BY g.name COLLATE NOCASE
|
|
`);
|
|
|
|
const selectGroupWithMembersById = db.prepare(`
|
|
SELECT
|
|
g.id,
|
|
g.name,
|
|
g.description,
|
|
g.created_at,
|
|
g.updated_at,
|
|
COUNT(DISTINCT u.id) AS member_count,
|
|
GROUP_CONCAT(u.id || '::' || u.username, '|||') AS member_pairs
|
|
FROM user_groups g
|
|
LEFT JOIN user_group_memberships m ON m.group_id = g.id
|
|
LEFT JOIN users u ON u.id = m.user_id
|
|
WHERE g.id = ?
|
|
GROUP BY g.id
|
|
`);
|
|
|
|
const selectGroupIdByName = db.prepare(`
|
|
SELECT id
|
|
FROM user_groups
|
|
WHERE lower(name) = lower(?)
|
|
LIMIT 1
|
|
`);
|
|
|
|
const insertGroupStatement = db.prepare(`
|
|
INSERT INTO user_groups (name, description, created_at, updated_at)
|
|
VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
`);
|
|
|
|
const parseMembers = (rawPairs) => {
|
|
if (!rawPairs) {
|
|
return [];
|
|
}
|
|
|
|
return rawPairs
|
|
.split('|||')
|
|
.map((entry) => {
|
|
const [idPart, usernamePart] = entry.split('::');
|
|
const numericId = Number(idPart);
|
|
return {
|
|
id: Number.isFinite(numericId) ? numericId : idPart,
|
|
username: (usernamePart || '').trim()
|
|
};
|
|
})
|
|
.filter((member) => member.username);
|
|
};
|
|
|
|
const sanitizeGroup = (row) => ({
|
|
id: row.id,
|
|
name: row.name,
|
|
description: row.description || '',
|
|
createdAt: row.created_at || null,
|
|
updatedAt: row.updated_at || null,
|
|
memberCount: Number(row.member_count) || 0,
|
|
members: parseMembers(row.member_pairs)
|
|
});
|
|
|
|
export function listGroups() {
|
|
const rows = selectGroupsWithMembers.all();
|
|
return rows.map(sanitizeGroup);
|
|
}
|
|
|
|
export function createGroup({ name, description }) {
|
|
const normalizedName = typeof name === 'string' ? name.trim() : '';
|
|
if (!normalizedName) {
|
|
const error = new Error('GROUP_NAME_REQUIRED');
|
|
error.code = 'GROUP_NAME_REQUIRED';
|
|
throw error;
|
|
}
|
|
|
|
const existing = selectGroupIdByName.get(normalizedName);
|
|
if (existing) {
|
|
const error = new Error('GROUP_NAME_TAKEN');
|
|
error.code = 'GROUP_NAME_TAKEN';
|
|
throw error;
|
|
}
|
|
|
|
const normalizedDescription = typeof description === 'string' ? description.trim() : null;
|
|
const insertResult = insertGroupStatement.run(normalizedName, normalizedDescription || null);
|
|
const groupId = Number(insertResult.lastInsertRowid);
|
|
const row = selectGroupWithMembersById.get(groupId);
|
|
return row ? sanitizeGroup(row) : null;
|
|
}
|