Skip to content

Commit

Permalink
Add logout button and disable delete for current user
Browse files Browse the repository at this point in the history
  • Loading branch information
rtm516 committed Aug 7, 2024
1 parent 035b70e commit 1f826d9
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import com.rtm516.mcxboxbroadcast.core.BuildData;
import com.rtm516.mcxboxbroadcast.manager.BackendManager;
import com.rtm516.mcxboxbroadcast.manager.config.MainConfig;
import com.rtm516.mcxboxbroadcast.manager.database.model.User;
import com.rtm516.mcxboxbroadcast.manager.models.response.ManagerInfoResponse;
import com.rtm516.mcxboxbroadcast.manager.models.response.UserInfoResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

Expand Down Expand Up @@ -45,4 +48,16 @@ public Map<String, String> settings(HttpServletResponse response) {
"Log lines", String.valueOf(config.logSize())
);
}

@GetMapping("/api/user")
public UserInfoResponse user(HttpServletResponse response) {
if (!backendManager.config().auth()) {
response.setStatus(404);
return null;
}

response.setStatus(200);
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return new UserInfoResponse(user.getId(), user.getUsername());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public User(String username, String password) {
this.password = password;
}

public ObjectId getId() {
return _id;
}

@Override
public String getUsername() {
return username;
Expand Down
6 changes: 3 additions & 3 deletions bootstrap/manager/src/ui/src/components/specialised/User.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { KeyIcon, UserMinusIcon } from '@heroicons/react/16/solid'

import Button from '../basic/Button'

function User ({ user, refreshUsers, className, confirmDelete, changePassword }) {
function User ({ user, disableDelete, refreshUsers, confirmDelete, changePassword }) {
const callDelete = () => {
confirmDelete((success) => {
if (success) {
Expand All @@ -19,10 +19,10 @@ function User ({ user, refreshUsers, className, confirmDelete, changePassword })

return (
<>
<div className={'flex hover:bg-slate-100 rounded p-2 gap-2 ' + className}>
<div className='flex hover:bg-slate-100 rounded p-2 gap-2'>
<div className='grow content-center'>{user.username}</div>
<Button title='Change password' color='green' onClick={() => callChangePassword()}><KeyIcon className='size-4' aria-hidden='true' /></Button>
<Button title='Delete' color='red' onClick={() => callDelete()}><UserMinusIcon className='size-4' aria-hidden='true' /></Button>
<Button title='Delete' color='red' disabled={disableDelete} onClick={() => !disableDelete && callDelete()}><UserMinusIcon className='size-4' aria-hidden='true' /></Button>
</div>
</>
)
Expand Down
4 changes: 4 additions & 0 deletions bootstrap/manager/src/ui/src/settings/UserPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Button from '../components/basic/Button'
import ConfirmModal from '../components/modals/ConfirmModal'
import CreateUserModal from '../components/modals/CreateUserModal'
import ChangePasswordModal from '../components/modals/ChangePasswordModal'
import { useOutletContext } from 'react-router-dom'

function UserPanel () {
const [users, setUsers] = useState([])
Expand All @@ -19,6 +20,8 @@ function UserPanel () {
const [changePasswordOpen, setChangePasswordOpen] = useState(false)
const [changePasswordUser, setChangePasswordUser] = useState('')

const { currentUserInfo } = useOutletContext()

useEffect(() => {
refreshUsers()
}, [])
Expand Down Expand Up @@ -64,6 +67,7 @@ function UserPanel () {
<User
key={user.id}
user={user}
disableDelete={currentUserInfo && user.id === currentUserInfo.id}
refreshUsers={refreshUsers}
changePassword={(userId) => {
setChangePasswordUser(userId)
Expand Down
53 changes: 42 additions & 11 deletions bootstrap/manager/src/ui/src/templates/Layout.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { NavLink, Outlet, useMatches } from 'react-router-dom'
import { Disclosure, Transition } from '@headlessui/react'
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'

import { NotificationContainer } from '../components/layout/NotificationContainer'
import Footer from '../components/layout/Footer'

function getNavClass (isMobile, { isActive }) {
function getNavClass (isMobile, { isActive } = { isActive: false }) {
let className = 'rounded-md px-3 py-2 font-medium'
if (isMobile) {
className = 'block text-base ' + className
Expand All @@ -23,12 +23,35 @@ function getNavClass (isMobile, { isActive }) {
return className
}

function getNav (isMobile = false) {
function getNav (userInfo, isMobile = false) {
return (
<>
<NavLink to='/bots' className={(a) => getNavClass(isMobile, a)}>Bots</NavLink>
<NavLink to='/servers' className={(a) => getNavClass(isMobile, a)}>Servers</NavLink>
<NavLink to='/settings' className={(a) => getNavClass(isMobile, a)}>Settings</NavLink>
{userInfo &&
<>
{!isMobile && <div className='border-r border-gray-700 w-0'>&nbsp;</div>}
<Menu as='div' className='relative'>
<div>
<MenuButton as='a' href='' className={getNavClass(isMobile)}>
<span className='absolute -inset-1.5' />
<span className='sr-only'>Open user menu</span>
{userInfo.username}
</MenuButton>
</div>
<MenuItems
transition
className='absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 transition focus:outline-none data-[closed]:scale-95 data-[closed]:transform data-[closed]:opacity-0 data-[enter]:duration-100 data-[leave]:duration-75 data-[enter]:ease-out data-[leave]:ease-in'
>
<MenuItem>
<a href='/logout' className='block px-4 py-2 text-sm text-gray-700 data-[focus]:bg-gray-100'>
Logout
</a>
</MenuItem>
</MenuItems>
</Menu>
</>}
</>
)
}
Expand All @@ -40,6 +63,14 @@ function Layout () {
const hideHeader = handle && handle.hideHeader ? handle.hideHeader(data) : false
const hideFooter = handle && handle.hideFooter ? handle.hideFooter(data) : false

const [userInfo, setUserInfo] = useState({})

useEffect(() => {
fetch('/api/user').then((res) => res.json()).then((data) => {
setUserInfo(data)
})
}, [])

useEffect(() => {
if (title) {
document.title = title + ' - MCXboxBroadcast Manager'
Expand All @@ -66,13 +97,13 @@ function Layout () {

<div className='hidden md:block'>
<div className='ml-10 flex items-baseline space-x-4'>
{getNav()}
{getNav(userInfo)}
</div>
</div>

<div className='-mr-2 flex md:hidden'>
{/* Mobile menu button */}
<Disclosure.Button className='relative inline-flex items-center justify-center rounded-md bg-gray-800 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800'>
<DisclosureButton className='relative inline-flex items-center justify-center rounded-md bg-gray-800 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800'>
<span className='absolute -inset-0.5' />
<span className='sr-only'>Open navigation menu</span>
{open
Expand All @@ -82,7 +113,7 @@ function Layout () {
: (
<Bars3Icon className='block h-6 w-6' aria-hidden='true' />
)}
</Disclosure.Button>
</DisclosureButton>
</div>
</div>
</div>
Expand All @@ -96,11 +127,11 @@ function Layout () {
leaveFrom='transform scale-100 opacity-100'
leaveTo='transform scale-95 opacity-0'
>
<Disclosure.Panel className='md:hidden'>
<DisclosurePanel className='md:hidden'>
<div className='space-y-1 px-2 pb-3 pt-2 sm:px-3'>
{getNav(true)}
{getNav(userInfo, true)}
</div>
</Disclosure.Panel>
</DisclosurePanel>
</Transition>
</>
)}
Expand All @@ -116,7 +147,7 @@ function Layout () {

<main className={'flex-1 ' + (!hideHeader ? '-mt-32' : '')}>
<NotificationContainer />
<Outlet />
<Outlet context={{ currentUserInfo: userInfo }} />
</main>

{(!hideFooter &&
Expand Down

0 comments on commit 1f826d9

Please sign in to comment.