User/Usergroup Pages first draft
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,50 @@
|
||||
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 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);
|
||||
}
|
||||
@@ -45,6 +45,8 @@ import {
|
||||
removeServer,
|
||||
setServerApiKey
|
||||
} from './setup/index.js';
|
||||
import { listUsers } from './users/index.js';
|
||||
import { listGroups } from './groups/index.js';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -1671,6 +1673,26 @@ app.get('/api/auth/session', (req, res) => {
|
||||
res.json({ user: sanitizeUser(user) });
|
||||
});
|
||||
|
||||
app.get('/api/users', (req, res) => {
|
||||
try {
|
||||
const users = listUsers();
|
||||
res.json({ items: users, total: users.length });
|
||||
} catch (error) {
|
||||
console.error('⚠️ [Users] Abruf der Benutzerliste fehlgeschlagen:', error);
|
||||
res.status(500).json({ error: 'USERS_FETCH_FAILED' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/groups', (req, res) => {
|
||||
try {
|
||||
const groups = listGroups();
|
||||
res.json({ items: groups, total: groups.length });
|
||||
} catch (error) {
|
||||
console.error('⚠️ [Groups] Abruf der Benutzergruppenliste fehlgeschlagen:', error);
|
||||
res.status(500).json({ error: 'GROUPS_FETCH_FAILED' });
|
||||
}
|
||||
});
|
||||
|
||||
// Superuser Setup
|
||||
app.get('/api/auth/superuser/status', (req, res) => {
|
||||
const exists = hasSuperuser();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -32,8 +32,8 @@
|
||||
<!-- Nepcha Analytics (nepcha.com) -->
|
||||
<!-- Nepcha is a easy-to-use web analytics. No cookies and fully compliant with GDPR, CCPA and PECR. -->
|
||||
<script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-999a8a11.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-4350b70a.css">
|
||||
<script type="module" crossorigin src="/assets/index-af8f32f4.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-58708f0f.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { db } from '../db/index.js';
|
||||
|
||||
const selectUsersWithGroups = db.prepare(`
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.email,
|
||||
u.is_active,
|
||||
u.last_login,
|
||||
u.created_at,
|
||||
u.updated_at,
|
||||
GROUP_CONCAT(g.name, '|||') AS group_names
|
||||
FROM users u
|
||||
LEFT JOIN user_group_memberships m ON m.user_id = u.id
|
||||
LEFT JOIN user_groups g ON g.id = m.group_id
|
||||
GROUP BY u.id
|
||||
ORDER BY u.username COLLATE NOCASE
|
||||
`);
|
||||
|
||||
const normalizeGroupNames = (rawNames) => {
|
||||
if (!rawNames) {
|
||||
return [];
|
||||
}
|
||||
return rawNames
|
||||
.split('|||')
|
||||
.map((name) => String(name || '').trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
const sanitizeUserRecord = (row) => ({
|
||||
id: row.id,
|
||||
username: row.username,
|
||||
email: row.email,
|
||||
isActive: Boolean(row.is_active),
|
||||
lastLogin: row.last_login || null,
|
||||
createdAt: row.created_at || null,
|
||||
updatedAt: row.updated_at || null,
|
||||
groups: normalizeGroupNames(row.group_names)
|
||||
});
|
||||
|
||||
export function listUsers() {
|
||||
const rows = selectUsersWithGroups.all();
|
||||
return rows.map(sanitizeUserRecord);
|
||||
}
|
||||
Reference in New Issue
Block a user