v0.12.51 updated "Mixed Age" login type.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rosterchirp-backend",
|
||||
"version": "0.12.50",
|
||||
"version": "0.12.51",
|
||||
"description": "RosterChirp backend server",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -190,10 +190,10 @@ router.post('/', authMiddleware, async (req, res) => {
|
||||
await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, userId]);
|
||||
await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, otherUserId]);
|
||||
|
||||
// Mixed Age: if the other user is a minor, auto-add their guardian
|
||||
// Mixed Age: if initiator is not a minor and the other user is a minor, auto-add their guardian
|
||||
let guardianAdded = false, guardianName = null;
|
||||
const loginType = await getLoginType(req.schema);
|
||||
if (loginType === 'mixed_age') {
|
||||
if (loginType === 'mixed_age' && !req.user.is_minor) {
|
||||
const otherUserFull = await queryOne(req.schema,
|
||||
'SELECT is_minor, guardian_user_id FROM users WHERE id=$1', [otherUserId]);
|
||||
if (otherUserFull?.is_minor && otherUserFull.guardian_user_id) {
|
||||
@@ -234,6 +234,7 @@ router.post('/', authMiddleware, async (req, res) => {
|
||||
[name, type||'private', req.user.id, !!isReadonly]
|
||||
);
|
||||
const groupId = r.rows[0].id;
|
||||
const groupGuardianNames = [];
|
||||
if (type === 'public') {
|
||||
const allUsers = await query(req.schema, "SELECT id FROM users WHERE status='active'");
|
||||
for (const u of allUsers) await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, u.id]);
|
||||
@@ -251,9 +252,30 @@ router.post('/', authMiddleware, async (req, res) => {
|
||||
if (parseInt(totalCount.cnt) >= 3) {
|
||||
await computeAndStoreComposite(req.schema, groupId);
|
||||
}
|
||||
|
||||
// Mixed Age: auto-add guardians for any minor members (non-minor initiators only)
|
||||
const groupLoginType = await getLoginType(req.schema);
|
||||
if (groupLoginType === 'mixed_age' && !req.user.is_minor && memberIds?.length > 0) {
|
||||
for (const uid of memberIds) {
|
||||
const memberInfo = await queryOne(req.schema,
|
||||
'SELECT is_minor, guardian_user_id FROM users WHERE id=$1', [uid]);
|
||||
if (memberInfo?.is_minor && memberInfo.guardian_user_id && memberInfo.guardian_user_id !== req.user.id) {
|
||||
await exec(req.schema,
|
||||
'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING',
|
||||
[groupId, memberInfo.guardian_user_id]);
|
||||
const g = await queryOne(req.schema,
|
||||
'SELECT name,display_name FROM users WHERE id=$1', [memberInfo.guardian_user_id]);
|
||||
const gName = g?.display_name || g?.name;
|
||||
if (gName && !groupGuardianNames.includes(gName)) groupGuardianNames.push(gName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await emitGroupNew(req.schema, io, groupId);
|
||||
res.json({ group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [groupId]) });
|
||||
res.json({
|
||||
group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [groupId]),
|
||||
...(groupGuardianNames.length ? { guardianAdded: true, guardianName: groupGuardianNames.join(', ') } : {}),
|
||||
});
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
|
||||
@@ -80,18 +80,18 @@ router.get('/search', authMiddleware, async (req, res) => {
|
||||
const group = await queryOne(req.schema, 'SELECT type, is_direct FROM groups WHERE id = $1', [parseInt(groupId)]);
|
||||
if (group && (group.type === 'private' || group.is_direct)) {
|
||||
users = await query(req.schema,
|
||||
`SELECT u.id,u.name,u.display_name,u.avatar,u.role,u.status,u.hide_admin_tag,u.allow_dm FROM users u JOIN group_members gm ON gm.user_id=u.id AND gm.group_id=$1 WHERE u.status='active' AND u.id!=$2 AND (u.name ILIKE $3 OR u.display_name ILIKE $3) ORDER BY u.name ASC${isTyped ? ' LIMIT 10' : ''}`,
|
||||
`SELECT u.id,u.name,u.display_name,u.avatar,u.role,u.status,u.hide_admin_tag,u.allow_dm,u.is_minor FROM users u JOIN group_members gm ON gm.user_id=u.id AND gm.group_id=$1 WHERE u.status='active' AND u.id!=$2 AND (u.name ILIKE $3 OR u.display_name ILIKE $3) ORDER BY u.name ASC${isTyped ? ' LIMIT 10' : ''}`,
|
||||
[parseInt(groupId), req.user.id, `%${q}%`]
|
||||
);
|
||||
} else {
|
||||
users = await query(req.schema,
|
||||
`SELECT id,name,display_name,avatar,role,status,hide_admin_tag,allow_dm FROM users WHERE status='active' AND id!=$1 AND (name ILIKE $2 OR display_name ILIKE $2) ORDER BY name ASC${isTyped ? ' LIMIT 10' : ''}`,
|
||||
`SELECT id,name,display_name,avatar,role,status,hide_admin_tag,allow_dm,is_minor FROM users WHERE status='active' AND id!=$1 AND (name ILIKE $2 OR display_name ILIKE $2) ORDER BY name ASC${isTyped ? ' LIMIT 10' : ''}`,
|
||||
[req.user.id, `%${q}%`]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
users = await query(req.schema,
|
||||
`SELECT id,name,display_name,avatar,role,status,hide_admin_tag,allow_dm FROM users WHERE status='active' AND (name ILIKE $1 OR display_name ILIKE $1) ORDER BY name ASC${isTyped ? ' LIMIT 10' : ''}`,
|
||||
`SELECT id,name,display_name,avatar,role,status,hide_admin_tag,allow_dm,is_minor FROM users WHERE status='active' AND (name ILIKE $1 OR display_name ILIKE $1) ORDER BY name ASC${isTyped ? ' LIMIT 10' : ''}`,
|
||||
[`%${q}%`]
|
||||
);
|
||||
}
|
||||
@@ -695,6 +695,64 @@ router.patch('/:id/deny-guardian', authMiddleware, teamManagerMiddleware, async
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
// List minor players available for this guardian to claim (Mixed Age — Family Manager)
|
||||
// Returns minors in the players group who either have no guardian yet or are already linked to me.
|
||||
router.get('/minor-players', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const playersRow = await queryOne(req.schema, "SELECT value FROM settings WHERE key='feature_players_group_id'");
|
||||
const playersGroupId = parseInt(playersRow?.value);
|
||||
if (!playersGroupId) return res.json({ users: [] });
|
||||
const users = await query(req.schema,
|
||||
`SELECT u.id,u.name,u.first_name,u.last_name,u.date_of_birth,u.avatar,u.status,u.guardian_user_id
|
||||
FROM users u
|
||||
JOIN user_group_members ugm ON ugm.user_id=u.id AND ugm.user_group_id=$1
|
||||
WHERE u.is_minor=TRUE AND u.status!='deleted'
|
||||
AND (u.guardian_user_id IS NULL OR u.guardian_user_id=$2)
|
||||
ORDER BY u.first_name,u.last_name`,
|
||||
[playersGroupId, req.user.id]
|
||||
);
|
||||
res.json({ users });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
// Claim minor as guardian (Mixed Age — Family Manager direct link, no approval needed)
|
||||
router.post('/me/guardian-children/:minorId', authMiddleware, async (req, res) => {
|
||||
const minorId = parseInt(req.params.minorId);
|
||||
try {
|
||||
const minor = await queryOne(req.schema, "SELECT * FROM users WHERE id=$1 AND status!='deleted'", [minorId]);
|
||||
if (!minor) return res.status(404).json({ error: 'User not found' });
|
||||
if (!minor.is_minor) return res.status(400).json({ error: 'User is not a minor' });
|
||||
if (minor.guardian_user_id && minor.guardian_user_id !== req.user.id)
|
||||
return res.status(409).json({ error: 'This minor already has a guardian' });
|
||||
await exec(req.schema,
|
||||
"UPDATE users SET guardian_user_id=$1,guardian_approval_required=FALSE,status='active',updated_at=NOW() WHERE id=$2",
|
||||
[req.user.id, minorId]
|
||||
);
|
||||
await addUserToPublicGroups(req.schema, minorId);
|
||||
const user = await queryOne(req.schema,
|
||||
'SELECT id,name,first_name,last_name,date_of_birth,avatar,status,guardian_user_id FROM users WHERE id=$1',
|
||||
[minorId]
|
||||
);
|
||||
res.json({ user });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
// Remove minor from guardian's list (Mixed Age — re-suspends the minor)
|
||||
router.delete('/me/guardian-children/:minorId', authMiddleware, async (req, res) => {
|
||||
const minorId = parseInt(req.params.minorId);
|
||||
try {
|
||||
const minor = await queryOne(req.schema, 'SELECT * FROM users WHERE id=$1', [minorId]);
|
||||
if (!minor) return res.status(404).json({ error: 'User not found' });
|
||||
if (minor.guardian_user_id !== req.user.id)
|
||||
return res.status(403).json({ error: 'You are not the guardian of this user' });
|
||||
await exec(req.schema,
|
||||
"UPDATE users SET guardian_user_id=NULL,status='suspended',updated_at=NOW() WHERE id=$1",
|
||||
[minorId]
|
||||
);
|
||||
res.json({ success: true });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
// Guardian self-link (Mixed Age — user links themselves as guardian of a minor, triggers approval)
|
||||
router.patch('/me/link-minor/:minorId', authMiddleware, async (req, res) => {
|
||||
const minorId = parseInt(req.params.minorId);
|
||||
|
||||
Reference in New Issue
Block a user