feat: add multi-user support and role-based access control #31

This commit is contained in:
rustmailer
2025-12-26 14:27:04 +08:00
parent 1e2f526a07
commit 4af5176b65
181 changed files with 23745 additions and 3187 deletions
+22 -9
View File
@@ -42,6 +42,8 @@ import { SyncFoldersDialog } from './components/sync-folders'
import { NoSyncAccountDialog } from './components/nosync-dialog'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
import { useTranslation } from 'react-i18next'
import { AccountAccessAssignmentDialog } from './components/access-assignment-dialog'
import { useCurrentUser } from '@/hooks/use-current-user'
export default function Accounts() {
const { t } = useTranslation()
@@ -49,6 +51,7 @@ export default function Accounts() {
// Dialog states
const [currentRow, setCurrentRow] = useState<AccountModel | null>(null)
const [open, setOpen] = useDialogState<AccountDialogType>(null)
const { require_any_permission } = useCurrentUser()
const { data: accountList, isLoading } = useQuery({
queryKey: ['account-list'],
@@ -63,7 +66,6 @@ export default function Accounts() {
<Main>
<div className="mx-auto w-full max-w-[88rem] px-4">
{/* Header Section */}
<div className='mb-2 flex items-center justify-between flex-wrap gap-x-4 gap-y-2'>
<div>
<h2 className='text-2xl font-bold tracking-tight'>{t('accounts.title')}</h2>
@@ -71,7 +73,7 @@ export default function Accounts() {
{t('accounts.description')}
</p>
</div>
<div className="flex gap-2">
{require_any_permission(['system:root', 'account:create']) && <div className="flex gap-2">
<div className="flex rounded-md shadow-sm">
<Button
onClick={() => setOpen("add-imap")}
@@ -98,10 +100,9 @@ export default function Accounts() {
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>}
</div>
{/* Table / Empty State Section */}
<div className='flex-1 overflow-auto py-1 flex-row lg:space-x-12 space-y-0'>
{isLoading ? (
<TableSkeleton columns={columns.length} rows={10} />
@@ -168,7 +169,8 @@ export default function Accounts() {
}}
currentRow={currentRow}
/>
<RunningStateDialog
{require_any_permission(['system:root', 'account:read_details'], currentRow.id) && <RunningStateDialog
key='running-state'
open={open === 'running-state'}
onOpenChange={() => {
@@ -178,7 +180,8 @@ export default function Accounts() {
}, 500)
}}
currentRow={currentRow}
/>
/>}
<AccountDeleteDialog
key={`account-delete-${currentRow.id}`}
open={open === 'delete'}
@@ -201,17 +204,27 @@ export default function Accounts() {
}}
currentRow={currentRow}
/>
{require_any_permission(['system:root', 'account:manage'], currentRow.id) && <AccountAccessAssignmentDialog
key={`access-assign-${currentRow.id}`}
open={open === 'access-assign'}
onOpenChange={() => {
setOpen('access-assign')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
currentRow={currentRow}
/>}
<AccountDetailDrawer
open={open === 'detail'}
onOpenChange={() => setOpen('detail')}
currentRow={currentRow}
/>
<OAuth2TokensDialog open={open === 'oauth2'}
{require_any_permission(['system:root', 'account:manage'], currentRow.id) && <OAuth2TokensDialog open={open === 'oauth2'}
onOpenChange={() => setOpen('oauth2')}
currentRow={currentRow}
/>
/>}
</>
)}
</AccountProvider>