mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-07 06:00:57 +00:00
User token table (#9954)
* Tweak layout of Token dialog * Render user details in API token table * Bump API version
This commit is contained in:
@ -1,12 +1,15 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 362
|
||||
INVENTREE_API_VERSION = 363
|
||||
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v363 -> 2025-07-04 : https://github.com/inventree/InvenTree/pull/9954
|
||||
- Adds "user_detail" field to the ApiToken serializer
|
||||
|
||||
v362 -> 2025-07-02 : https://github.com/inventree/InvenTree/pull/9939
|
||||
- Allow filtering of BuildItem API by "location" of StockItem
|
||||
- Allow filtering of SalesOrderAllocation API by "location" of StockItem
|
||||
|
@ -134,46 +134,6 @@ def generate_roles_dict(roles) -> dict:
|
||||
return role_dict
|
||||
|
||||
|
||||
class ApiTokenSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the ApiToken model."""
|
||||
|
||||
in_use = serializers.SerializerMethodField(read_only=True)
|
||||
user = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(), required=False
|
||||
)
|
||||
|
||||
def get_in_use(self, token: ApiToken) -> bool:
|
||||
"""Return True if the token is currently used to call the endpoint."""
|
||||
from InvenTree.middleware import get_token_from_request
|
||||
|
||||
request = self.context.get('request')
|
||||
rq_token = get_token_from_request(request)
|
||||
return token.key == rq_token
|
||||
|
||||
class Meta:
|
||||
"""Meta options for ApiTokenSerializer."""
|
||||
|
||||
model = ApiToken
|
||||
fields = [
|
||||
'created',
|
||||
'expiry',
|
||||
'id',
|
||||
'last_seen',
|
||||
'name',
|
||||
'token',
|
||||
'active',
|
||||
'revoked',
|
||||
'user',
|
||||
'in_use',
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate the data for the serializer."""
|
||||
if 'user' not in data:
|
||||
data['user'] = self.context['request'].user
|
||||
return super().validate(data)
|
||||
|
||||
|
||||
class GetAuthTokenSerializer(serializers.Serializer):
|
||||
"""Serializer for the GetAuthToken API endpoint."""
|
||||
|
||||
@ -248,6 +208,49 @@ class UserSerializer(InvenTreeModelSerializer):
|
||||
)
|
||||
|
||||
|
||||
class ApiTokenSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the ApiToken model."""
|
||||
|
||||
in_use = serializers.SerializerMethodField(read_only=True)
|
||||
user = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(), required=False
|
||||
)
|
||||
|
||||
def get_in_use(self, token: ApiToken) -> bool:
|
||||
"""Return True if the token is currently used to call the endpoint."""
|
||||
from InvenTree.middleware import get_token_from_request
|
||||
|
||||
request = self.context.get('request')
|
||||
rq_token = get_token_from_request(request)
|
||||
return token.key == rq_token
|
||||
|
||||
class Meta:
|
||||
"""Meta options for ApiTokenSerializer."""
|
||||
|
||||
model = ApiToken
|
||||
fields = [
|
||||
'created',
|
||||
'expiry',
|
||||
'id',
|
||||
'last_seen',
|
||||
'name',
|
||||
'token',
|
||||
'active',
|
||||
'revoked',
|
||||
'user',
|
||||
'user_detail',
|
||||
'in_use',
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate the data for the serializer."""
|
||||
if 'user' not in data:
|
||||
data['user'] = self.context['request'].user
|
||||
return super().validate(data)
|
||||
|
||||
user_detail = UserSerializer(source='user', read_only=True)
|
||||
|
||||
|
||||
class GroupSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for a 'Group'."""
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { apiUrl } from '@lib/functions/Api';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Badge, Code, Flex, Modal, Text } from '@mantine/core';
|
||||
import { Badge, Code, Flex, Modal, Paper, Text } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconCircleX } from '@tabler/icons-react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
@ -11,6 +11,7 @@ import { api } from '../../App';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { CopyButton } from '../../components/buttons/CopyButton';
|
||||
import { StylishText } from '../../components/items/StylishText';
|
||||
import { RenderUser } from '../../components/render/User';
|
||||
import { showApiErrorMessage } from '../../functions/notifications';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
@ -99,7 +100,18 @@ export function ApiTokenTable({
|
||||
}
|
||||
];
|
||||
if (!only_myself) {
|
||||
cols.push({ accessor: 'user', title: t`User`, sortable: true });
|
||||
cols.push({
|
||||
accessor: 'user',
|
||||
title: t`User`,
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
if (record.user_detail) {
|
||||
return <RenderUser instance={record.user_detail} />;
|
||||
} else {
|
||||
return record.user;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return cols;
|
||||
}, [only_myself]);
|
||||
@ -178,10 +190,12 @@ export function ApiTokenTable({
|
||||
Tokens are only shown once - make sure to note it down.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Paper p='sm'>
|
||||
<Flex>
|
||||
<Code>{token}</Code>
|
||||
<CopyButton value={token} />
|
||||
</Flex>
|
||||
</Paper>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user