From 1f5abaa6fb0bcd0d85ecf1468f5414266f79eae4 Mon Sep 17 00:00:00 2001 From: Tink bot Date: Tue, 26 May 2026 22:39:18 +0200 Subject: [PATCH] feat(veans): require APIToken.ExpiresAt with FarFuture sentinel --- veans/internal/client/types.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/veans/internal/client/types.go b/veans/internal/client/types.go index e074e5361..c8e54c709 100644 --- a/veans/internal/client/types.go +++ b/veans/internal/client/types.go @@ -140,17 +140,23 @@ const ( ) // APIToken is the request and response shape for `PUT /tokens`. The plaintext -// `Token` field is only populated on creation. +// `Token` field is only populated on creation. Vikunja requires ExpiresAt; +// callers that want a long-lived token use FarFuture (year 9999). type APIToken struct { ID int64 `json:"id,omitempty"` Title string `json:"title"` Token string `json:"token,omitempty"` Permissions map[string][]string `json:"permissions"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + ExpiresAt time.Time `json:"expires_at"` OwnerID int64 `json:"owner_id,omitempty"` Created time.Time `json:"created,omitempty"` } +// FarFuture is what veans uses for "no expiry" since Vikunja's API token +// model marks expires_at as required. Year 9999 is well past any reasonable +// rotation horizon and is what the frontend uses for its "never" option. +var FarFuture = time.Date(9999, time.December, 31, 0, 0, 0, 0, time.UTC) + // Info is the parsed shape of `GET /info`. type Info struct { Version string `json:"version"`