Skip to content

Commit

Permalink
feat: add permission solution
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Jul 19, 2023
1 parent 0d1c2fe commit e2b93b6
Show file tree
Hide file tree
Showing 22 changed files with 421 additions and 47 deletions.
24 changes: 24 additions & 0 deletions demo/rbac/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
13 changes: 13 additions & 0 deletions demo/rbac/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tushan</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
23 changes: 23 additions & 0 deletions demo/rbac/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "demo-rbac",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tushan": "workspace:*"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "^4.9.3",
"vite": "^4.2.0"
}
}
10 changes: 10 additions & 0 deletions demo/rbac/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions demo/rbac/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Category,
createTextField,
jsonServerProvider,
ListTable,
Resource,
Tushan,
} from 'tushan';
import { IconImage, IconUser } from 'tushan/icon';
import { authProvider } from './auth';
import { photoFields, userFields } from './fields';

const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');

function App() {
return (
<Tushan
basename="/"
dataProvider={dataProvider}
authProvider={authProvider}
>
{({ permissions }) => (
<>
<Resource
name="users"
label="User"
icon={<IconUser />}
list={
<ListTable
filter={[
createTextField('q', {
label: 'Search',
edit: {
placeholder: 'Search name...',
},
}),
]}
fields={userFields}
action={{
create: permissions.includes('admin'),
detail: true,
edit: permissions.includes('admin'),
delete: true,
}}
/>
}
/>

<Resource
name="photos"
icon={<IconImage />}
list={
<ListTable
fields={photoFields}
action={{ detail: true, edit: true, delete: true }}
/>
}
/>
</>
)}
</Tushan>
);
}

export default App;
37 changes: 37 additions & 0 deletions demo/rbac/src/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { AuthProvider } from 'tushan';

export const authProvider: AuthProvider = {
login: ({ username, password }) => {
if (username !== 'tushan' || password !== 'tushan') {
return Promise.reject();
}

localStorage.setItem('username', username);
return Promise.resolve();
},
logout: () => {
localStorage.removeItem('username');
return Promise.resolve();
},
checkAuth: () =>
localStorage.getItem('username') ? Promise.resolve() : Promise.reject(),
checkError: (error) => {
const status = error.status;
if (status === 401 || status === 403) {
localStorage.removeItem('username');
return Promise.reject();
}
// other error code (404, 500, etc): no need to log out
return Promise.resolve();
},
getIdentity: () =>
Promise.resolve({
id: 'user',
fullName: 'John Doe',
}),
getPermissions: () =>
Promise.resolve([
// 'admin',
'user',
]),
};
87 changes: 87 additions & 0 deletions demo/rbac/src/fields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
createAvatarField,
createBooleanField,
createEmailField,
createImageField,
createReferenceField,
createSelectField,
createTextAreaField,
createTextField,
createUrlField,
} from 'tushan';

export const userFields = [
createTextField('id', {
label: 'ID',
}),
createTextField('name', {
list: {
sort: true,
},
}),
createEmailField('email', {
edit: {
hidden: true,
},
create: {
hidden: false,
},
}),
createUrlField('website'),
];

export const photoFields = [
createTextField('id', {
label: 'ID',
}),
createTextField('albumId'),
createTextField('title'),
createImageField('url', {
height: 300,
}),
createAvatarField('thumbnailUrl'),
];

export const commentFields = [
createTextField('id', {
list: {
width: 100,
},
}),
createTextField('name'),
createEmailField('email'),
createTextAreaField('body', {
list: {
ellipsis: true,
},
}),
];

export const todoFields = [
createTextField('id', {
list: {
width: 100,
},
}),
createReferenceField('userId', {
label: 'User',
reference: 'users',
displayField: 'name',
}),
createTextField('title'),
// createBooleanField('completed'),
createSelectField('completed', {
items: [
{
value: true,
label: 'Completed',
color: 'red',
},
{
value: false,
label: 'Processing',
color: 'green',
},
],
}),
];
7 changes: 7 additions & 0 deletions demo/rbac/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<App />
);
1 change: 1 addition & 0 deletions demo/rbac/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
21 changes: 21 additions & 0 deletions demo/rbac/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
9 changes: 9 additions & 0 deletions demo/rbac/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
9 changes: 9 additions & 0 deletions demo/rbac/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

console.log('后台默认密码: tushan/tushan');

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
});
1 change: 1 addition & 0 deletions packages/tushan/client/api/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './createAuthProvider';
export * from './createAuthHTTPClient';
export * from './useLogin';
export * from './useLogout';
export * from './usePermissions';
28 changes: 28 additions & 0 deletions packages/tushan/client/api/auth/usePermissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useTushanContext } from '../../context/tushan';

export function usePermissions<Permissions = any, Error = any>(
params = {},
queryParams: UseQueryOptions<Permissions, Error, Permissions, any> = {
staleTime: 5 * 60 * 1000,
}
) {
const { authProvider } = useTushanContext();

const result = useQuery(
['auth', 'getPermissions', params],
() => authProvider?.getPermissions(params) ?? Promise.resolve(null),
queryParams
);

return useMemo(
() => ({
permissions: result.data,
isLoading: result.isLoading,
error: result.error,
refetch: result.refetch,
}),
[result]
);
}
3 changes: 2 additions & 1 deletion packages/tushan/client/components/BuiltinRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { useTushanContext } from '../context/tushan';
import { useConfigureAdminRouterFromChildren } from '../hooks/useConfigureAdminRouterFromChildren';
import { useDelay } from '../hooks/useDelay';
import { useUserStore } from '../store/user';
import type { TushanChildren } from '../types';
import { createSelector } from '../utils/createSelector';
import { Dashboard } from './defaults/Dashboard';
import { LoginPage } from './defaults/LoginPage';
import { BasicLayout } from './layout';
import { LoadingView } from './LoadingView';

export interface BuiltinRoutesProps {
children?: React.ReactNode;
children: TushanChildren;
}

export const BuiltinRoutes: React.FC<BuiltinRoutesProps> = React.memo(
Expand Down
4 changes: 2 additions & 2 deletions packages/tushan/client/components/Tushan.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { useMemo } from 'react';
import { BrowserRouter, HashRouter } from 'react-router-dom';
import { BuiltinRoutes } from './BuiltinRoutes';
import { BuiltinRoutes, BuiltinRoutesProps } from './BuiltinRoutes';
import { TushanContextProps, TushanContextProvider } from '../context/tushan';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { defaultQueryClient } from '../api';
import { ArcoDesignProvider } from './ArcoDesignProvider';
import '@arco-design/web-react/dist/css/arco.css';

interface TushanProps extends TushanContextProps, React.PropsWithChildren {
interface TushanProps extends TushanContextProps, BuiltinRoutesProps {
queryClient?: QueryClient;
}
export const Tushan: React.FC<TushanProps> = React.memo((props) => {
Expand Down
Loading

0 comments on commit e2b93b6

Please sign in to comment.