Add user management in settings
This commit is contained in:
parent
e0f6d8fa0b
commit
ab278a5a99
|
@ -56,7 +56,6 @@ const schema = makeAugmentedSchema({
|
|||
hasRole: true,
|
||||
},
|
||||
mutation: false,
|
||||
query: true,
|
||||
query: {
|
||||
exclude: [
|
||||
'ScannerResult',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { registerUser } from './users'
|
||||
import { registerUser, authorizeUser } from './users'
|
||||
|
||||
async function initialSetup(driver) {
|
||||
const session = driver.session()
|
||||
|
@ -59,10 +59,12 @@ const Mutation = {
|
|||
|
||||
session.close()
|
||||
|
||||
const token = (await authorizeUser(root, args, ctx, info)).token
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status: 'Initial setup successful',
|
||||
token: userResult.token,
|
||||
token,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -84,9 +84,30 @@ const Mutation = {
|
|||
token: token,
|
||||
}
|
||||
},
|
||||
async updateUser(root, args, ctx, info) {
|
||||
if (args.rootPath) {
|
||||
if (!(await fs.exists(args.rootPath))) {
|
||||
throw Error('New root path not found in server filesystem')
|
||||
}
|
||||
}
|
||||
|
||||
return neo4jgraphql(root, args, ctx, info)
|
||||
},
|
||||
async createUser(root, args, ctx, info) {
|
||||
if (args.rootPath) {
|
||||
if (!(await fs.exists(args.rootPath))) {
|
||||
throw Error('Root path not found in server filesystem')
|
||||
}
|
||||
}
|
||||
|
||||
args.id = uuid()
|
||||
|
||||
return neo4jgraphql(root, args, ctx, info)
|
||||
},
|
||||
}
|
||||
|
||||
export const registerUser = Mutation.registerUser
|
||||
export const authorizeUser = Mutation.authorizeUser
|
||||
|
||||
const Query = {
|
||||
myUser(root, args, ctx, info) {
|
||||
|
|
|
@ -106,6 +106,12 @@ type Mutation {
|
|||
password: String!
|
||||
rootPath: String!
|
||||
): AuthorizeResult @neo4j_ignore
|
||||
|
||||
updateUser(id: ID!, username: String, rootPath: String, admin: Boolean): User
|
||||
@hasRole(roles: [admin])
|
||||
createUser(id: ID, username: String, rootPath: String, admin: Boolean): User
|
||||
@hasRole(roles: [admin])
|
||||
deleteUser(id: ID!): User @hasRole(roles: [admin])
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Mutation } from 'react-apollo'
|
||||
import { Table, Button, Input, Checkbox } from 'semantic-ui-react'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const createUserMutation = gql`
|
||||
mutation createUser($username: String, $rootPath: String, $admin: Boolean) {
|
||||
createUser(username: $username, rootPath: $rootPath, admin: $admin) {
|
||||
id
|
||||
username
|
||||
rootPath
|
||||
admin
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const initialState = {
|
||||
username: '',
|
||||
rootPath: '',
|
||||
admin: false,
|
||||
}
|
||||
|
||||
const AddUserRow = ({ setShow, show, onUserAdded }) => {
|
||||
const [state, setState] = useState(initialState)
|
||||
|
||||
function updateInput(event, key) {
|
||||
setState({
|
||||
...state,
|
||||
[key]: event.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
if (!show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Mutation
|
||||
mutation={createUserMutation}
|
||||
onCompleted={data => {
|
||||
onUserAdded()
|
||||
}}
|
||||
>
|
||||
{(createUser, { loading, data }) => (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder="Username"
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder="/path/to/photos"
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Checkbox
|
||||
toggle
|
||||
checked={state.admin}
|
||||
onChange={(e, data) => {
|
||||
console.log(data)
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button negative onClick={e => setShow(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
positive
|
||||
onClick={() => {
|
||||
createUser({
|
||||
variables: {
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
setState(initialState)
|
||||
}}
|
||||
>
|
||||
Add User
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Mutation>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddUserRow
|
|
@ -4,6 +4,7 @@ import Layout from '../../Layout'
|
|||
import { Button, Icon } from 'semantic-ui-react'
|
||||
import { Mutation } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import UsersTable from './UsersTable'
|
||||
|
||||
const scanMutation = gql`
|
||||
mutation scanAllMutation {
|
||||
|
@ -18,7 +19,7 @@ const SettingsPage = () => (
|
|||
<Layout>
|
||||
<h1>Settings</h1>
|
||||
<Mutation mutation={scanMutation}>
|
||||
{(scan, { data }) => (
|
||||
{(scan, { data, called }) => (
|
||||
<>
|
||||
<h2>Scanner</h2>
|
||||
<Button
|
||||
|
@ -27,15 +28,15 @@ const SettingsPage = () => (
|
|||
onClick={() => {
|
||||
scan()
|
||||
}}
|
||||
disabled={data && data.scanAll && data.scanAll.success}
|
||||
disabled={called}
|
||||
>
|
||||
<Icon name="sync" />
|
||||
Scan All
|
||||
</Button>
|
||||
<p>Scan for new images for all users</p>
|
||||
</>
|
||||
)}
|
||||
</Mutation>
|
||||
<UsersTable />
|
||||
</Layout>
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Mutation } from 'react-apollo'
|
||||
import { Table, Icon, Button, Input, Checkbox, Modal } from 'semantic-ui-react'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const updateUserMutation = gql`
|
||||
mutation updateUser(
|
||||
$id: ID!
|
||||
$username: String
|
||||
$rootPath: String
|
||||
$admin: Boolean
|
||||
) {
|
||||
updateUser(
|
||||
id: $id
|
||||
username: $username
|
||||
rootPath: $rootPath
|
||||
admin: $admin
|
||||
) {
|
||||
id
|
||||
username
|
||||
rootPath
|
||||
admin
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const deleteUserMutation = gql`
|
||||
mutation deleteUser($id: ID!) {
|
||||
deleteUser(id: $id) {
|
||||
id
|
||||
username
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const UserRow = ({ user, refetchUsers }) => {
|
||||
const [state, setState] = useState({
|
||||
...user,
|
||||
editing: false,
|
||||
})
|
||||
|
||||
const [showComfirmDelete, setConfirmDelete] = useState(false)
|
||||
|
||||
function updateInput(event, key) {
|
||||
setState({
|
||||
...state,
|
||||
[key]: event.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
if (state.editing) {
|
||||
return (
|
||||
<Mutation
|
||||
mutation={updateUserMutation}
|
||||
onCompleted={data => {
|
||||
setState({
|
||||
...data.updateUser,
|
||||
editing: false,
|
||||
})
|
||||
refetchUsers()
|
||||
}}
|
||||
>
|
||||
{(updateUser, { loading, data }) => (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder={user.username}
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder={user.rootPath}
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Checkbox
|
||||
toggle
|
||||
checked={state.admin}
|
||||
onChange={(e, data) => {
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button
|
||||
negative
|
||||
onClick={e =>
|
||||
setState({
|
||||
...state.oldState,
|
||||
})
|
||||
}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
positive
|
||||
onClick={() =>
|
||||
updateUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Mutation>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Mutation
|
||||
mutation={deleteUserMutation}
|
||||
onCompleted={() => {
|
||||
refetchUsers()
|
||||
}}
|
||||
>
|
||||
{(deleteUser, { loading, data }) => (
|
||||
<Table.Row>
|
||||
<Table.Cell>{user.username}</Table.Cell>
|
||||
<Table.Cell>{user.rootPath}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{user.admin ? <Icon name="checkmark" size="large" /> : null}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setState({ ...state, editing: true, oldState: state })
|
||||
}}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
negative
|
||||
onClick={() => {
|
||||
setConfirmDelete(true)
|
||||
}}
|
||||
>
|
||||
<Icon name="delete" />
|
||||
Delete
|
||||
</Button>
|
||||
<Modal open={showComfirmDelete}>
|
||||
<Modal.Header>Delete user</Modal.Header>
|
||||
<Modal.Content>
|
||||
<p>
|
||||
{`Are you sure, you want to delete `}
|
||||
<b>{user.username}</b>?
|
||||
</p>
|
||||
<p>{`This action cannot be undone`}</p>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => setConfirmDelete(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
negative
|
||||
onClick={() => {
|
||||
setConfirmDelete(false)
|
||||
deleteUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Delete {user.username}
|
||||
</Button>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Mutation>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserRow
|
|
@ -0,0 +1,80 @@
|
|||
import React, { useState } from 'react'
|
||||
|
||||
import { Table, Loader, Button } from 'semantic-ui-react'
|
||||
import { Query } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import UserRow from './UserRow'
|
||||
import AddUserRow from './AddUserRow'
|
||||
|
||||
const usersQuery = gql`
|
||||
query settingsUsersQuery {
|
||||
User {
|
||||
id
|
||||
username
|
||||
rootPath
|
||||
admin
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const UsersTable = () => {
|
||||
const [showAddUser, setShowAddUser] = useState(false)
|
||||
|
||||
return (
|
||||
<Query query={usersQuery}>
|
||||
{({ loading, error, data, refetch }) => {
|
||||
let userRows = []
|
||||
if (data && data.User) {
|
||||
userRows = data.User.map(user => (
|
||||
<UserRow user={user} refetchUsers={refetch} key={user.id} />
|
||||
))
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<h2>Users</h2>
|
||||
<Loader active={loading} />
|
||||
<Table celled>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Username</Table.HeaderCell>
|
||||
<Table.HeaderCell>Photo path</Table.HeaderCell>
|
||||
<Table.HeaderCell>Admin</Table.HeaderCell>
|
||||
<Table.HeaderCell>Action</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
|
||||
<Table.Body>
|
||||
{userRows}
|
||||
<AddUserRow
|
||||
show={showAddUser}
|
||||
setShow={setShowAddUser}
|
||||
onUserAdded={() => {
|
||||
setShowAddUser(false)
|
||||
refetch()
|
||||
}}
|
||||
/>
|
||||
</Table.Body>
|
||||
|
||||
<Table.Footer>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="4">
|
||||
<Button
|
||||
positive
|
||||
floated="right"
|
||||
onClick={e => setShowAddUser(true)}
|
||||
>
|
||||
New user
|
||||
</Button>
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Footer>
|
||||
</Table>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Query>
|
||||
)
|
||||
}
|
||||
|
||||
export default UsersTable
|
Loading…
Reference in New Issue