2
0
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:
Oliver
2025-07-04 11:38:21 +10:00
committed by GitHub
parent ada346d339
commit 6453abb974
3 changed files with 67 additions and 47 deletions

View File

@ -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

View File

@ -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'."""

View File

@ -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>
<Flex>
<Code>{token}</Code>
<CopyButton value={token} />
</Flex>
<Paper p='sm'>
<Flex>
<Code>{token}</Code>
<CopyButton value={token} />
</Flex>
</Paper>
</Modal>
</>
)}