feat: role-aware presence bar, WebSocket logging fixes

- findAdminByKey returns role from admins.json (defaults to 'admin')
- JWT includes config-defined role instead of hardcoded 'admin'
- PresenceBar split into "who's here?" (page admins) and "connected"
  (bot/utility services with icon+color badges)
- Bot/utility roles appear in presence on all pages when connected
- usePresence hook uses refs to avoid WS reconnect on navigation
- WS auth log prints admin name instead of generic 'admin'
- WS connection log reads X-Forwarded-For for real client IP
- AuthContext stores adminRole from login response
- Uncomment admins.json Docker volume mount, add SELinux :z flags

Made-with: Cursor
This commit is contained in:
cottongin
2026-04-05 04:27:07 -04:00
parent 52e9a7af42
commit b2bb2989e9
9 changed files with 207 additions and 53 deletions

View File

@@ -15,6 +15,7 @@ export const useAuth = () => {
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(localStorage.getItem('adminToken'));
const [adminName, setAdminName] = useState(localStorage.getItem('adminName'));
const [adminRole, setAdminRole] = useState(localStorage.getItem('adminRole'));
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);
@@ -27,9 +28,12 @@ export const AuthProvider = ({ children }) => {
});
setIsAuthenticated(true);
const name = response.data.user?.name;
const role = response.data.user?.role || 'admin';
if (name) {
setAdminName(name);
setAdminRole(role);
localStorage.setItem('adminName', name);
localStorage.setItem('adminRole', role);
} else {
logout();
}
@@ -47,11 +51,13 @@ export const AuthProvider = ({ children }) => {
const login = async (key) => {
try {
const response = await axios.post('/api/auth/login', { key });
const { token: newToken, name } = response.data;
const { token: newToken, name, role } = response.data;
localStorage.setItem('adminToken', newToken);
localStorage.setItem('adminName', name);
localStorage.setItem('adminRole', role || 'admin');
setToken(newToken);
setAdminName(name);
setAdminRole(role || 'admin');
setIsAuthenticated(true);
migratePreferences(name);
return { success: true };
@@ -66,14 +72,17 @@ export const AuthProvider = ({ children }) => {
const logout = () => {
localStorage.removeItem('adminToken');
localStorage.removeItem('adminName');
localStorage.removeItem('adminRole');
setToken(null);
setAdminName(null);
setAdminRole(null);
setIsAuthenticated(false);
};
const value = {
token,
adminName,
adminRole,
isAuthenticated,
loading,
login,