[WEB-3918] fix: api tokens is_active (#6941)

* fix: is_active always returning true
chore: formate expired_at to iso date

* Display exact expiration timestamp for API tokens

* chore: remove conversion to iso

* chore: remove unwanted imports

* fix: added timestamp for api token expiry

* fix: handle none value in expired_at

* fix: fix: handle none value in expired_at

* chore: add type hints

* fix: refactor

---------

Co-authored-by: Alaaeddine bousselmi <alaaeddine.bousselmi@medtech.tn>
Co-authored-by: gakshita <akshitagoyal1516@gmail.com>
Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com>
This commit is contained in:
Sangeetha 2025-04-24 01:28:29 +05:30 committed by GitHub
parent b5ceb94fb2
commit 2bbaaed3ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 37 additions and 22 deletions

View file

@ -1,5 +1,7 @@
from .base import BaseSerializer
from plane.db.models import APIToken, APIActivityLog
from rest_framework import serializers
from django.utils import timezone
class APITokenSerializer(BaseSerializer):
@ -17,10 +19,17 @@ class APITokenSerializer(BaseSerializer):
class APITokenReadSerializer(BaseSerializer):
is_active = serializers.SerializerMethodField()
class Meta:
model = APIToken
exclude = ("token",)
def get_is_active(self, obj: APIToken) -> bool:
if obj.expired_at is None:
return True
return timezone.now() < obj.expired_at
class APIActivityLogSerializer(BaseSerializer):
class Meta:

View file

@ -46,7 +46,7 @@ export const CreateApiTokenModal: React.FC<Props> = (props) => {
const csvData = {
Title: data.label,
Description: data.description,
Expiry: data.expired_at ? renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "" : "Never expires",
Expiry: data.expired_at ? (renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "") : "Never expires",
"Secret key": data.token ?? "",
};

View file

@ -13,7 +13,7 @@ import { Button, CustomSelect, Input, TextArea, ToggleSwitch, TOAST_TYPE, setToa
import { DateDropdown } from "@/components/dropdowns";
// helpers
import { cn } from "@/helpers/common.helper";
import { renderFormattedDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper";
type Props = {
handleClose: () => void;
@ -51,20 +51,21 @@ const defaultValues: Partial<IApiToken> = {
expired_at: null,
};
const getExpiryDate = (val: string): string | null | undefined => {
const getExpiryDate = (val: string): Date | null | undefined => {
const today = new Date();
const dateToAdd = EXPIRY_DATE_OPTIONS.find((option) => option.key === val)?.value;
if (dateToAdd) {
const expiryDate = add(today, dateToAdd);
return renderFormattedDate(expiryDate);
}
if (dateToAdd) return add(today, dateToAdd);
return null;
};
const getFormattedDate = (date: Date): Date => {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
return add(date, { hours, minutes, seconds });
};
export const CreateApiTokenForm: React.FC<Props> = (props) => {
const { handleClose, neverExpires, toggleNeverExpires, onSubmit } = props;
// states
@ -97,12 +98,13 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
// if never expires is toggled on, set expired_at to null
if (neverExpires) payload.expired_at = null;
// if never expires is toggled off, and the user has selected a custom date, set expired_at to the custom date
else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate);
else if (data.expired_at === "custom") {
payload.expired_at = customDate && getFormattedDate(customDate).toISOString();
}
// if never expires is toggled off, and the user has selected a predefined date, set expired_at to the predefined date
else {
const expiryDate = getExpiryDate(data.expired_at ?? "");
if (expiryDate) payload.expired_at = renderFormattedPayloadDate(new Date(expiryDate));
if (expiryDate) payload.expired_at = expiryDate.toISOString();
}
await onSubmit(payload).then(() => {
@ -114,6 +116,8 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
const today = new Date();
const tomorrow = add(today, { days: 1 });
const expiredAt = watch("expired_at");
const expiryDate = getExpiryDate(expiredAt ?? "");
const customDateFormatted = customDate && getFormattedDate(customDate);
return (
<form onSubmit={handleSubmit(handleFormSubmit)}>
@ -219,10 +223,10 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
<span className="text-xs text-custom-text-400">
{expiredAt === "custom"
? customDate
? `Expires ${renderFormattedDate(customDate)}`
? `Expires ${renderFormattedDate(customDateFormatted ?? "")} at ${renderFormattedTime(customDateFormatted ?? "")}`
: null
: expiredAt
? `Expires ${getExpiryDate(expiredAt ?? "")}`
? `Expires ${renderFormattedDate(expiryDate ?? "")} at ${renderFormattedTime(expiryDate ?? "")}`
: null}
</span>
)}
@ -249,4 +253,4 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
</div>
</form>
);
};
};

View file

@ -6,7 +6,7 @@ import { IApiToken } from "@plane/types";
// ui
import { Button, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
// helpers
import { renderFormattedDate } from "@/helpers/date-time.helper";
import { renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper";
import { copyTextToClipboard } from "@/helpers/string.helper";
// types
import { usePlatformOS } from "@/hooks/use-platform-os";
@ -49,7 +49,9 @@ export const GeneratedTokenDetails: React.FC<Props> = (props) => {
</button>
<div className="mt-6 flex items-center justify-between">
<p className="text-xs text-custom-text-400">
{tokenDetails.expired_at ? `Expires ${renderFormattedDate(tokenDetails.expired_at)}` : "Never expires"}
{tokenDetails.expired_at
? `Expires ${renderFormattedDate(tokenDetails.expired_at!)} at ${renderFormattedTime(tokenDetails.expired_at!)}`
: "Never expires"}
</p>
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
{t("close")}

View file

@ -6,7 +6,7 @@ import { IApiToken } from "@plane/types";
// components
import { Tooltip } from "@plane/ui";
import { DeleteApiTokenModal } from "@/components/api-token";
import { renderFormattedDate, calculateTimeAgo } from "@/helpers/date-time.helper";
import { renderFormattedDate, calculateTimeAgo, renderFormattedTime } from "@/helpers/date-time.helper";
import { usePlatformOS } from "@/hooks/use-platform-os";
// ui
// helpers
@ -52,7 +52,7 @@ export const ApiTokenListItem: React.FC<Props> = (props) => {
<p className="mb-1 text-xs leading-6 text-custom-text-400">
{token.is_active
? token.expired_at
? `Expires ${renderFormattedDate(token.expired_at!)}`
? `Expires ${renderFormattedDate(token.expired_at!)} at ${renderFormattedTime(token.expired_at!)}`
: "Never expires"
: `Expired ${calculateTimeAgo(token.expired_at)}`}
</p>
@ -60,4 +60,4 @@ export const ApiTokenListItem: React.FC<Props> = (props) => {
</div>
</>
);
};
};